From 8d91e3f614383a010c74072b4ae876309be3995c Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Tue, 10 Jan 2023 12:48:09 +1100 Subject: MDEV-30191 Remove the to-be-freed spider condition in an sp call The condition is freed in sp_head::execute, after calling ha_spider::reset. This commit partially reverts the change in commit e954d9de886aebc68c39240304fe97ae88276dbb, so that the condition is always freed regardless of the wide_handler->sql_command, which will prevent access to the freed condition later. Signed-off-by: Yuchen Pei --- storage/spider/ha_spider.cc | 6 +-- .../mysql-test/spider/bugfix/r/mdev_30191.result | 44 +++++++++++++++++++ .../mysql-test/spider/bugfix/t/mdev_30191.cnf | 3 ++ .../mysql-test/spider/bugfix/t/mdev_30191.test | 51 ++++++++++++++++++++++ 4 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 storage/spider/mysql-test/spider/bugfix/r/mdev_30191.result create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_30191.cnf create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_30191.test diff --git a/storage/spider/ha_spider.cc b/storage/spider/ha_spider.cc index 42ee1041c60..1700cd0557e 100644 --- a/storage/spider/ha_spider.cc +++ b/storage/spider/ha_spider.cc @@ -1365,10 +1365,8 @@ int ha_spider::reset() #endif result_list.direct_distinct = FALSE; store_error_num = 0; - if ( - wide_handler && - wide_handler->sql_command != SQLCOM_END - ) { + if (wide_handler) + { wide_handler->sql_command = SQLCOM_END; wide_handler->between_flg = FALSE; wide_handler->idx_bitmap_is_set = FALSE; diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_30191.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_30191.result new file mode 100644 index 00000000000..941f210cbbe --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_30191.result @@ -0,0 +1,44 @@ +# +# MDEV-30191 SIGSEGV & heap-use-after-free in spider_db_print_item_type, SIGABRT in __cxa_pure_virtual/spider_db_print_item_type, Got error 128 "Out of memory in engine", 56/112 memory not freed, and Assertion `fixed()' failed in Item_sp_variable::val_str on SP call +# +for master_1 +for child2 +child2_1 +child2_2 +child2_3 +for child3 +connection child2_1; +CREATE DATABASE auto_test_remote; +USE auto_test_remote; +CREATE TABLE tbl_a (c INT); +connection master_1; +CREATE DATABASE auto_test_local; +USE auto_test_local; +CREATE TABLE tbl_a ( +c INT +) ENGINE=Spider DEFAULT CHARSET=utf8 COMMENT='table "tbl_a", srv "s_2_1"'; +CREATE TABLE tbl_b (c INT); +CREATE PROCEDURE sp() BEGIN +DECLARE v1 DATE; +WHILE EXISTS (SELECT 1 FROM tbl_a WHERE c>v1 AND c<=v1) DO +SELECT 1; +END WHILE; +WHILE EXISTS (SELECT 1 +FROM tbl_a +WHERE cv1 AND c<=v1) DO + SELECT 1; + END WHILE; + WHILE EXISTS (SELECT 1 + FROM tbl_a + WHERE c Date: Mon, 16 Jan 2023 17:02:16 +0100 Subject: MDEV-29774 BF abort no longer wakes up debug_sync waiters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit d7d3ad698abde16978e094b825e17a6ecd8604e, "hard" kill is required to interrupt debug sync waits. Affected the following tests: - galera_var_retry_autocommit, - galera_bf_abort_at_after_statement - galera_parallel_apply_3nodes Reviewed-by: Jan Lindström --- mysql-test/suite/galera/disabled.def | 1 - mysql-test/suite/galera_3nodes/disabled.def | 2 -- sql/service_wsrep.cc | 2 +- sql/sp_head.cc | 2 +- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/mysql-test/suite/galera/disabled.def b/mysql-test/suite/galera/disabled.def index 0210afb7e0a..d15f2d271ca 100644 --- a/mysql-test/suite/galera/disabled.def +++ b/mysql-test/suite/galera/disabled.def @@ -11,7 +11,6 @@ ############################################################################## galera_as_slave_ctas : MDEV-28378 timeout -galera_bf_abort_at_after_statement : Timeout in wait_condition.inc for SELECT COUNT(*) = 1 FROM t1 where id = 1 and val = 3 galera_pc_recovery : MDEV-25199 cluster fails to start up galera_sst_encrypted : MDEV-29876 Galera test failure on galera_sst_encrypted MW-284 : MDEV-29861 Galera test case hangs diff --git a/mysql-test/suite/galera_3nodes/disabled.def b/mysql-test/suite/galera_3nodes/disabled.def index e092b445d0a..22ff54ed504 100644 --- a/mysql-test/suite/galera_3nodes/disabled.def +++ b/mysql-test/suite/galera_3nodes/disabled.def @@ -13,8 +13,6 @@ galera_2_cluster : MDEV-29877 Galera test failure on galera_2_cluster galera_gtid_2_cluster : MDEV-29877 Galera test failure on galera_2_cluster -galera_parallel_apply_3nodes : MDEV-29368 DEBUG_SYNC timeout -galera_parallel_apply_3nodes : MDEV-29774 Galera test galera_parallel_apply_3nodes is unstable galera_vote_rejoin_mysqldump : MDEV-24481: galera_3nodes.galera_vote_rejoin_mysqldump MTR failed: mysql_shutdown failed galera_2_cluster : MDEV-29877 Galera test failure on galera_2_cluster galera_gtid_2_cluster : MDEV-29877 Galera test failure on galera_2_cluster diff --git a/sql/service_wsrep.cc b/sql/service_wsrep.cc index e5c51287ab8..2d8eff2bd4b 100644 --- a/sql/service_wsrep.cc +++ b/sql/service_wsrep.cc @@ -257,7 +257,7 @@ extern "C" my_bool wsrep_thd_bf_abort(THD *bf_thd, THD *victim_thd, } victim_thd->wsrep_aborter= bf_thd->thread_id; - victim_thd->awake_no_mutex(KILL_QUERY); + victim_thd->awake_no_mutex(KILL_QUERY_HARD); } else WSREP_DEBUG("wsrep_thd_bf_abort skipped awake for %llu", thd_get_thread_id(victim_thd)); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 2481f633b16..2121d68c282 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1497,7 +1497,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) wsrep_current_error_status(thd)); thd->wsrep_cs().reset_error(); /* Reset also thd->killed if it has been set during BF abort. */ - if (thd->killed == KILL_QUERY) + if (killed_mask_hard(thd->killed) == KILL_QUERY) thd->killed= NOT_KILLED; /* if failed transaction was not replayed, must return with error from here */ if (!must_replay) err_status = 1; -- cgit v1.2.1 From 7fa5cce305df0fd4d938c047a80b998bb261f956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 19 Jan 2023 16:10:18 +0200 Subject: MDEV-30289: Remove the pointer indirection for mtr_t::m_memo --- storage/innobase/include/mtr0mtr.h | 28 +++---- storage/innobase/mtr/mtr0mtr.cc | 161 +++++++++++++++---------------------- 2 files changed, 81 insertions(+), 108 deletions(-) diff --git a/storage/innobase/include/mtr0mtr.h b/storage/innobase/include/mtr0mtr.h index 1b7455c046b..84e78ffc083 100644 --- a/storage/innobase/include/mtr0mtr.h +++ b/storage/innobase/include/mtr0mtr.h @@ -77,6 +77,9 @@ struct mtr_memo_slot_t /** Mini-transaction handle and buffer */ struct mtr_t { + mtr_t(); + ~mtr_t(); + /** Start a mini-transaction. */ void start(); @@ -91,11 +94,11 @@ struct mtr_t { /** Release latches of unmodified buffer pages. @param begin first slot to release */ void rollback_to_savepoint(ulint begin) - { rollback_to_savepoint(begin, m_memo->size()); } + { rollback_to_savepoint(begin, m_memo.size()); } /** Release the last acquired buffer page latch. */ void release_last_page() - { auto s= m_memo->size(); rollback_to_savepoint(s - 1, s); } + { auto s= m_memo.size(); rollback_to_savepoint(s - 1, s); } /** Commit a mini-transaction that is shrinking a tablespace. @param space tablespace that is being shrunk */ @@ -119,7 +122,7 @@ struct mtr_t { ulint get_savepoint() const { ut_ad(is_active()); - return m_memo ? m_memo->size() : 0; + return m_memo.size(); } /** Release the (index tree) s-latch stored in an mtr memo after a savepoint. @@ -128,7 +131,7 @@ struct mtr_t { void release_s_latch_at_savepoint(ulint savepoint, index_lock *lock) { ut_ad(is_active()); - mtr_memo_slot_t &slot= m_memo->at(savepoint); + mtr_memo_slot_t &slot= m_memo[savepoint]; ut_ad(slot.object == lock); ut_ad(slot.type == MTR_MEMO_S_LOCK); slot.object= nullptr; @@ -138,7 +141,7 @@ struct mtr_t { void release_block_at_savepoint(ulint savepoint, buf_block_t *block) { ut_ad(is_active()); - mtr_memo_slot_t &slot= m_memo->at(savepoint); + mtr_memo_slot_t &slot= m_memo[savepoint]; ut_ad(slot.object == block); ut_ad(!(slot.type & MTR_MEMO_MODIFY)); slot.object= nullptr; @@ -172,7 +175,7 @@ struct mtr_t { ut_ad(is_active()); ut_ad(!memo_contains_flagged(block, MTR_MEMO_PAGE_S_FIX | MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX)); - mtr_memo_slot_t &slot= m_memo->at(savepoint); + mtr_memo_slot_t &slot= m_memo[savepoint]; ut_ad(slot.object == block); ut_ad(slot.type == MTR_MEMO_BUF_FIX); slot.type= MTR_MEMO_PAGE_X_FIX; @@ -189,7 +192,7 @@ struct mtr_t { ut_ad(is_active()); ut_ad(!memo_contains_flagged(block, MTR_MEMO_PAGE_S_FIX | MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX)); - mtr_memo_slot_t &slot= m_memo->at(savepoint); + mtr_memo_slot_t &slot= m_memo[savepoint]; ut_ad(slot.object == block); ut_ad(slot.type == MTR_MEMO_BUF_FIX); slot.type= MTR_MEMO_PAGE_SX_FIX; @@ -409,7 +412,7 @@ public: @param latch latch type */ void u_lock_register(ulint savepoint) { - mtr_memo_slot_t &slot= m_memo->at(savepoint); + mtr_memo_slot_t &slot= m_memo[savepoint]; ut_ad(slot.type == MTR_MEMO_BUF_FIX); slot.type= MTR_MEMO_PAGE_SX_FIX; } @@ -418,7 +421,7 @@ public: @param latch latch type */ void s_lock_register(ulint savepoint) { - mtr_memo_slot_t &slot= m_memo->at(savepoint); + mtr_memo_slot_t &slot= m_memo[savepoint]; ut_ad(slot.type == MTR_MEMO_BUF_FIX); slot.type= MTR_MEMO_PAGE_S_FIX; } @@ -477,10 +480,7 @@ public: m_made_dirty= is_block_dirtied(*static_cast(object)); - if (!m_memo) - m_memo= new std::vector(1, {object, type}); - else - m_memo->emplace_back(mtr_memo_slot_t{object, type}); + m_memo.emplace_back(mtr_memo_slot_t{object, type}); } /** @return the size of the log is empty */ @@ -790,7 +790,7 @@ private: #endif /* UNIV_DEBUG */ /** acquired dict_index_t::lock, fil_space_t::latch, buf_block_t */ - std::vector *m_memo= nullptr; + std::vector m_memo; /** mini-transaction log */ mtr_buf_t m_log; diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc index 5dacdf27489..834d425a73d 100644 --- a/storage/innobase/mtr/mtr0mtr.cc +++ b/storage/innobase/mtr/mtr0mtr.cc @@ -76,10 +76,13 @@ void mtr_memo_slot_t::release() const } } +mtr_t::mtr_t()= default; +mtr_t::~mtr_t()= default; + /** Start a mini-transaction. */ void mtr_t::start() { - ut_ad(!m_memo); + ut_ad(m_memo.empty()); ut_ad(!m_freed_pages); ut_ad(!m_freed_space); MEM_UNDEFINED(this, sizeof *this); @@ -110,24 +113,20 @@ void mtr_t::start() inline void mtr_t::release_resources() { ut_ad(is_active()); - ut_ad(!m_memo); + ut_ad(m_memo.empty()); m_log.erase(); ut_d(m_commit= true); } void mtr_t::release() { - if (m_memo) + for (auto it= m_memo.rbegin(); it != m_memo.rend(); it++) { - for (auto it= m_memo->rbegin(); it != m_memo->rend(); it++) - { - mtr_memo_slot_t &slot= *it; - if (slot.object) - slot.release(); - } - delete m_memo; - m_memo= nullptr; + mtr_memo_slot_t &slot= *it; + if (slot.object) + slot.release(); } + m_memo.clear(); } /** Commit a mini-transaction. */ @@ -190,17 +189,14 @@ void mtr_t::commit() else ut_ad(!m_freed_space); - if (m_memo) + for (const mtr_memo_slot_t &slot : m_memo) { - for (const mtr_memo_slot_t &slot : *m_memo) + if (slot.object && slot.type & MTR_MEMO_MODIFY) { - if (slot.object && slot.type & MTR_MEMO_MODIFY) - { - ut_ad(slot.type == MTR_MEMO_PAGE_X_MODIFY || - slot.type == MTR_MEMO_PAGE_SX_MODIFY); - buf_flush_note_modification(static_cast(slot.object), - lsns.first, m_commit_lsn); - } + ut_ad(slot.type == MTR_MEMO_PAGE_X_MODIFY || + slot.type == MTR_MEMO_PAGE_SX_MODIFY); + buf_flush_note_modification(static_cast(slot.object), + lsns.first, m_commit_lsn); } } @@ -223,14 +219,13 @@ void mtr_t::commit() void mtr_t::rollback_to_savepoint(ulint begin, ulint end) { - ut_ad(m_memo); - ut_ad(end <= m_memo->size()); + ut_ad(end <= m_memo.size()); ut_ad(begin <= end); ulint s= end; while (s-- > begin) { - const mtr_memo_slot_t &slot= (*m_memo)[s]; + const mtr_memo_slot_t &slot= m_memo[s]; if (!slot.object) continue; /* This is intended for releasing latches on indexes or unmodified @@ -240,7 +235,7 @@ void mtr_t::rollback_to_savepoint(ulint begin, ulint end) slot.release(); } - m_memo->erase(m_memo->begin() + begin, m_memo->begin() + end); + m_memo.erase(m_memo.begin() + begin, m_memo.begin() + end); } /** Commit a mini-transaction that is shrinking a tablespace. @@ -252,7 +247,7 @@ void mtr_t::commit_shrink(fil_space_t &space) ut_ad(!high_level_read_only); ut_ad(m_modifications); ut_ad(m_made_dirty); - ut_ad(m_memo); + ut_ad(!m_memo.empty()); ut_ad(!recv_recovery_is_on()); ut_ad(m_log_mode == MTR_LOG_ALL); ut_ad(UT_LIST_GET_LEN(space.chain) == 1); @@ -292,7 +287,7 @@ void mtr_t::commit_shrink(fil_space_t &space) const page_id_t high{space.id, space.size}; - for (mtr_memo_slot_t &slot : *m_memo) + for (mtr_memo_slot_t &slot : m_memo) { if (!slot.object) continue; @@ -539,19 +534,18 @@ void mtr_t::x_lock_space(fil_space_t *space) void mtr_t::release(const void *object) { ut_ad(is_active()); - ut_ad(m_memo); auto it= - std::find_if(m_memo->begin(), m_memo->end(), + std::find_if(m_memo.begin(), m_memo.end(), [object](const mtr_memo_slot_t& slot) { return slot.object == object; }); - ut_ad(it != m_memo->end()); + ut_ad(it != m_memo.end()); ut_ad(!(it->type & MTR_MEMO_MODIFY)); it->release(); - m_memo->erase(it); - ut_ad(std::find_if(m_memo->begin(), m_memo->end(), + m_memo.erase(it); + ut_ad(std::find_if(m_memo.begin(), m_memo.end(), [object](const mtr_memo_slot_t& slot) - { return slot.object == &object; }) == m_memo->end()); + { return slot.object == &object; }) == m_memo.end()); } static bool log_margin_warned; @@ -810,11 +804,11 @@ std::pair mtr_t::do_write() #ifndef DBUG_OFF do { - if (!m_memo || m_log_mode != MTR_LOG_ALL) + if (m_log_mode != MTR_LOG_ALL) continue; DBUG_EXECUTE_IF("skip_page_checksum", continue;); - for (const mtr_memo_slot_t& slot : *m_memo) + for (const mtr_memo_slot_t& slot : m_memo) if (slot.type & MTR_MEMO_MODIFY) { const buf_page_t &b= *static_cast(slot.object); @@ -891,12 +885,9 @@ piecewise: bool mtr_t::have_x_latch(const buf_block_t &block) const { - if (!m_memo) - return false; - ut_d(const mtr_memo_slot_t *found= nullptr); - for (const mtr_memo_slot_t &slot : *m_memo) + for (const mtr_memo_slot_t &slot : m_memo) { if (slot.object != &block) continue; @@ -916,16 +907,13 @@ bool mtr_t::have_x_latch(const buf_block_t &block) const bool mtr_t::have_u_or_x_latch(const buf_block_t &block) const { - if (m_memo) + for (const mtr_memo_slot_t &slot : m_memo) { - for (const mtr_memo_slot_t &slot : *m_memo) + if (slot.object == &block && + slot.type & (MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX)) { - if (slot.object == &block && - slot.type & (MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX)) - { - ut_ad(block.page.lock.have_u_or_x()); - return true; - } + ut_ad(block.page.lock.have_u_or_x()); + return true; } } return false; @@ -937,18 +925,15 @@ bool mtr_t::have_u_or_x_latch(const buf_block_t &block) const @return whether space.latch is being held */ bool mtr_t::memo_contains(const fil_space_t& space, bool shared) const { - if (m_memo) - { - const mtr_memo_type_t type= shared - ? MTR_MEMO_SPACE_S_LOCK : MTR_MEMO_SPACE_X_LOCK; + const mtr_memo_type_t type= shared + ? MTR_MEMO_SPACE_S_LOCK : MTR_MEMO_SPACE_X_LOCK; - for (const mtr_memo_slot_t &slot : *m_memo) + for (const mtr_memo_slot_t &slot : m_memo) + { + if (slot.object == &space && slot.type == type) { - if (slot.object == &space && slot.type == type) - { - ut_ad(shared || space.is_owner()); - return true; - } + ut_ad(shared || space.is_owner()); + return true; } } @@ -958,9 +943,8 @@ bool mtr_t::memo_contains(const fil_space_t& space, bool shared) const void mtr_t::page_lock_upgrade(const buf_block_t &block) { ut_ad(block.page.lock.have_x()); - ut_ad(m_memo); - for (mtr_memo_slot_t &slot : *m_memo) + for (mtr_memo_slot_t &slot : m_memo) if (slot.object == &block && slot.type & MTR_MEMO_PAGE_SX_FIX) slot.type= mtr_memo_type_t(slot.type ^ (MTR_MEMO_PAGE_SX_FIX | MTR_MEMO_PAGE_X_FIX)); @@ -973,9 +957,8 @@ void mtr_t::page_lock_upgrade(const buf_block_t &block) void mtr_t::lock_upgrade(const index_lock &lock) { ut_ad(lock.have_x()); - ut_ad(m_memo); - for (mtr_memo_slot_t &slot : *m_memo) + for (mtr_memo_slot_t &slot : m_memo) if (slot.object == &lock && slot.type == MTR_MEMO_SX_LOCK) slot.type= MTR_MEMO_X_LOCK; } @@ -1028,10 +1011,9 @@ done: void mtr_t::upgrade_buffer_fix(ulint savepoint, rw_lock_type_t rw_latch) { ut_ad(is_active()); - ut_ad(m_memo); - ut_ad(savepoint < m_memo->size()); + ut_ad(savepoint < m_memo.size()); - mtr_memo_slot_t &slot= (*m_memo)[savepoint]; + mtr_memo_slot_t &slot= m_memo[savepoint]; ut_ad(slot.type == MTR_MEMO_BUF_FIX); buf_block_t *block= static_cast(slot.object); ut_d(const auto state= block->page.state()); @@ -1070,27 +1052,24 @@ bool mtr_t::memo_contains(const index_lock &lock, mtr_memo_type_t type) const ut_ad(type == MTR_MEMO_X_LOCK || type == MTR_MEMO_S_LOCK || type == MTR_MEMO_SX_LOCK); - if (m_memo) + for (const mtr_memo_slot_t &slot : m_memo) { - for (const mtr_memo_slot_t &slot : *m_memo) + if (slot.object == &lock && slot.type == type) { - if (slot.object == &lock && slot.type == type) - { - switch (type) { - case MTR_MEMO_X_LOCK: - ut_ad(lock.have_x()); - break; - case MTR_MEMO_SX_LOCK: - ut_ad(lock.have_u_or_x()); - break; - case MTR_MEMO_S_LOCK: - ut_ad(lock.have_s()); - break; - default: - break; - } - return true; + switch (type) { + case MTR_MEMO_X_LOCK: + ut_ad(lock.have_x()); + break; + case MTR_MEMO_SX_LOCK: + ut_ad(lock.have_u_or_x()); + break; + case MTR_MEMO_S_LOCK: + ut_ad(lock.have_s()); + break; + default: + break; } + return true; } } @@ -1118,7 +1097,7 @@ bool mtr_t::memo_contains_flagged(const void *object, ulint flags) const MTR_MEMO_MODIFY)) == !!(flags & (MTR_MEMO_X_LOCK | MTR_MEMO_SX_LOCK | MTR_MEMO_S_LOCK))); - for (const mtr_memo_slot_t &slot : *m_memo) + for (const mtr_memo_slot_t &slot : m_memo) { if (object != slot.object) continue; @@ -1153,7 +1132,7 @@ buf_block_t* mtr_t::memo_contains_page_flagged(const byte *ptr, ulint flags) { ptr= page_align(ptr); - for (const mtr_memo_slot_t &slot : *m_memo) + for (const mtr_memo_slot_t &slot : m_memo) { if (!slot.object || !(flags & slot.type)) continue; @@ -1178,16 +1157,9 @@ buf_block_t* mtr_t::memo_contains_page_flagged(const byte *ptr, ulint flags) @param block page that will be modified */ void mtr_t::modify(const buf_block_t &block) { - if (UNIV_UNLIKELY(!m_memo)) - { - /* This must be PageConverter::update_page() in IMPORT TABLESPACE. */ - ut_ad(!block.page.in_LRU_list); - return; - } - mtr_memo_slot_t *found= nullptr; - for (mtr_memo_slot_t &slot : *m_memo) + for (mtr_memo_slot_t &slot : m_memo) { if (slot.object == &block && slot.type & (MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX)) @@ -1199,7 +1171,9 @@ void mtr_t::modify(const buf_block_t &block) if (UNIV_UNLIKELY(!found)) { - ut_ad("modifying an unlatched page" == 0); + /* This must be PageConverter::update_page() in IMPORT TABLESPACE. */ + ut_ad(m_memo.empty()); + ut_ad(!block.page.in_LRU_list); return; } found->type= static_cast(found->type | MTR_MEMO_MODIFY); @@ -1217,11 +1191,10 @@ void mtr_t::free(const fil_space_t &space, uint32_t offset) if (is_logged()) { - ut_ad(m_memo); buf_block_t *freed= nullptr; const page_id_t id{space.id, offset}; - for (auto it= m_memo->rbegin(); it != m_memo->rend(); it++) + for (auto it= m_memo.rbegin(); it != m_memo.rend(); it++) { mtr_memo_slot_t &slot= *it; buf_block_t *block= static_cast(slot.object); -- cgit v1.2.1 From 67dc8af2a75a70df50f76119ed4bfafcf60548c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 19 Jan 2023 16:10:29 +0200 Subject: MDEV-30289: Implement small_vector for mtr_t::m_memo To avoid heap memory allocation overhead for mtr_t::m_memo, we will allocate a small number of elements statically in mtr_t::m_memo::small. Only if that preallocated data is insufficient, we will invoke my_alloc() or my_realloc() for more storage. The implementation of the data structure is inspired by llvm::SmallVector. --- storage/innobase/CMakeLists.txt | 2 + storage/innobase/include/mtr0mtr.h | 6 +- storage/innobase/include/small_vector.h | 97 +++++++++++++++++++++++++++++++++ storage/innobase/mtr/mtr0mtr.cc | 20 ++++++- 4 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 storage/innobase/include/small_vector.h diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index d655cf81231..c250f17147f 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -344,12 +344,14 @@ SET(INNOBASE_SOURCES include/row0upd.inl include/row0vers.h include/rw_lock.h + include/small_vector.h include/srv0mon.h include/srv0mon.inl include/srv0srv.h include/srv0start.h include/srw_lock.h include/sux_lock.h + include/transactional_lock_guard.h include/trx0i_s.h include/trx0purge.h include/trx0rec.h diff --git a/storage/innobase/include/mtr0mtr.h b/storage/innobase/include/mtr0mtr.h index 84e78ffc083..41f9b473856 100644 --- a/storage/innobase/include/mtr0mtr.h +++ b/storage/innobase/include/mtr0mtr.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2022, MariaDB Corporation. +Copyright (c) 2013, 2023, 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 @@ -29,7 +29,7 @@ Created 11/26/1995 Heikki Tuuri #include "fil0fil.h" #include "dyn0buf.h" #include "buf0buf.h" -#include +#include "small_vector.h" /** Start a mini-transaction. */ #define mtr_start(m) (m)->start() @@ -790,7 +790,7 @@ private: #endif /* UNIV_DEBUG */ /** acquired dict_index_t::lock, fil_space_t::latch, buf_block_t */ - std::vector m_memo; + small_vector m_memo; /** mini-transaction log */ mtr_buf_t m_log; diff --git a/storage/innobase/include/small_vector.h b/storage/innobase/include/small_vector.h new file mode 100644 index 00000000000..76069cfc168 --- /dev/null +++ b/storage/innobase/include/small_vector.h @@ -0,0 +1,97 @@ +/***************************************************************************** + +Copyright (c) 2023, 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, Fifth Floor, Boston, MA 02110-1335 USA + +*****************************************************************************/ + +#pragma once +/* A normally small vector, inspired by llvm::SmallVector */ +#include "my_global.h" +#include +#include + +class small_vector_base +{ +protected: + typedef uint32_t Size_T; + void *BeginX; + Size_T Size= 0, Capacity; + small_vector_base()= delete; + small_vector_base(void *small, size_t small_size) + : BeginX(small), Capacity(Size_T(small_size)) {} + ATTRIBUTE_COLD void grow_by_1(void *small, size_t element_size); +public: + size_t size() const { return Size; } + size_t capacity() const { return Capacity; } + bool empty() const { return !Size; } + void clear() { Size= 0; } +protected: + void set_size(size_t N) { Size= Size_T(N); } +}; + +template +class small_vector : public small_vector_base +{ + /** The fixed storage allocation */ + T small[N]; + + using small_vector_base::set_size; + + void grow_if_needed() + { + if (unlikely(size() >= capacity())) + grow_by_1(small, sizeof *small); + } + +public: + small_vector() : small_vector_base(small, N) + { + TRASH_ALLOC(small, sizeof small); + } + ~small_vector() + { + if (small != begin()) + my_free(begin()); + MEM_MAKE_ADDRESSABLE(small, sizeof small); + } + + using iterator= T *; + using const_iterator= const T *; + using reverse_iterator= std::reverse_iterator; + using reference= T &; + + iterator begin() { return static_cast(BeginX); } + const_iterator begin() const { return static_cast(BeginX); } + iterator end() { return begin() + size(); } + const_iterator end() const { return begin() + size(); } + + reverse_iterator rbegin() { return reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + + reference operator[](size_t i) { assert(i < size()); return begin()[i]; } + + void erase(const_iterator S, const_iterator E) + { + set_size(std::move(const_cast(E), end(), + const_cast(S)) - begin()); + } + + void emplace_back(T &&arg) + { + grow_if_needed(); + ::new (end()) T(arg); + set_size(size() + 1); + } +}; diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc index 834d425a73d..730914b2892 100644 --- a/storage/innobase/mtr/mtr0mtr.cc +++ b/storage/innobase/mtr/mtr0mtr.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2022, MariaDB Corporation. +Copyright (c) 2017, 2023, 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 @@ -542,7 +542,7 @@ void mtr_t::release(const void *object) ut_ad(it != m_memo.end()); ut_ad(!(it->type & MTR_MEMO_MODIFY)); it->release(); - m_memo.erase(it); + m_memo.erase(it, it + 1); ut_ad(std::find_if(m_memo.begin(), m_memo.end(), [object](const mtr_memo_slot_t& slot) { return slot.object == &object; }) == m_memo.end()); @@ -1235,3 +1235,19 @@ void mtr_t::free(const fil_space_t &space, uint32_t offset) m_log.close(log_write(id, nullptr)); } } + +void small_vector_base::grow_by_1(void *small, size_t element_size) +{ + const size_t cap= Capacity*= 2, s= cap * element_size; + void *new_begin; + if (BeginX == small) + { + new_begin= my_malloc(PSI_NOT_INSTRUMENTED, s, MYF(0)); + memcpy(new_begin, BeginX, size() * element_size); + TRASH_FREE(small, size() * element_size); + } + else + new_begin= my_realloc(PSI_NOT_INSTRUMENTED, BeginX, s, MYF(0)); + + BeginX= new_begin; +} -- cgit v1.2.1 From f9cac8d2cbf82d4d616905fb3dfab34a9901179d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 19 Jan 2023 17:19:18 +0200 Subject: MDEV-30400 Assertion height == btr_page_get_level(...) on INSERT This also fixes part of MDEV-29835 Partial server freeze which is caused by violations of the latching order that was defined in https://dev.mysql.com/worklog/task/?id=6326 (WL#6326: InnoDB: fix index->lock contention). Unless the current thread is holding an exclusive dict_index_t::lock, it must acquire page latches in a strict parent-to-child, left-to-right order. Not all cases are fixed yet. Failure to follow the correct latching order will cause deadlocks of threads due to lock order inversion. As part of these changes, the BTR_MODIFY_TREE mode is modified so that an Update latch (U a.k.a. SX) will be acquired on the root page, and eXclusive latches (X) will be acquired on all pages leading to the leaf page, as well as any left and right siblings of the pages along the path. The test innodb.innodb_wl6326 will be removed, because at the time the DEBUG_SYNC point is hit, the thread is actually holding several page latches that will be blocking a concurrent SELECT statement. We also remove double bookkeeping that was caused due to excessive information hiding in mtr_t::m_memo. We simply let mtr_t::m_memo store information of latched pages, and ensure that mtr_memo_slot_t::object is never a null pointer. The tree_blocks[] and tree_savepoints[] were redundant. mtr_t::get_already_latched(): Look up a latched page in mtr_t::m_memo. This avoids many redundant entries in mtr_t::m_memo, as well as redundant calls to buf_page_get_gen() for blocks that had already been looked up in a mini-transaction. btr_get_latched_root(): Return a pointer to an already latched root page. This replaces btr_root_block_get() in cases where the mini-transaction has already latched the root page. btr_page_get_parent(): Fetch a parent page that was already latched in BTR_MODIFY_TREE, by invoking mtr_t::get_already_latched(). If needed, upgrade the root page U latch to X. This avoids bloating mtr_t::m_memo as well as redundant buf_pool.page_hash lookups. For non-QUICK CHECK TABLE as well as for B-tree defragmentation, we will invoke btr_cur_search_to_nth_level(). btr_cur_search_to_nth_level(): This will only be used for non-leaf (level>0) B-tree searches that were formerly named BTR_CONT_SEARCH_TREE or BTR_CONT_MODIFY_TREE. In MDEV-29835, this function could be removed altogether, or retained for the case of CHECK TABLE without QUICK. btr_cur_t::search_leaf(): Replaces btr_cur_search_to_nth_level() for searches to level=0 (the leaf level). btr_cur_t::pessimistic_search_leaf(): Implement the new BTR_MODIFY_TREE latching logic in the case that page splits or merges will be needed. The parent pages (and their siblings) should already be latched on the first dive to the leaf and be present in mtr_t::m_memo; there should be no need for BTR_CONT_MODIFY_TREE. This pre-latching almost suffices; MDEV-29835 will have to revise it and remove work-arounds where mtr_t::get_already_latched() fails to find a block. rtr_search_to_nth_level(): A SPATIAL INDEX version of btr_search_to_nth_level() that can search to any level (including the leaf level). rtr_search_leaf(), rtr_insert_leaf(): Wrappers for rtr_search_to_nth_level(). rtr_search(): Replaces rtr_pcur_open(). rtr_cur_restore_position(): Remove an unused constant parameter. btr_pcur_open_on_user_rec(): Remove the constant parameter mode=PAGE_CUR_GE. btr_cur_latch_leaves(): Update a pre-existing mtr_t::m_memo entry for the current leaf page. row_ins_clust_index_entry_low(): Use a new mode=BTR_MODIFY_ROOT_AND_LEAF to gain access to the root page when mode!=BTR_MODIFY_TREE, to write the PAGE_ROOT_AUTO_INC. btr_cur_t::open_leaf(): Some clean-up. mtr_t::lock_register(): Register a page latch on a buffer-fixed block. BTR_SEARCH_TREE, BTR_CONT_SEARCH_TREE: Remove. BTR_CONT_MODIFY_TREE: Note that this is only used by rtr_search_to_nth_level(). btr_pcur_optimistic_latch_leaves(): Replaces btr_cur_optimistic_latch_leaves(). ibuf_delete_rec(): Acquire ibuf.index->lock.u_lock() in order to avoid a deadlock with ibuf_insert_low(BTR_MODIFY_PREV). Tested by: Matthias Leich --- mysql-test/suite/innodb/r/innodb_wl6326.result | 405 ---- mysql-test/suite/innodb/t/innodb_wl6326.opt | 1 - mysql-test/suite/innodb/t/innodb_wl6326.test | 519 ----- mysql-test/suite/innodb_gis/r/rtree_split.result | 12 + mysql-test/suite/innodb_gis/t/rtree_split.test | 15 + storage/innobase/btr/btr0btr.cc | 498 +++-- storage/innobase/btr/btr0cur.cc | 2268 ++++++++-------------- storage/innobase/btr/btr0defragment.cc | 68 +- storage/innobase/btr/btr0pcur.cc | 109 +- storage/innobase/btr/btr0sea.cc | 22 +- storage/innobase/dict/dict0crea.cc | 29 +- storage/innobase/dict/dict0dict.cc | 8 +- storage/innobase/dict/dict0load.cc | 23 +- storage/innobase/dict/dict0stats.cc | 4 +- storage/innobase/fsp/fsp0fsp.cc | 25 +- storage/innobase/gis/gis0sea.cc | 634 ++++-- storage/innobase/handler/ha_innodb.cc | 3 +- storage/innobase/handler/handler0alter.cc | 9 +- storage/innobase/ibuf/ibuf0ibuf.cc | 24 +- storage/innobase/include/btr0btr.h | 11 +- storage/innobase/include/btr0cur.h | 89 +- storage/innobase/include/btr0pcur.h | 49 +- storage/innobase/include/btr0pcur.inl | 40 +- storage/innobase/include/btr0types.h | 32 +- storage/innobase/include/gis0rtree.h | 46 +- storage/innobase/include/gis0type.h | 8 +- storage/innobase/include/ibuf0ibuf.inl | 3 +- storage/innobase/include/mtr0log.h | 6 +- storage/innobase/include/mtr0mtr.h | 84 +- storage/innobase/include/small_vector.h | 3 + storage/innobase/mtr/mtr0mtr.cc | 39 +- storage/innobase/row/row0import.cc | 5 +- storage/innobase/row/row0ins.cc | 104 +- storage/innobase/row/row0log.cc | 33 +- storage/innobase/row/row0merge.cc | 18 +- storage/innobase/row/row0purge.cc | 46 +- storage/innobase/row/row0row.cc | 16 +- storage/innobase/row/row0sel.cc | 16 +- storage/innobase/row/row0uins.cc | 44 +- storage/innobase/row/row0umod.cc | 53 +- storage/innobase/row/row0upd.cc | 53 +- 41 files changed, 2316 insertions(+), 3158 deletions(-) delete mode 100644 mysql-test/suite/innodb/r/innodb_wl6326.result delete mode 100644 mysql-test/suite/innodb/t/innodb_wl6326.opt delete mode 100644 mysql-test/suite/innodb/t/innodb_wl6326.test diff --git a/mysql-test/suite/innodb/r/innodb_wl6326.result b/mysql-test/suite/innodb/r/innodb_wl6326.result deleted file mode 100644 index fcd58aedafe..00000000000 --- a/mysql-test/suite/innodb/r/innodb_wl6326.result +++ /dev/null @@ -1,405 +0,0 @@ -SET GLOBAL innodb_adaptive_hash_index = false; -SET GLOBAL innodb_stats_persistent = false; -connect con1,localhost,root,,; -connect con2,localhost,root,,; -connect con3,localhost,root,,; -CREATE TABLE t1 ( -a00 CHAR(255) NOT NULL DEFAULT 'a', -a01 CHAR(255) NOT NULL DEFAULT 'a', -a02 CHAR(255) NOT NULL DEFAULT 'a', -a03 CHAR(255) NOT NULL DEFAULT 'a', -a04 CHAR(255) NOT NULL DEFAULT 'a', -a05 CHAR(255) NOT NULL DEFAULT 'a', -a06 CHAR(255) NOT NULL DEFAULT 'a', -b INT NOT NULL DEFAULT 0 -) ENGINE = InnoDB; -ALTER TABLE t1 ADD PRIMARY KEY( -a00, -a01, -a02, -a03, -a04, -a05, -a06 -); -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status Engine-independent statistics collected -test.t1 analyze status OK -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; -CLUST_INDEX_SIZE -1 -SET GLOBAL innodb_limit_optimistic_insert_debug = 7; -BEGIN; -INSERT INTO t1 (a00) VALUES ('aa'); -INSERT INTO t1 (a00) VALUES ('ab'); -INSERT INTO t1 (a00) VALUES ('ac'); -INSERT INTO t1 (a00) VALUES ('ad'); -INSERT INTO t1 (a00) VALUES ('ae'); -INSERT INTO t1 (a00) VALUES ('af'); -INSERT INTO t1 (a00) VALUES ('ag'); -INSERT INTO t1 (a00) VALUES ('ah'); -COMMIT; -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status Engine-independent statistics collected -test.t1 analyze status OK -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; -CLUST_INDEX_SIZE -3 -BEGIN; -INSERT INTO t1 (a00) VALUES ('ai'); -INSERT INTO t1 (a00) VALUES ('aj'); -INSERT INTO t1 (a00) VALUES ('ak'); -COMMIT; -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status Engine-independent statistics collected -test.t1 analyze status OK -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; -CLUST_INDEX_SIZE -4 -BEGIN; -INSERT INTO t1 (a00) VALUES ('al'); -INSERT INTO t1 (a00) VALUES ('am'); -INSERT INTO t1 (a00) VALUES ('an'); -INSERT INTO t1 (a00) VALUES ('ao'); -INSERT INTO t1 (a00) VALUES ('ap'); -INSERT INTO t1 (a00) VALUES ('aq'); -INSERT INTO t1 (a00) VALUES ('ar'); -COMMIT; -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status Engine-independent statistics collected -test.t1 analyze status OK -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; -CLUST_INDEX_SIZE -5 -BEGIN; -INSERT INTO t1 (a00) VALUES ('as'); -INSERT INTO t1 (a00) VALUES ('at'); -INSERT INTO t1 (a00) VALUES ('au'); -INSERT INTO t1 (a00) VALUES ('av'); -INSERT INTO t1 (a00) VALUES ('aw'); -INSERT INTO t1 (a00) VALUES ('ax'); -INSERT INTO t1 (a00) VALUES ('ay'); -COMMIT; -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status Engine-independent statistics collected -test.t1 analyze status OK -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; -CLUST_INDEX_SIZE -6 -BEGIN; -INSERT INTO t1 (a00) VALUES ('az'); -INSERT INTO t1 (a00) VALUES ('ba'); -INSERT INTO t1 (a00) VALUES ('bb'); -INSERT INTO t1 (a00) VALUES ('bc'); -INSERT INTO t1 (a00) VALUES ('bd'); -INSERT INTO t1 (a00) VALUES ('be'); -INSERT INTO t1 (a00) VALUES ('bf'); -COMMIT; -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status Engine-independent statistics collected -test.t1 analyze status OK -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; -CLUST_INDEX_SIZE -7 -BEGIN; -INSERT INTO t1 (a00) VALUES ('bg'); -INSERT INTO t1 (a00) VALUES ('bh'); -INSERT INTO t1 (a00) VALUES ('bi'); -INSERT INTO t1 (a00) VALUES ('bj'); -INSERT INTO t1 (a00) VALUES ('bk'); -INSERT INTO t1 (a00) VALUES ('bl'); -INSERT INTO t1 (a00) VALUES ('bm'); -COMMIT; -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status Engine-independent statistics collected -test.t1 analyze status OK -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; -CLUST_INDEX_SIZE -8 -BEGIN; -INSERT INTO t1 (a00) VALUES ('bn'); -INSERT INTO t1 (a00) VALUES ('bo'); -INSERT INTO t1 (a00) VALUES ('bp'); -INSERT INTO t1 (a00) VALUES ('bq'); -INSERT INTO t1 (a00) VALUES ('br'); -INSERT INTO t1 (a00) VALUES ('bs'); -INSERT INTO t1 (a00) VALUES ('bt'); -COMMIT; -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status Engine-independent statistics collected -test.t1 analyze status OK -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; -CLUST_INDEX_SIZE -11 -BEGIN; -INSERT INTO t1 (a00) VALUES ('bu'); -INSERT INTO t1 (a00) VALUES ('bv'); -INSERT INTO t1 (a00) VALUES ('bw'); -INSERT INTO t1 (a00) VALUES ('bx'); -INSERT INTO t1 (a00) VALUES ('by'); -INSERT INTO t1 (a00) VALUES ('bz'); -INSERT INTO t1 (a00) VALUES ('ca'); -INSERT INTO t1 (a00) VALUES ('cb'); -INSERT INTO t1 (a00) VALUES ('cc'); -INSERT INTO t1 (a00) VALUES ('cd'); -INSERT INTO t1 (a00) VALUES ('ce'); -INSERT INTO t1 (a00) VALUES ('cf'); -INSERT INTO t1 (a00) VALUES ('cg'); -INSERT INTO t1 (a00) VALUES ('ch'); -INSERT INTO t1 (a00) VALUES ('ci'); -INSERT INTO t1 (a00) VALUES ('cj'); -INSERT INTO t1 (a00) VALUES ('ck'); -INSERT INTO t1 (a00) VALUES ('cl'); -INSERT INTO t1 (a00) VALUES ('cm'); -INSERT INTO t1 (a00) VALUES ('cn'); -INSERT INTO t1 (a00) VALUES ('co'); -COMMIT; -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status Engine-independent statistics collected -test.t1 analyze status OK -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; -CLUST_INDEX_SIZE -15 -BEGIN; -INSERT INTO t1 (a00) VALUES ('cp'); -INSERT INTO t1 (a00) VALUES ('cq'); -INSERT INTO t1 (a00) VALUES ('cr'); -INSERT INTO t1 (a00) VALUES ('cs'); -INSERT INTO t1 (a00) VALUES ('ct'); -INSERT INTO t1 (a00) VALUES ('cu'); -INSERT INTO t1 (a00) VALUES ('cv'); -INSERT INTO t1 (a00) VALUES ('cw'); -INSERT INTO t1 (a00) VALUES ('cx'); -INSERT INTO t1 (a00) VALUES ('cy'); -INSERT INTO t1 (a00) VALUES ('cz'); -INSERT INTO t1 (a00) VALUES ('da'); -INSERT INTO t1 (a00) VALUES ('db'); -INSERT INTO t1 (a00) VALUES ('dc'); -INSERT INTO t1 (a00) VALUES ('dd'); -INSERT INTO t1 (a00) VALUES ('de'); -INSERT INTO t1 (a00) VALUES ('df'); -INSERT INTO t1 (a00) VALUES ('dg'); -INSERT INTO t1 (a00) VALUES ('dh'); -INSERT INTO t1 (a00) VALUES ('di'); -INSERT INTO t1 (a00) VALUES ('dj'); -INSERT INTO t1 (a00) VALUES ('dk'); -INSERT INTO t1 (a00) VALUES ('dl'); -INSERT INTO t1 (a00) VALUES ('dm'); -INSERT INTO t1 (a00) VALUES ('dn'); -INSERT INTO t1 (a00) VALUES ('do'); -INSERT INTO t1 (a00) VALUES ('dp'); -INSERT INTO t1 (a00) VALUES ('dq'); -INSERT INTO t1 (a00) VALUES ('dr'); -INSERT INTO t1 (a00) VALUES ('ds'); -INSERT INTO t1 (a00) VALUES ('dt'); -INSERT INTO t1 (a00) VALUES ('du'); -INSERT INTO t1 (a00) VALUES ('dv'); -INSERT INTO t1 (a00) VALUES ('dw'); -INSERT INTO t1 (a00) VALUES ('dx'); -INSERT INTO t1 (a00) VALUES ('dy'); -INSERT INTO t1 (a00) VALUES ('dz'); -INSERT INTO t1 (a00) VALUES ('ea'); -INSERT INTO t1 (a00) VALUES ('eb'); -INSERT INTO t1 (a00) VALUES ('ec'); -INSERT INTO t1 (a00) VALUES ('ed'); -INSERT INTO t1 (a00) VALUES ('ee'); -INSERT INTO t1 (a00) VALUES ('ef'); -INSERT INTO t1 (a00) VALUES ('eg'); -INSERT INTO t1 (a00) VALUES ('eh'); -INSERT INTO t1 (a00) VALUES ('ei'); -INSERT INTO t1 (a00) VALUES ('ej'); -INSERT INTO t1 (a00) VALUES ('ek'); -INSERT INTO t1 (a00) VALUES ('el'); -COMMIT; -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status Engine-independent statistics collected -test.t1 analyze status OK -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; -CLUST_INDEX_SIZE -23 -BEGIN; -INSERT INTO t1 (a00) VALUES ('em'); -INSERT INTO t1 (a00) VALUES ('en'); -INSERT INTO t1 (a00) VALUES ('eo'); -INSERT INTO t1 (a00) VALUES ('ep'); -INSERT INTO t1 (a00) VALUES ('eq'); -INSERT INTO t1 (a00) VALUES ('er'); -INSERT INTO t1 (a00) VALUES ('es'); -INSERT INTO t1 (a00) VALUES ('et'); -INSERT INTO t1 (a00) VALUES ('eu'); -INSERT INTO t1 (a00) VALUES ('ev'); -INSERT INTO t1 (a00) VALUES ('ew'); -INSERT INTO t1 (a00) VALUES ('ex'); -INSERT INTO t1 (a00) VALUES ('ey'); -INSERT INTO t1 (a00) VALUES ('ez'); -INSERT INTO t1 (a00) VALUES ('fa'); -INSERT INTO t1 (a00) VALUES ('fb'); -INSERT INTO t1 (a00) VALUES ('fc'); -INSERT INTO t1 (a00) VALUES ('fd'); -INSERT INTO t1 (a00) VALUES ('fe'); -INSERT INTO t1 (a00) VALUES ('ff'); -INSERT INTO t1 (a00) VALUES ('fg'); -INSERT INTO t1 (a00) VALUES ('fh'); -INSERT INTO t1 (a00) VALUES ('fi'); -INSERT INTO t1 (a00) VALUES ('fj'); -INSERT INTO t1 (a00) VALUES ('fk'); -INSERT INTO t1 (a00) VALUES ('fl'); -INSERT INTO t1 (a00) VALUES ('fm'); -INSERT INTO t1 (a00) VALUES ('fn'); -INSERT INTO t1 (a00) VALUES ('fo'); -INSERT INTO t1 (a00) VALUES ('fp'); -INSERT INTO t1 (a00) VALUES ('fq'); -INSERT INTO t1 (a00) VALUES ('fr'); -INSERT INTO t1 (a00) VALUES ('fs'); -INSERT INTO t1 (a00) VALUES ('ft'); -INSERT INTO t1 (a00) VALUES ('fu'); -INSERT INTO t1 (a00) VALUES ('fv'); -INSERT INTO t1 (a00) VALUES ('fw'); -INSERT INTO t1 (a00) VALUES ('fx'); -INSERT INTO t1 (a00) VALUES ('fy'); -INSERT INTO t1 (a00) VALUES ('fz'); -INSERT INTO t1 (a00) VALUES ('ga'); -INSERT INTO t1 (a00) VALUES ('gb'); -INSERT INTO t1 (a00) VALUES ('gc'); -INSERT INTO t1 (a00) VALUES ('gd'); -INSERT INTO t1 (a00) VALUES ('ge'); -INSERT INTO t1 (a00) VALUES ('gf'); -INSERT INTO t1 (a00) VALUES ('gg'); -INSERT INTO t1 (a00) VALUES ('gh'); -COMMIT; -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status Engine-independent statistics collected -test.t1 analyze status OK -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; -CLUST_INDEX_SIZE -29 -SET GLOBAL innodb_limit_optimistic_insert_debug = 0; -# Test start -SET DEBUG_SYNC = 'RESET'; -INSERT INTO t1 (a00) VALUES ('bfa'); -connection con1; -SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue'; -INSERT INTO t1 (a00) VALUES ('bfb'); -connection con2; -SET DEBUG_SYNC = 'now WAIT_FOR reached'; -SELECT a00,a01 FROM t1 WHERE a00 = 'aa'; -a00 a01 -aa a -SELECT a00,a01 FROM t1 WHERE a00 = 'aq'; -a00 a01 -aq a -SELECT a00,a01 FROM t1 WHERE a00 = 'cp'; -a00 a01 -cp a -SELECT a00,a01 FROM t1 WHERE a00 = 'el'; -a00 a01 -el a -SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1'; -SELECT a00,a01 FROM t1 WHERE a00 = 'ar'; -connection con3; -SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2'; -SELECT a00,a01 FROM t1 WHERE a00 = 'cn'; -connection default; -SET DEBUG_SYNC = 'now WAIT_FOR lockwait1 TIMEOUT 1'; -SET DEBUG_SYNC = 'now WAIT_FOR lockwait2 TIMEOUT 1'; -SET DEBUG_SYNC = 'now SIGNAL continue'; -connection con1; -connection con2; -a00 a01 -ar a -connection con3; -a00 a01 -cn a -connection default; -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status Engine-independent statistics collected -test.t1 analyze status OK -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; -CLUST_INDEX_SIZE -30 -SET DEBUG_SYNC = 'RESET'; -INSERT INTO t1 (a00) VALUES ('coa'); -connection con1; -SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue'; -INSERT INTO t1 (a00) VALUES ('cob'); -connection con2; -SET DEBUG_SYNC = 'now WAIT_FOR reached'; -SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1'; -SELECT a00,a01 FROM t1 WHERE a00 = 'aa'; -connection con3; -SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2'; -SELECT a00,a01 FROM t1 WHERE a00 = 'el'; -connection default; -SET DEBUG_SYNC = 'now WAIT_FOR lockwait1 TIMEOUT 1'; -SET DEBUG_SYNC = 'now WAIT_FOR lockwait2 TIMEOUT 1'; -SET DEBUG_SYNC = 'now SIGNAL continue'; -connection con1; -connection con2; -a00 a01 -aa a -connection con3; -a00 a01 -el a -connection default; -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status Engine-independent statistics collected -test.t1 analyze status OK -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; -CLUST_INDEX_SIZE -31 -SET DEBUG_SYNC = 'RESET'; -INSERT INTO t1 (a00) VALUES ('gba'); -connection con1; -SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue'; -INSERT INTO t1 (a00) VALUES ('gbb'); -connection con2; -SET DEBUG_SYNC = 'now WAIT_FOR reached'; -SELECT a00,a01 FROM t1 WHERE a00 = 'aa'; -a00 a01 -aa a -SELECT a00,a01 FROM t1 WHERE a00 = 'ek'; -a00 a01 -ek a -SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1'; -SELECT a00,a01 FROM t1 WHERE a00 = 'el'; -connection con3; -SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2'; -SELECT a00,a01 FROM t1 WHERE a00 = 'gb'; -connection default; -SET DEBUG_SYNC = 'now WAIT_FOR lockwait1 TIMEOUT 1'; -SET DEBUG_SYNC = 'now WAIT_FOR lockwait2 TIMEOUT 1'; -SET DEBUG_SYNC = 'now SIGNAL continue'; -connection con1; -connection con2; -a00 a01 -el a -connection con3; -a00 a01 -gb a -connection default; -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status Engine-independent statistics collected -test.t1 analyze status OK -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; -CLUST_INDEX_SIZE -32 -SET DEBUG_SYNC = 'RESET'; -connection default; -disconnect con1; -disconnect con2; -disconnect con3; -DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/innodb_wl6326.opt b/mysql-test/suite/innodb/t/innodb_wl6326.opt deleted file mode 100644 index 99bf0e5a28b..00000000000 --- a/mysql-test/suite/innodb/t/innodb_wl6326.opt +++ /dev/null @@ -1 +0,0 @@ ---innodb-sys-tablestats diff --git a/mysql-test/suite/innodb/t/innodb_wl6326.test b/mysql-test/suite/innodb/t/innodb_wl6326.test deleted file mode 100644 index 1cf98cd1c7b..00000000000 --- a/mysql-test/suite/innodb/t/innodb_wl6326.test +++ /dev/null @@ -1,519 +0,0 @@ -# -# WL#6326: InnoDB: fix index->lock contention -# - ---source include/have_innodb.inc ---source include/have_debug.inc ---source include/have_debug_sync.inc ---source include/have_innodb_16k.inc - ---disable_query_log -SET @old_innodb_limit_optimistic_insert_debug = @@innodb_limit_optimistic_insert_debug; -SET @old_innodb_adaptive_hash_index = @@innodb_adaptive_hash_index; -SET @old_innodb_stats_persistent = @@innodb_stats_persistent; ---enable_query_log - -# Save the initial number of concurrent sessions ---source include/count_sessions.inc - -SET GLOBAL innodb_adaptive_hash_index = false; -SET GLOBAL innodb_stats_persistent = false; - ---connect (con1,localhost,root,,) ---connect (con2,localhost,root,,) ---connect (con3,localhost,root,,) - -CREATE TABLE t1 ( - a00 CHAR(255) NOT NULL DEFAULT 'a', - a01 CHAR(255) NOT NULL DEFAULT 'a', - a02 CHAR(255) NOT NULL DEFAULT 'a', - a03 CHAR(255) NOT NULL DEFAULT 'a', - a04 CHAR(255) NOT NULL DEFAULT 'a', - a05 CHAR(255) NOT NULL DEFAULT 'a', - a06 CHAR(255) NOT NULL DEFAULT 'a', - b INT NOT NULL DEFAULT 0 -) ENGINE = InnoDB; - -ALTER TABLE t1 ADD PRIMARY KEY( - a00, - a01, - a02, - a03, - a04, - a05, - a06 -); - -# -# Prepare primary key index tree to be used for this test. -# - -# Only root (1) -ANALYZE TABLE t1; -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; - -# Make the first records sparse artificially, -# not to cause modify_tree by single node_ptr insert operation. -# * (7 - 2) records should be larger than a half of the page size -# * (7 + 2) records should be fit to the page -# (above t1 definition is already adjusted) -SET GLOBAL innodb_limit_optimistic_insert_debug = 7; - -BEGIN; -INSERT INTO t1 (a00) VALUES ('aa'); -INSERT INTO t1 (a00) VALUES ('ab'); -INSERT INTO t1 (a00) VALUES ('ac'); -INSERT INTO t1 (a00) VALUES ('ad'); -INSERT INTO t1 (a00) VALUES ('ae'); -INSERT INTO t1 (a00) VALUES ('af'); -INSERT INTO t1 (a00) VALUES ('ag'); -INSERT INTO t1 (a00) VALUES ('ah'); -COMMIT; -# Raise root (1-2) -# (aa,ad) -# (aa,ab,ac)(ad,ae,af,ag,ah) -ANALYZE TABLE t1; -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; - -BEGIN; -INSERT INTO t1 (a00) VALUES ('ai'); -INSERT INTO t1 (a00) VALUES ('aj'); -INSERT INTO t1 (a00) VALUES ('ak'); -COMMIT; -# Split leaf (1-3) -# (aa,ad,ak) -# (aa,ab,ac)(ad,ae,af,ag,ah,ai,aj)(ak) -ANALYZE TABLE t1; -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; - -BEGIN; -INSERT INTO t1 (a00) VALUES ('al'); -INSERT INTO t1 (a00) VALUES ('am'); -INSERT INTO t1 (a00) VALUES ('an'); -INSERT INTO t1 (a00) VALUES ('ao'); -INSERT INTO t1 (a00) VALUES ('ap'); -INSERT INTO t1 (a00) VALUES ('aq'); -INSERT INTO t1 (a00) VALUES ('ar'); -COMMIT; -# Split leaf (1-4) -# (aa,ad,ak,ar) -# (aa,ab,ac)(ad,ae,af,ag,ah,ai,aj)(ak,al,am,an,ao,ap,aq)(ar) -ANALYZE TABLE t1; -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; - -BEGIN; -INSERT INTO t1 (a00) VALUES ('as'); -INSERT INTO t1 (a00) VALUES ('at'); -INSERT INTO t1 (a00) VALUES ('au'); -INSERT INTO t1 (a00) VALUES ('av'); -INSERT INTO t1 (a00) VALUES ('aw'); -INSERT INTO t1 (a00) VALUES ('ax'); -INSERT INTO t1 (a00) VALUES ('ay'); -COMMIT; -# Split leaf (1-5) -# (aa,ad,ak,ar,ay) -# (aa,ab,ac)(ad,ae,af,ag,ah,ai,aj)(ak,al,am,an,ao,ap,aq)(ar,as,at,au,av,aw,ax)(ay) -ANALYZE TABLE t1; -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; - -BEGIN; -INSERT INTO t1 (a00) VALUES ('az'); -INSERT INTO t1 (a00) VALUES ('ba'); -INSERT INTO t1 (a00) VALUES ('bb'); -INSERT INTO t1 (a00) VALUES ('bc'); -INSERT INTO t1 (a00) VALUES ('bd'); -INSERT INTO t1 (a00) VALUES ('be'); -INSERT INTO t1 (a00) VALUES ('bf'); -COMMIT; -# Split leaf (1-6) -# (aa,ad,ak,ar,ay,bf) -# (aa,ab,ac)(ad..)(ak..)(ar,as,at,au,av,aw,ax)(ay,az,ba,bb,bc,bd,be)(bf) -ANALYZE TABLE t1; -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; - -BEGIN; -INSERT INTO t1 (a00) VALUES ('bg'); -INSERT INTO t1 (a00) VALUES ('bh'); -INSERT INTO t1 (a00) VALUES ('bi'); -INSERT INTO t1 (a00) VALUES ('bj'); -INSERT INTO t1 (a00) VALUES ('bk'); -INSERT INTO t1 (a00) VALUES ('bl'); -INSERT INTO t1 (a00) VALUES ('bm'); -COMMIT; -# Split leaf (1-7) -# (aa,ad,ak,ar,ay,bf,bm) -# (aa,ab,ac)(ad..)(ak..)(ar..)(ay,az,ba,bb,bc,bd,be)(bf,bg,bh,bi,bj,bk,bl)(bm) -ANALYZE TABLE t1; -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; - -BEGIN; -INSERT INTO t1 (a00) VALUES ('bn'); -INSERT INTO t1 (a00) VALUES ('bo'); -INSERT INTO t1 (a00) VALUES ('bp'); -INSERT INTO t1 (a00) VALUES ('bq'); -INSERT INTO t1 (a00) VALUES ('br'); -INSERT INTO t1 (a00) VALUES ('bs'); -INSERT INTO t1 (a00) VALUES ('bt'); -COMMIT; -# Raise root (1-2-8) -# (aa,ar) -# (aa,ad,ak) (ar,ay,bf,bm,bt) -# (aa,ab,ac)(ad..)(ak..)(ar..)(ay..)(bf..)(bm..)(bt) -ANALYZE TABLE t1; -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; - -BEGIN; -INSERT INTO t1 (a00) VALUES ('bu'); -INSERT INTO t1 (a00) VALUES ('bv'); -INSERT INTO t1 (a00) VALUES ('bw'); -INSERT INTO t1 (a00) VALUES ('bx'); -INSERT INTO t1 (a00) VALUES ('by'); -INSERT INTO t1 (a00) VALUES ('bz'); -INSERT INTO t1 (a00) VALUES ('ca'); - -INSERT INTO t1 (a00) VALUES ('cb'); -INSERT INTO t1 (a00) VALUES ('cc'); -INSERT INTO t1 (a00) VALUES ('cd'); -INSERT INTO t1 (a00) VALUES ('ce'); -INSERT INTO t1 (a00) VALUES ('cf'); -INSERT INTO t1 (a00) VALUES ('cg'); -INSERT INTO t1 (a00) VALUES ('ch'); - -INSERT INTO t1 (a00) VALUES ('ci'); -INSERT INTO t1 (a00) VALUES ('cj'); -INSERT INTO t1 (a00) VALUES ('ck'); -INSERT INTO t1 (a00) VALUES ('cl'); -INSERT INTO t1 (a00) VALUES ('cm'); -INSERT INTO t1 (a00) VALUES ('cn'); -INSERT INTO t1 (a00) VALUES ('co'); -COMMIT; -# Split also at level 1 (1-3-11) -# (aa,ar,co) -# (aa,ad,ak) (ar,ay,bf,bm,bt,ca,ch) (co) -# (aa,ab,ac)(ad..)(ak..)(ar..)(ay..)(bf..)(bm..)(bt..)(ca..)(ch..)(co) -ANALYZE TABLE t1; -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; - -BEGIN; -INSERT INTO t1 (a00) VALUES ('cp'); -INSERT INTO t1 (a00) VALUES ('cq'); -INSERT INTO t1 (a00) VALUES ('cr'); -INSERT INTO t1 (a00) VALUES ('cs'); -INSERT INTO t1 (a00) VALUES ('ct'); -INSERT INTO t1 (a00) VALUES ('cu'); -INSERT INTO t1 (a00) VALUES ('cv'); - -INSERT INTO t1 (a00) VALUES ('cw'); -INSERT INTO t1 (a00) VALUES ('cx'); -INSERT INTO t1 (a00) VALUES ('cy'); -INSERT INTO t1 (a00) VALUES ('cz'); -INSERT INTO t1 (a00) VALUES ('da'); -INSERT INTO t1 (a00) VALUES ('db'); -INSERT INTO t1 (a00) VALUES ('dc'); - -INSERT INTO t1 (a00) VALUES ('dd'); -INSERT INTO t1 (a00) VALUES ('de'); -INSERT INTO t1 (a00) VALUES ('df'); -INSERT INTO t1 (a00) VALUES ('dg'); -INSERT INTO t1 (a00) VALUES ('dh'); -INSERT INTO t1 (a00) VALUES ('di'); -INSERT INTO t1 (a00) VALUES ('dj'); - -INSERT INTO t1 (a00) VALUES ('dk'); -INSERT INTO t1 (a00) VALUES ('dl'); -INSERT INTO t1 (a00) VALUES ('dm'); -INSERT INTO t1 (a00) VALUES ('dn'); -INSERT INTO t1 (a00) VALUES ('do'); -INSERT INTO t1 (a00) VALUES ('dp'); -INSERT INTO t1 (a00) VALUES ('dq'); - -INSERT INTO t1 (a00) VALUES ('dr'); -INSERT INTO t1 (a00) VALUES ('ds'); -INSERT INTO t1 (a00) VALUES ('dt'); -INSERT INTO t1 (a00) VALUES ('du'); -INSERT INTO t1 (a00) VALUES ('dv'); -INSERT INTO t1 (a00) VALUES ('dw'); -INSERT INTO t1 (a00) VALUES ('dx'); - -INSERT INTO t1 (a00) VALUES ('dy'); -INSERT INTO t1 (a00) VALUES ('dz'); -INSERT INTO t1 (a00) VALUES ('ea'); -INSERT INTO t1 (a00) VALUES ('eb'); -INSERT INTO t1 (a00) VALUES ('ec'); -INSERT INTO t1 (a00) VALUES ('ed'); -INSERT INTO t1 (a00) VALUES ('ee'); - -INSERT INTO t1 (a00) VALUES ('ef'); -INSERT INTO t1 (a00) VALUES ('eg'); -INSERT INTO t1 (a00) VALUES ('eh'); -INSERT INTO t1 (a00) VALUES ('ei'); -INSERT INTO t1 (a00) VALUES ('ej'); -INSERT INTO t1 (a00) VALUES ('ek'); -INSERT INTO t1 (a00) VALUES ('el'); -COMMIT; -# Split also at level 1 (1-4-18) -# (aa,ar,co,el) -# (aa,ad,ak) (ar,ay,bf,bm,bt,ca,ch) (co,cv,dc,dj,dq,dx,ee) (el) -# (aa,ab,ac)(ad..)(ak..)(ar..)(ay..)(bf..)(bm..)(bt..)(ca..)(ch..)(co..)(cv..)(dc..)(dj..)(dq..)(dx..)(ee..)(el) -ANALYZE TABLE t1; -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; - -BEGIN; -INSERT INTO t1 (a00) VALUES ('em'); -INSERT INTO t1 (a00) VALUES ('en'); -INSERT INTO t1 (a00) VALUES ('eo'); -INSERT INTO t1 (a00) VALUES ('ep'); -INSERT INTO t1 (a00) VALUES ('eq'); -INSERT INTO t1 (a00) VALUES ('er'); -INSERT INTO t1 (a00) VALUES ('es'); - -INSERT INTO t1 (a00) VALUES ('et'); -INSERT INTO t1 (a00) VALUES ('eu'); -INSERT INTO t1 (a00) VALUES ('ev'); -INSERT INTO t1 (a00) VALUES ('ew'); -INSERT INTO t1 (a00) VALUES ('ex'); -INSERT INTO t1 (a00) VALUES ('ey'); -INSERT INTO t1 (a00) VALUES ('ez'); - -INSERT INTO t1 (a00) VALUES ('fa'); -INSERT INTO t1 (a00) VALUES ('fb'); -INSERT INTO t1 (a00) VALUES ('fc'); -INSERT INTO t1 (a00) VALUES ('fd'); -INSERT INTO t1 (a00) VALUES ('fe'); -INSERT INTO t1 (a00) VALUES ('ff'); -INSERT INTO t1 (a00) VALUES ('fg'); - -INSERT INTO t1 (a00) VALUES ('fh'); -INSERT INTO t1 (a00) VALUES ('fi'); -INSERT INTO t1 (a00) VALUES ('fj'); -INSERT INTO t1 (a00) VALUES ('fk'); -INSERT INTO t1 (a00) VALUES ('fl'); -INSERT INTO t1 (a00) VALUES ('fm'); -INSERT INTO t1 (a00) VALUES ('fn'); - -INSERT INTO t1 (a00) VALUES ('fo'); -INSERT INTO t1 (a00) VALUES ('fp'); -INSERT INTO t1 (a00) VALUES ('fq'); -INSERT INTO t1 (a00) VALUES ('fr'); -INSERT INTO t1 (a00) VALUES ('fs'); -INSERT INTO t1 (a00) VALUES ('ft'); -INSERT INTO t1 (a00) VALUES ('fu'); - -INSERT INTO t1 (a00) VALUES ('fv'); -INSERT INTO t1 (a00) VALUES ('fw'); -INSERT INTO t1 (a00) VALUES ('fx'); -INSERT INTO t1 (a00) VALUES ('fy'); -INSERT INTO t1 (a00) VALUES ('fz'); -INSERT INTO t1 (a00) VALUES ('ga'); -INSERT INTO t1 (a00) VALUES ('gb'); - -INSERT INTO t1 (a00) VALUES ('gc'); -INSERT INTO t1 (a00) VALUES ('gd'); -INSERT INTO t1 (a00) VALUES ('ge'); -INSERT INTO t1 (a00) VALUES ('gf'); -INSERT INTO t1 (a00) VALUES ('gg'); -INSERT INTO t1 (a00) VALUES ('gh'); -COMMIT; - -# Current tree form (1-4-24) -# (aa,ar,co,el) -# (aa,ad,ak) (ar,ay,bf,bm,bt,ca,ch) (co,cv,dc,dj,dq,dx,ee) (el..,gb) -# (aa,ab,ac)(ad..)(ak..)(ar..)(ay..)(bf..)(bm..)(bt..)(ca..)(ch..)(co..)(cv..)(dc..)(dj..)(dq..)(dx..)(ee..)(el..)..(gb..) -ANALYZE TABLE t1; -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; - -# Insert the rest of records normally -SET GLOBAL innodb_limit_optimistic_insert_debug = 0; - ---echo # Test start - -# (1) Insert records to leaf page (bf..) and cause modify_page. -# - root page is not X latched -# - latched from level 1 page (ar,ay,bf,bm,bt,ca,ch) - -SET DEBUG_SYNC = 'RESET'; - -# Filling leaf page (bf..) -INSERT INTO t1 (a00) VALUES ('bfa'); - ---connection con1 -SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue'; -# Cause modify_tree ---send -INSERT INTO t1 (a00) VALUES ('bfb'); - ---connection con2 -SET DEBUG_SYNC = 'now WAIT_FOR reached'; -# Not blocked searches -SELECT a00,a01 FROM t1 WHERE a00 = 'aa'; -SELECT a00,a01 FROM t1 WHERE a00 = 'aq'; -# "where a00 = 'co'" is blocked because searching from smaller ('co','a','a',..). -SELECT a00,a01 FROM t1 WHERE a00 = 'cp'; -SELECT a00,a01 FROM t1 WHERE a00 = 'el'; - -SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1'; -# Blocked ---send -SELECT a00,a01 FROM t1 WHERE a00 = 'ar'; - ---connection con3 -SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2'; -# Blocked ---send -SELECT a00,a01 FROM t1 WHERE a00 = 'cn'; - ---connection default -# FIXME: These occasionally time out! ---disable_warnings -SET DEBUG_SYNC = 'now WAIT_FOR lockwait1 TIMEOUT 1'; -SET DEBUG_SYNC = 'now WAIT_FOR lockwait2 TIMEOUT 1'; ---enable_warnings -SET DEBUG_SYNC = 'now SIGNAL continue'; - ---connection con1 ---reap - ---connection con2 ---reap - ---connection con3 ---reap - ---connection default - -ANALYZE TABLE t1; -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; - - - -# (2) Insert records to leaf page (co..) and cause modify_page -# - root page is X latched, because node_ptr for 'co' -# is 1st record for (co,cv,dc,dj,dq,dx,ee) -# -# * ordinary pessimitic insert might be done by pessistic update -# and we should consider possibility node_ptr to be deleted. - -SET DEBUG_SYNC = 'RESET'; - -# Filling leaf page (co..) -INSERT INTO t1 (a00) VALUES ('coa'); - ---connection con1 -SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue'; -# Cause modify_tree ---send -INSERT INTO t1 (a00) VALUES ('cob'); - ---connection con2 -SET DEBUG_SYNC = 'now WAIT_FOR reached'; -# All searches are blocked because root page is X latched - -SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1'; -# Blocked ---send -SELECT a00,a01 FROM t1 WHERE a00 = 'aa'; - ---connection con3 -SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2'; -# Blocked ---send -SELECT a00,a01 FROM t1 WHERE a00 = 'el'; - ---connection default -# FIXME: These occasionally time out! ---disable_warnings -SET DEBUG_SYNC = 'now WAIT_FOR lockwait1 TIMEOUT 1'; -SET DEBUG_SYNC = 'now WAIT_FOR lockwait2 TIMEOUT 1'; ---enable_warnings -SET DEBUG_SYNC = 'now SIGNAL continue'; - ---connection con1 ---reap - ---connection con2 ---reap - ---connection con3 ---reap - ---connection default - -ANALYZE TABLE t1; -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; - - - -# (3) Insert records to rightmost leaf page (gb..) and cause modify_page -# - root page is not X latched, because node_ptr for 'gb' is the last record -# of the level 1 though it is last record in the page. -# - lathed from level 1 page (el..,gb) - -SET DEBUG_SYNC = 'RESET'; - -# Filling leaf page (gb..) -INSERT INTO t1 (a00) VALUES ('gba'); - ---connection con1 -SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue'; -# Cause modify_tree ---send -INSERT INTO t1 (a00) VALUES ('gbb'); - ---connection con2 -SET DEBUG_SYNC = 'now WAIT_FOR reached'; -# Not blocked searches -SELECT a00,a01 FROM t1 WHERE a00 = 'aa'; -SELECT a00,a01 FROM t1 WHERE a00 = 'ek'; - -SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1'; -# Blocked ---send -SELECT a00,a01 FROM t1 WHERE a00 = 'el'; - ---connection con3 -SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2'; -# Blocked ---send -SELECT a00,a01 FROM t1 WHERE a00 = 'gb'; - ---connection default -# FIXME: These occasionally time out! ---disable_warnings -SET DEBUG_SYNC = 'now WAIT_FOR lockwait1 TIMEOUT 1'; -SET DEBUG_SYNC = 'now WAIT_FOR lockwait2 TIMEOUT 1'; ---enable_warnings -SET DEBUG_SYNC = 'now SIGNAL continue'; - ---connection con1 ---reap - ---connection con2 ---reap - ---connection con3 ---reap - ---connection default -ANALYZE TABLE t1; -SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; - - - -# Cleanup -SET DEBUG_SYNC = 'RESET'; - ---connection default ---disconnect con1 ---disconnect con2 ---disconnect con3 - -DROP TABLE t1; - ---disable_query_log -SET GLOBAL innodb_limit_optimistic_insert_debug = @old_innodb_limit_optimistic_insert_debug; -SET GLOBAL innodb_adaptive_hash_index = @old_innodb_adaptive_hash_index; -SET GLOBAL innodb_stats_persistent = @old_innodb_stats_persistent; ---enable_query_log - -# Wait till all disconnects are completed. ---source include/wait_until_count_sessions.inc diff --git a/mysql-test/suite/innodb_gis/r/rtree_split.result b/mysql-test/suite/innodb_gis/r/rtree_split.result index 8e475776ce0..10262f0220b 100644 --- a/mysql-test/suite/innodb_gis/r/rtree_split.result +++ b/mysql-test/suite/innodb_gis/r/rtree_split.result @@ -61,3 +61,15 @@ select count(*) from t1 where MBRWithin(t1.c2, @g1); count(*) 57344 drop table t1; +# +# MDEV-30400 Assertion height == btr_page_get_level ... on INSERT +# +CREATE TABLE t1 (c POINT NOT NULL,SPATIAL (c)) ENGINE=InnoDB; +SET @save_limit=@@GLOBAL.innodb_limit_optimistic_insert_debug; +SET GLOBAL innodb_limit_optimistic_insert_debug=2; +BEGIN; +INSERT INTO t1 SELECT POINTFROMTEXT ('POINT(0 0)') FROM seq_1_to_366; +ROLLBACK; +SET GLOBAL innodb_limit_optimistic_insert_debug=@save_limit; +DROP TABLE t1; +# End of 10.6 tests diff --git a/mysql-test/suite/innodb_gis/t/rtree_split.test b/mysql-test/suite/innodb_gis/t/rtree_split.test index 6f285187508..de7fc676e0e 100644 --- a/mysql-test/suite/innodb_gis/t/rtree_split.test +++ b/mysql-test/suite/innodb_gis/t/rtree_split.test @@ -73,3 +73,18 @@ select count(*) from t1 where MBRWithin(t1.c2, @g1); # Clean up. drop table t1; + +--echo # +--echo # MDEV-30400 Assertion height == btr_page_get_level ... on INSERT +--echo # + +CREATE TABLE t1 (c POINT NOT NULL,SPATIAL (c)) ENGINE=InnoDB; +SET @save_limit=@@GLOBAL.innodb_limit_optimistic_insert_debug; +SET GLOBAL innodb_limit_optimistic_insert_debug=2; +BEGIN; +INSERT INTO t1 SELECT POINTFROMTEXT ('POINT(0 0)') FROM seq_1_to_366; +ROLLBACK; +SET GLOBAL innodb_limit_optimistic_insert_debug=@save_limit; +DROP TABLE t1; + +--echo # End of 10.6 tests diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index 0bb16dba374..ef44ed5d9d6 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -2,7 +2,7 @@ Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2014, 2022, MariaDB Corporation. +Copyright (c) 2014, 2023, 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 @@ -460,6 +460,53 @@ btr_page_create( } } +buf_block_t * +mtr_t::get_already_latched(const page_id_t id, mtr_memo_type_t type) const +{ + ut_ad(is_active()); + ut_ad(type == MTR_MEMO_PAGE_X_FIX || type == MTR_MEMO_PAGE_SX_FIX || + type == MTR_MEMO_PAGE_S_FIX); + for (ulint i= 0; i < m_memo.size(); i++) + { + const mtr_memo_slot_t &slot= m_memo[i]; + const auto slot_type= mtr_memo_type_t(slot.type & ~MTR_MEMO_MODIFY); + if (slot_type == MTR_MEMO_PAGE_X_FIX || slot_type == type) + { + buf_block_t *block= static_cast(slot.object); + if (block->page.id() == id) + return block; + } + } + return nullptr; +} + +/** Fetch an index root page that was already latched in the +mini-transaction. */ +static buf_block_t *btr_get_latched_root(const dict_index_t &index, mtr_t *mtr) +{ + return mtr->get_already_latched(page_id_t{index.table->space_id, index.page}, + MTR_MEMO_PAGE_SX_FIX); +} + +/** Fet an index page that should have been already latched in the +mini-transaction. */ +static buf_block_t * +btr_block_reget(mtr_t *mtr, const dict_index_t &index, + const page_id_t id, rw_lock_type_t rw_latch, + dberr_t *err) +{ + if (buf_block_t *block= + mtr->get_already_latched(id, mtr_memo_type_t(rw_latch))) + { + *err= DB_SUCCESS; + return block; + } + + /* MDEV-29385 FIXME: Acquire the page latch upfront. */ + ut_ad(mtr->memo_contains_flagged(&index.lock, MTR_MEMO_X_LOCK)); + return btr_block_get(index, id.page_no(), rw_latch, true, mtr, err); +} + /**************************************************************//** Allocates a new file page to be used in an ibuf tree. Takes the page from the free list of the tree, which must contain pages! @@ -472,18 +519,16 @@ btr_page_alloc_for_ibuf( mtr_t* mtr, /*!< in: mtr */ dberr_t* err) /*!< out: error code */ { - buf_block_t *root= btr_root_block_get(index, RW_SX_LATCH, mtr, err); + buf_block_t *root= btr_get_latched_root(*index, mtr); if (UNIV_UNLIKELY(!root)) return root; - buf_block_t *new_block= - buf_page_get_gen(page_id_t(index->table->space_id, + buf_page_get_gen(page_id_t(IBUF_SPACE_ID, mach_read_from_4(PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST + FLST_FIRST + FIL_ADDR_PAGE + root->page.frame)), - index->table->space->zip_size(), RW_X_LATCH, nullptr, - BUF_GET, mtr, err); + 0, RW_X_LATCH, nullptr, BUF_GET, mtr, err); if (new_block) *err= flst_remove(root, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, new_block, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST_NODE, mtr); @@ -523,11 +568,11 @@ btr_page_alloc_low( #ifdef BTR_CUR_HASH_ADAPT ut_ad(!root->index || !root->index->freed()); #endif - mtr->release_block_at_savepoint(savepoint, root); + mtr->rollback_to_savepoint(savepoint); } else { - mtr->u_lock_register(savepoint); + mtr->lock_register(savepoint, MTR_MEMO_PAGE_SX_FIX); root->page.lock.u_lock(); #ifdef BTR_CUR_HASH_ADAPT btr_search_drop_page_hash_index(root, true); @@ -579,15 +624,12 @@ btr_page_free_for_ibuf( mtr_t* mtr) /*!< in: mtr */ { ut_ad(mtr->memo_contains_flagged(block, MTR_MEMO_PAGE_X_FIX)); - - dberr_t err; - if (buf_block_t *root= btr_root_block_get(index, RW_SX_LATCH, mtr, &err)) - { - err= flst_add_first(root, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, + buf_block_t *root= btr_get_latched_root(*index, mtr); + dberr_t err= + flst_add_first(root, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, block, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST_NODE, mtr); - ut_d(if (err == DB_SUCCESS) - flst_validate(root, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, mtr)); - } + ut_d(if (err == DB_SUCCESS) + flst_validate(root, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, mtr)); return err; } @@ -637,11 +679,11 @@ dberr_t btr_page_free(dict_index_t* index, buf_block_t* block, mtr_t* mtr, #ifdef BTR_CUR_HASH_ADAPT ut_ad(!root->index || !root->index->freed()); #endif - mtr->release_block_at_savepoint(savepoint, root); + mtr->rollback_to_savepoint(savepoint); } else { - mtr->u_lock_register(savepoint); + mtr->lock_register(savepoint, MTR_MEMO_PAGE_SX_FIX); root->page.lock.u_lock(); #ifdef BTR_CUR_HASH_ADAPT btr_search_drop_page_hash_index(root, true); @@ -712,35 +754,27 @@ btr_node_ptr_get_child( mtr, err); } -MY_ATTRIBUTE((nonnull(2,3,5), warn_unused_result)) +MY_ATTRIBUTE((nonnull(2,3,4), warn_unused_result)) /************************************************************//** Returns the upper level node pointer to a page. It is assumed that mtr holds an sx-latch on the tree. @return rec_get_offsets() of the node pointer record */ static rec_offs* -btr_page_get_father_node_ptr_func( -/*==============================*/ +btr_page_get_father_node_ptr_for_validate( rec_offs* offsets,/*!< in: work area for the return value */ mem_heap_t* heap, /*!< in: memory heap to use */ btr_cur_t* cursor, /*!< in: cursor pointing to user record, out: cursor on node pointer record, its page x-latched */ - btr_latch_mode latch_mode,/*!< in: BTR_CONT_MODIFY_TREE - or BTR_CONT_SEARCH_TREE */ mtr_t* mtr) /*!< in: mtr */ { - ut_ad(latch_mode == BTR_CONT_MODIFY_TREE - || latch_mode == BTR_CONT_SEARCH_TREE); - const uint32_t page_no = btr_cur_get_block(cursor)->page.id().page_no(); dict_index_t* index = btr_cur_get_index(cursor); ut_ad(!dict_index_is_spatial(index)); - ut_ad(srv_read_only_mode - || mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK - | MTR_MEMO_SX_LOCK)); - + ut_ad(mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK + | MTR_MEMO_SX_LOCK)); ut_ad(dict_index_get_page(index) != page_no); const auto level = btr_page_get_level(btr_cur_get_page(cursor)); @@ -752,12 +786,16 @@ btr_page_get_father_node_ptr_func( dict_index_build_node_ptr(index, user_rec, 0, heap, level), - PAGE_CUR_LE, latch_mode, + RW_S_LATCH, cursor, mtr) != DB_SUCCESS) { return nullptr; } const rec_t* node_ptr = btr_cur_get_rec(cursor); +#if 0 /* MDEV-29835 FIXME */ + ut_ad(!btr_cur_get_block(cursor)->page.lock.not_recursive() + || mtr->memo_contains(index->lock, MTR_MEMO_X_LOCK)); +#endif offsets = rec_get_offsets(node_ptr, index, offsets, 0, ULINT_UNDEFINED, &heap); @@ -769,13 +807,64 @@ btr_page_get_father_node_ptr_func( return(offsets); } -#define btr_page_get_father_node_ptr(of,heap,cur,mtr) \ - btr_page_get_father_node_ptr_func( \ - of,heap,cur,BTR_CONT_MODIFY_TREE,mtr) +MY_ATTRIBUTE((nonnull(2,3,4), warn_unused_result)) +/************************************************************//** +Returns the upper level node pointer to a page. It is assumed that +it has already been latched. +@return rec_get_offsets() of the node pointer record */ +static +rec_offs* +btr_page_get_parent( + rec_offs* offsets,/*!< in: work area for the return value */ + mem_heap_t* heap, /*!< in: memory heap to use */ + btr_cur_t* cursor, /*!< in: cursor pointing to user record, + out: cursor on node pointer record, + its page x-latched */ + mtr_t* mtr) /*!< in: mtr */ +{ + const uint32_t page_no= cursor->block()->page.id().page_no(); + const dict_index_t *index= cursor->index(); + ut_ad(!index->is_spatial()); + ut_ad(index->page != page_no); + + uint32_t p= index->page; + const dtuple_t *tuple= + dict_index_build_node_ptr(index, btr_cur_get_rec(cursor), 0, heap, + btr_page_get_level(btr_cur_get_page(cursor))); + + ulint i; + for (i= 0; i < mtr->get_savepoint(); i++) + if (buf_block_t *block= mtr->block_at_savepoint(i)) + if (block->page.id().page_no() == p) + { + ut_ad(block->page.lock.have_u_or_x() || + (!block->page.lock.have_s() && index->lock.have_x())); + ulint up_match= 0, low_match= 0; + cursor->page_cur.block= block; + if (page_cur_search_with_match(tuple, PAGE_CUR_LE, &up_match, + &low_match, &cursor->page_cur, + nullptr)) + return nullptr; + offsets= rec_get_offsets(cursor->page_cur.rec, index, offsets, 0, + ULINT_UNDEFINED, &heap); + p= btr_node_ptr_get_child_page_no(cursor->page_cur.rec, offsets); + if (p != page_no) + { + i= 0; // MDEV-29835 FIXME: require all pages to be latched in order! + continue; + } + ut_ad(block->page.lock.have_u_or_x()); + if (block->page.lock.have_u_not_x()) + { + ut_ad(block->page.id().page_no() == index->page); + block->page.lock.u_x_upgrade(); + mtr->page_lock_upgrade(*block); + } + return offsets; + } -#define btr_page_get_father_node_ptr_for_validate(of,heap,cur,mtr) \ - btr_page_get_father_node_ptr_func( \ - of,heap,cur,BTR_CONT_SEARCH_TREE,mtr) + return nullptr; +} /************************************************************//** Returns the upper level node pointer to a page. It is assumed that mtr holds @@ -796,7 +885,7 @@ btr_page_get_father_block( if (UNIV_UNLIKELY(!rec)) return nullptr; cursor->page_cur.rec= rec; - return btr_page_get_father_node_ptr(offsets, heap, cursor, mtr); + return btr_page_get_parent(offsets, heap, cursor, mtr); } /** Seek to the parent page of a B-tree page. @@ -811,7 +900,7 @@ bool btr_page_get_father(mtr_t* mtr, btr_cur_t* cursor) return false; cursor->page_cur.rec= rec; mem_heap_t *heap= mem_heap_create(100); - const bool got= btr_page_get_father_node_ptr(nullptr, heap, cursor, mtr); + const bool got= btr_page_get_parent(nullptr, heap, cursor, mtr); mem_heap_free(heap); return got; } @@ -1718,48 +1807,43 @@ void btr_set_instant(buf_block_t* root, const dict_index_t& index, mtr_t* mtr) /** Reset the table to the canonical format on ROLLBACK of instant ALTER TABLE. @param[in] index clustered index with instant ALTER TABLE @param[in] all whether to reset FIL_PAGE_TYPE as well -@param[in,out] mtr mini-transaction -@return error code */ +@param[in,out] mtr mini-transaction */ ATTRIBUTE_COLD -dberr_t btr_reset_instant(const dict_index_t &index, bool all, mtr_t *mtr) +void btr_reset_instant(const dict_index_t &index, bool all, mtr_t *mtr) { ut_ad(!index.table->is_temporary()); ut_ad(index.is_primary()); - dberr_t err; - if (buf_block_t *root= btr_root_block_get(&index, RW_SX_LATCH, mtr, &err)) + buf_block_t *root= btr_get_latched_root(index, mtr); + byte *page_type= root->page.frame + FIL_PAGE_TYPE; + if (all) { - byte *page_type= root->page.frame + FIL_PAGE_TYPE; - if (all) - { - ut_ad(mach_read_from_2(page_type) == FIL_PAGE_TYPE_INSTANT || - mach_read_from_2(page_type) == FIL_PAGE_INDEX); - mtr->write<2,mtr_t::MAYBE_NOP>(*root, page_type, FIL_PAGE_INDEX); - byte *instant= PAGE_INSTANT + PAGE_HEADER + root->page.frame; - mtr->write<2,mtr_t::MAYBE_NOP>(*root, instant, - page_ptr_get_direction(instant + 1)); - } - else - ut_ad(mach_read_from_2(page_type) == FIL_PAGE_TYPE_INSTANT); - static const byte supremuminfimum[8 + 8] = "supremuminfimum"; - uint16_t infimum, supremum; - if (page_is_comp(root->page.frame)) - { - infimum= PAGE_NEW_INFIMUM; - supremum= PAGE_NEW_SUPREMUM; - } - else - { - infimum= PAGE_OLD_INFIMUM; - supremum= PAGE_OLD_SUPREMUM; - } - ut_ad(!memcmp(&root->page.frame[infimum], supremuminfimum + 8, 8) == - !memcmp(&root->page.frame[supremum], supremuminfimum, 8)); - mtr->memcpy(*root, &root->page.frame[infimum], - supremuminfimum + 8, 8); - mtr->memcpy(*root, &root->page.frame[supremum], - supremuminfimum, 8); + ut_ad(mach_read_from_2(page_type) == FIL_PAGE_TYPE_INSTANT || + mach_read_from_2(page_type) == FIL_PAGE_INDEX); + mtr->write<2,mtr_t::MAYBE_NOP>(*root, page_type, FIL_PAGE_INDEX); + byte *instant= PAGE_INSTANT + PAGE_HEADER + root->page.frame; + mtr->write<2,mtr_t::MAYBE_NOP>(*root, instant, + page_ptr_get_direction(instant + 1)); } - return err; + else + ut_ad(mach_read_from_2(page_type) == FIL_PAGE_TYPE_INSTANT); + static const byte supremuminfimum[8 + 8] = "supremuminfimum"; + uint16_t infimum, supremum; + if (page_is_comp(root->page.frame)) + { + infimum= PAGE_NEW_INFIMUM; + supremum= PAGE_NEW_SUPREMUM; + } + else + { + infimum= PAGE_OLD_INFIMUM; + supremum= PAGE_OLD_SUPREMUM; + } + ut_ad(!memcmp(&root->page.frame[infimum], supremuminfimum + 8, 8) == + !memcmp(&root->page.frame[supremum], supremuminfimum, 8)); + mtr->memcpy(*root, &root->page.frame[infimum], + supremuminfimum + 8, 8); + mtr->memcpy(*root, &root->page.frame[supremum], + supremuminfimum, 8); } /*************************************************************//** @@ -1856,11 +1940,6 @@ btr_root_raise_and_insert( } /* Copy the records from root to the new page one by one. */ - dberr_t e; - if (!err) { - err = &e; - } - if (0 #ifdef UNIV_ZIP_COPY || new_page_zip @@ -2004,21 +2083,15 @@ btr_root_raise_and_insert( page_cursor->block = new_block; page_cursor->index = index; - if (tuple) { - ut_ad(dtuple_check_typed(tuple)); - /* Reposition the cursor to the child node */ - ulint low_match = 0, up_match = 0; + ut_ad(dtuple_check_typed(tuple)); + /* Reposition the cursor to the child node */ + ulint low_match = 0, up_match = 0; - if (page_cur_search_with_match(tuple, PAGE_CUR_LE, - &up_match, &low_match, - page_cursor, nullptr)) { - if (err) { - *err = DB_CORRUPTION; - } - return nullptr; - } - } else { - page_cursor->rec = page_get_infimum_rec(new_block->page.frame); + if (page_cur_search_with_match(tuple, PAGE_CUR_LE, + &up_match, &low_match, + page_cursor, nullptr)) { + *err = DB_CORRUPTION; + return nullptr; } /* Split the child and insert tuple */ @@ -2237,6 +2310,7 @@ func_exit: return(rec); } +#ifdef UNIV_DEBUG /*************************************************************//** Returns TRUE if the insert fits on the appropriate half-page with the chosen split_rec. @@ -2334,6 +2408,7 @@ got_rec: return(false); } +#endif /*******************************************************//** Inserts a data tuple to a tree on a non-leaf level. It is assumed @@ -2356,25 +2431,34 @@ btr_insert_on_non_leaf_level( rtr_info_t rtr_info; ut_ad(level > 0); - auto mode = PAGE_CUR_LE; + + flags |= BTR_NO_LOCKING_FLAG | BTR_KEEP_SYS_FLAG + | BTR_NO_UNDO_LOG_FLAG; + cursor.page_cur.index = index; + + dberr_t err; if (index->is_spatial()) { - mode = PAGE_CUR_RTREE_INSERT; /* For spatial index, initialize structures to track its parents etc. */ rtr_init_rtr_info(&rtr_info, false, &cursor, index, false); rtr_info_update_btr(&cursor, &rtr_info); + err = rtr_search_to_nth_level(level, tuple, + PAGE_CUR_RTREE_INSERT, + BTR_CONT_MODIFY_TREE, + &cursor, mtr); + } else { + err = btr_cur_search_to_nth_level(level, tuple, RW_X_LATCH, + &cursor, mtr); } - flags |= BTR_NO_LOCKING_FLAG | BTR_KEEP_SYS_FLAG - | BTR_NO_UNDO_LOG_FLAG; - cursor.page_cur.index = index; - - dberr_t err = btr_cur_search_to_nth_level(level, tuple, mode, - BTR_CONT_MODIFY_TREE, - &cursor, mtr); ut_ad(cursor.flag == BTR_CUR_BINARY); +#if 0 /* MDEV-29835 FIXME */ + ut_ad(!btr_cur_get_block(&cursor)->page.lock.not_recursive() + || index->is_spatial() + || mtr->memo_contains(index->lock, MTR_MEMO_X_LOCK)); +#endif if (UNIV_LIKELY(err == DB_SUCCESS)) { err = btr_cur_optimistic_insert(flags, @@ -2470,6 +2554,7 @@ btr_attach_half_pages( /* Get the level of the split pages */ const ulint level = btr_page_get_level(block->page.frame); ut_ad(level == btr_page_get_level(new_block->page.frame)); + page_id_t id{block->page.id()}; /* Get the previous and next pages of page */ const uint32_t prev_page_no = btr_page_get_prev(block->page.frame); @@ -2477,12 +2562,32 @@ btr_attach_half_pages( /* for consistency, both blocks should be locked, before change */ if (prev_page_no != FIL_NULL && direction == FSP_DOWN) { - prev_block = btr_block_get(*index, prev_page_no, RW_X_LATCH, - !level, mtr); + id.set_page_no(prev_page_no); + prev_block = mtr->get_already_latched(id, MTR_MEMO_PAGE_X_FIX); +#if 1 /* MDEV-29835 FIXME: acquire page latches upfront */ + if (!prev_block) { +# if 0 /* MDEV-29835 FIXME */ + ut_ad(mtr->memo_contains(index->lock, + MTR_MEMO_X_LOCK)); +# endif + prev_block = btr_block_get(*index, prev_page_no, + RW_X_LATCH, !level, mtr); + } +#endif } if (next_page_no != FIL_NULL && direction != FSP_DOWN) { - next_block = btr_block_get(*index, next_page_no, RW_X_LATCH, - !level, mtr); + id.set_page_no(next_page_no); + next_block = mtr->get_already_latched(id, MTR_MEMO_PAGE_X_FIX); +#if 1 /* MDEV-29835 FIXME: acquire page latches upfront */ + if (!next_block) { +# if 0 /* MDEV-29835 FIXME */ + ut_ad(mtr->memo_contains(index->lock, + MTR_MEMO_X_LOCK)); +# endif + next_block = btr_block_get(*index, next_page_no, + RW_X_LATCH, !level, mtr); + } +#endif } /* Build the node pointer (= node key and page address) for the upper @@ -3018,6 +3123,7 @@ insert_empty: return nullptr; } +#ifdef UNIV_DEBUG /* If the split is made on the leaf level and the insert will fit on the appropriate half-page, we may release the tree x-latch. We can then move the records after releasing the tree latch, @@ -3025,21 +3131,21 @@ insert_empty: const bool insert_will_fit = !new_page_zip && btr_page_insert_fits(cursor, split_rec, offsets, tuple, n_ext, heap); +#endif if (!split_rec && !insert_left) { UT_DELETE_ARRAY(buf); buf = NULL; } - if (!srv_read_only_mode - && insert_will_fit +#if 0 // FIXME: this used to be a no-op, and may cause trouble if enabled + if (insert_will_fit && page_is_leaf(page) && !dict_index_is_online_ddl(cursor->index())) { -#if 0 // FIXME: this used to be a no-op, and may cause trouble if enabled mtr->release(cursor->index()->lock); -#endif /* NOTE: We cannot release root block latch here, because it has segment header and already modified in most of cases.*/ } +#endif /* 5. Move then the records to the new page */ if (direction == FSP_DOWN) { @@ -3271,52 +3377,58 @@ func_exit: dberr_t btr_level_list_remove(const buf_block_t& block, const dict_index_t& index, mtr_t* mtr) { - ut_ad(mtr->memo_contains_flagged(&block, MTR_MEMO_PAGE_X_FIX)); - ut_ad(block.zip_size() == index.table->space->zip_size()); - ut_ad(index.table->space->id == block.page.id().space()); - /* Get the previous and next page numbers of page */ - - const page_t* page = block.page.frame; - const uint32_t prev_page_no = btr_page_get_prev(page); - const uint32_t next_page_no = btr_page_get_next(page); - - /* Update page links of the level */ - dberr_t err; + ut_ad(mtr->memo_contains_flagged(&block, MTR_MEMO_PAGE_X_FIX)); + ut_ad(block.zip_size() == index.table->space->zip_size()); + ut_ad(index.table->space->id == block.page.id().space()); + /* Get the previous and next page numbers of page */ + const uint32_t prev_page_no= btr_page_get_prev(block.page.frame); + const uint32_t next_page_no= btr_page_get_next(block.page.frame); + page_id_t id{block.page.id()}; + buf_block_t *prev= nullptr, *next; + dberr_t err; - if (prev_page_no != FIL_NULL) { - buf_block_t* prev_block = btr_block_get( - index, prev_page_no, RW_X_LATCH, page_is_leaf(page), - mtr, &err); - if (UNIV_UNLIKELY(!prev_block)) { - return err; - } - if (UNIV_UNLIKELY(memcmp_aligned<4>(prev_block->page.frame - + FIL_PAGE_NEXT, - page + FIL_PAGE_OFFSET, - 4))) { - return DB_CORRUPTION; - } - btr_page_set_next(prev_block, next_page_no, mtr); - } + /* Update page links of the level */ + if (prev_page_no != FIL_NULL) + { + id.set_page_no(prev_page_no); + prev= mtr->get_already_latched(id, MTR_MEMO_PAGE_X_FIX); +#if 1 /* MDEV-29835 FIXME: acquire page latches upfront */ + if (!prev) + { +# if 0 /* MDEV-29835 FIXME */ + ut_ad(mtr->memo_contains(index.lock, MTR_MEMO_X_LOCK)); +# endif + prev= btr_block_get(index, id.page_no(), RW_X_LATCH, + page_is_leaf(block.page.frame), mtr, &err); + if (UNIV_UNLIKELY(!prev)) + return err; + } +#endif + } - if (next_page_no != FIL_NULL) { - buf_block_t* next_block = btr_block_get( - index, next_page_no, RW_X_LATCH, page_is_leaf(page), - mtr, &err); + if (next_page_no != FIL_NULL) + { + id.set_page_no(next_page_no); + next= mtr->get_already_latched(id, MTR_MEMO_PAGE_X_FIX); +#if 1 /* MDEV-29835 FIXME: acquire page latches upfront */ + if (!next) + { +# if 0 /* MDEV-29835 FIXME */ + ut_ad(mtr->memo_contains(index.lock, MTR_MEMO_X_LOCK)); +# endif + next= btr_block_get(index, id.page_no(), RW_X_LATCH, + page_is_leaf(block.page.frame), mtr, &err); + if (UNIV_UNLIKELY(!next)) + return err; + } +#endif + btr_page_set_prev(next, prev_page_no, mtr); + } - if (UNIV_UNLIKELY(!next_block)) { - return err; - } - if (UNIV_UNLIKELY(memcmp_aligned<4>(next_block->page.frame - + FIL_PAGE_PREV, - page + FIL_PAGE_OFFSET, - 4))) { - return DB_CORRUPTION; - } - btr_page_set_prev(next_block, prev_page_no, mtr); - } + if (prev) + btr_page_set_next(prev, next_page_no, mtr); - return DB_SUCCESS; + return DB_SUCCESS; } /*************************************************************//** @@ -4166,23 +4278,30 @@ btr_discard_page( const uint32_t left_page_no = btr_page_get_prev(block->page.frame); const uint32_t right_page_no = btr_page_get_next(block->page.frame); + page_id_t merge_page_id{block->page.id()}; ut_d(bool parent_is_different = false); + dberr_t err; if (left_page_no != FIL_NULL) { - dberr_t err; - merge_block = btr_block_get(*index, left_page_no, RW_X_LATCH, - true, mtr, &err); + merge_page_id.set_page_no(left_page_no); + merge_block = btr_block_reget(mtr, *index, merge_page_id, + RW_X_LATCH, &err); if (UNIV_UNLIKELY(!merge_block)) { return err; } - +#if 0 /* MDEV-29385 FIXME: Acquire the page latch upfront. */ + ut_ad(!memcmp_aligned<4>(merge_block->page.frame + + FIL_PAGE_NEXT, + block->page.frame + FIL_PAGE_OFFSET, + 4)); +#else if (UNIV_UNLIKELY(memcmp_aligned<4>(merge_block->page.frame + FIL_PAGE_NEXT, block->page.frame + FIL_PAGE_OFFSET, 4))) { return DB_CORRUPTION; } - +#endif ut_d(parent_is_different = (page_rec_get_next( page_get_infimum_rec( @@ -4190,19 +4309,25 @@ btr_discard_page( &parent_cursor))) == btr_cur_get_rec(&parent_cursor))); } else if (right_page_no != FIL_NULL) { - dberr_t err; - merge_block = btr_block_get(*index, right_page_no, RW_X_LATCH, - true, mtr, &err); + merge_page_id.set_page_no(right_page_no); + merge_block = btr_block_reget(mtr, *index, merge_page_id, + RW_X_LATCH, &err); if (UNIV_UNLIKELY(!merge_block)) { return err; } +#if 0 /* MDEV-29385 FIXME: Acquire the page latch upfront. */ + ut_ad(!memcmp_aligned<4>(merge_block->page.frame + + FIL_PAGE_PREV, + block->page.frame + FIL_PAGE_OFFSET, + 4)); +#else if (UNIV_UNLIKELY(memcmp_aligned<4>(merge_block->page.frame + FIL_PAGE_PREV, block->page.frame + FIL_PAGE_OFFSET, 4))) { return DB_CORRUPTION; } - +#endif ut_d(parent_is_different = page_rec_is_supremum( page_rec_get_next(btr_cur_get_rec(&parent_cursor)))); if (page_is_leaf(merge_block->page.frame)) { @@ -4244,13 +4369,10 @@ btr_discard_page( } #ifdef UNIV_ZIP_DEBUG - { - page_zip_des_t* merge_page_zip - = buf_block_get_page_zip(merge_block); - ut_a(!merge_page_zip - || page_zip_validate(merge_page_zip, - merge_block->page.frame, index)); - } + if (page_zip_des_t* merge_page_zip + = buf_block_get_page_zip(merge_block)); + ut_a(page_zip_validate(merge_page_zip, + merge_block->page.frame, index)); #endif /* UNIV_ZIP_DEBUG */ if (index->has_locking()) { @@ -4269,7 +4391,7 @@ btr_discard_page( } /* Free the file page */ - dberr_t err = btr_page_free(index, block, mtr); + err = btr_page_free(index, block, mtr); if (err == DB_SUCCESS) { /* btr_check_node_ptr() needs parent block latched. @@ -4462,6 +4584,8 @@ btr_check_node_ptr( offsets = btr_page_get_father_block(NULL, heap, mtr, &cursor); } + ut_ad(offsets); + if (page_is_leaf(page)) { goto func_exit; @@ -4793,19 +4917,16 @@ btr_validate_level( page_zip_des_t* page_zip; #endif /* UNIV_ZIP_DEBUG */ ulint savepoint = 0; - ulint savepoint2 = 0; uint32_t parent_page_no = FIL_NULL; uint32_t parent_right_page_no = FIL_NULL; bool rightmost_child = false; mtr.start(); - if (!srv_read_only_mode) { - if (lockout) { - mtr_x_lock_index(index, &mtr); - } else { - mtr_sx_lock_index(index, &mtr); - } + if (lockout) { + mtr_x_lock_index(index, &mtr); + } else { + mtr_sx_lock_index(index, &mtr); } dberr_t err; @@ -4853,7 +4974,6 @@ corrupted: offsets = rec_get_offsets(node_ptr, index, offsets, 0, ULINT_UNDEFINED, &heap); - savepoint2 = mtr_set_savepoint(&mtr); block = btr_node_ptr_get_child(node_ptr, index, offsets, &mtr, &err); if (!block) { @@ -4874,10 +4994,8 @@ corrupted: /* To obey latch order of tree blocks, we should release the right_block once to obtain lock of the uncle block. */ - mtr_release_block_at_savepoint( - &mtr, savepoint2, block); + mtr.release_last_page(); - savepoint2 = mtr_set_savepoint(&mtr); block = btr_block_get(*index, left_page_no, RW_SX_LATCH, false, &mtr, &err); @@ -4905,12 +5023,10 @@ func_exit: mem_heap_empty(heap); offsets = offsets2 = NULL; - if (!srv_read_only_mode) { - if (lockout) { - mtr_x_lock_index(index, &mtr); - } else { - mtr_sx_lock_index(index, &mtr); - } + if (lockout) { + mtr_x_lock_index(index, &mtr); + } else { + mtr_sx_lock_index(index, &mtr); } page = block->page.frame; @@ -4955,7 +5071,7 @@ func_exit: if (right_page_no != FIL_NULL) { const rec_t* right_rec; - savepoint = mtr_set_savepoint(&mtr); + savepoint = mtr.get_savepoint(); right_block = btr_block_get(*index, right_page_no, RW_SX_LATCH, !level, &mtr, &err); @@ -5150,8 +5266,10 @@ broken_links: /* To obey latch order of tree blocks, we should release the right_block once to obtain lock of the uncle block. */ - mtr_release_block_at_savepoint( - &mtr, savepoint, right_block); + ut_ad(right_block + == mtr.at_savepoint(savepoint)); + mtr.rollback_to_savepoint(savepoint, + savepoint + 1); if (parent_right_page_no != FIL_NULL) { btr_block_get(*index, diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index ac06d9b1568..b3bfb74bb8b 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -3,7 +3,7 @@ Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2015, 2022, MariaDB Corporation. +Copyright (c) 2015, 2023, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -103,14 +103,14 @@ throughput clearly from about 100000. */ #define BTR_CUR_FINE_HISTORY_LENGTH 100000 #ifdef BTR_CUR_HASH_ADAPT -/** Number of searches down the B-tree in btr_cur_search_to_nth_level(). */ +/** Number of searches down the B-tree in btr_cur_t::search_leaf(). */ ib_counter_t btr_cur_n_non_sea; /** Old value of btr_cur_n_non_sea. Copied by srv_refresh_innodb_monitor_stats(). Referenced by srv_printf_innodb_monitor(). */ ulint btr_cur_n_non_sea_old; /** Number of successful adaptive hash index lookups in -btr_cur_search_to_nth_level(). */ +btr_cur_t::search_leaf(). */ ib_counter_t btr_cur_n_sea; /** Old value of btr_cur_n_sea. Copied by srv_refresh_innodb_monitor_stats(). Referenced by @@ -188,164 +188,106 @@ btr_rec_free_externally_stored_fields( /*==================== B-TREE SEARCH =========================*/ /** Latches the leaf page or pages requested. -@param[in] block leaf page where the search converged +@param[in] block_savepoint leaf page where the search converged @param[in] latch_mode BTR_SEARCH_LEAF, ... @param[in] cursor cursor -@param[in] mtr mini-transaction -@param[out] latch_leaves latched blocks and savepoints */ +@param[in] mtr mini-transaction */ void btr_cur_latch_leaves( - buf_block_t* block, + ulint block_savepoint, btr_latch_mode latch_mode, btr_cur_t* cursor, - mtr_t* mtr, - btr_latch_leaves_t* latch_leaves) + mtr_t* mtr) { compile_time_assert(int(MTR_MEMO_PAGE_S_FIX) == int(RW_S_LATCH)); compile_time_assert(int(MTR_MEMO_PAGE_X_FIX) == int(RW_X_LATCH)); compile_time_assert(int(MTR_MEMO_PAGE_SX_FIX) == int(RW_SX_LATCH)); + + buf_block_t* block = mtr->at_savepoint(block_savepoint); + ut_ad(block->page.id().space() == cursor->index()->table->space->id); ut_ad(block->page.in_file()); - ut_ad(srv_read_only_mode - || mtr->memo_contains_flagged(&cursor->index()->lock, - MTR_MEMO_S_LOCK - | MTR_MEMO_X_LOCK - | MTR_MEMO_SX_LOCK)); - auto rtr_info = cursor->rtr_info; - if (UNIV_LIKELY_NULL(rtr_info) && !cursor->index()->is_spatial()) { - rtr_info = nullptr; - } - + ut_ad(mtr->memo_contains_flagged(&cursor->index()->lock, + MTR_MEMO_S_LOCK + | MTR_MEMO_X_LOCK + | MTR_MEMO_SX_LOCK)); const rw_lock_type_t mode = rw_lock_type_t( latch_mode & (RW_X_LATCH | RW_S_LATCH)); static_assert(ulint{RW_S_LATCH} == ulint{BTR_SEARCH_LEAF}, ""); static_assert(ulint{RW_X_LATCH} == ulint{BTR_MODIFY_LEAF}, ""); - static_assert(BTR_SEARCH_LEAF & BTR_SEARCH_TREE, ""); switch (latch_mode) { - default: - break; uint32_t left_page_no; uint32_t right_page_no; - ulint save; + default: + ut_ad(latch_mode == BTR_CONT_MODIFY_TREE); + ut_ad(cursor->index()->is_spatial()); + break; case BTR_SEARCH_LEAF: - case BTR_MODIFY_LEAF: - case BTR_SEARCH_TREE: - if (UNIV_LIKELY_NULL(rtr_info)) { - rtr_info->tree_savepoints[RTR_MAX_LEVELS] - = mtr->get_savepoint(); - } -latch_block: - if (latch_leaves) { - latch_leaves->savepoints[1] = mtr->get_savepoint(); - latch_leaves->blocks[1] = block; - } - block->page.fix(); - mtr->page_lock(block, mode); - if (UNIV_LIKELY_NULL(rtr_info)) { - rtr_info->tree_blocks[RTR_MAX_LEVELS] = block; - } - return; + s_latch_block: + block->page.lock.s_lock(); +#ifdef BTR_CUR_HASH_ADAPT + btr_search_drop_page_hash_index(block, true); +#endif + mtr->lock_register(block_savepoint, MTR_MEMO_PAGE_S_FIX); + break; case BTR_MODIFY_TREE: /* It is exclusive for other operations which calls btr_page_set_prev() */ ut_ad(mtr->memo_contains_flagged(&cursor->index()->lock, MTR_MEMO_X_LOCK | MTR_MEMO_SX_LOCK)); - save = mtr->get_savepoint(); /* x-latch also siblings from left to right */ left_page_no = btr_page_get_prev(block->page.frame); if (left_page_no != FIL_NULL) { - buf_block_t *b = btr_block_get( - *cursor->index(), left_page_no, RW_X_LATCH, - true, mtr); - - if (latch_leaves) { - latch_leaves->savepoints[0] = save; - latch_leaves->blocks[0] = b; - } - - if (UNIV_LIKELY_NULL(rtr_info)) { - rtr_info->tree_savepoints[RTR_MAX_LEVELS] - = save; - rtr_info->tree_blocks[RTR_MAX_LEVELS] = b; - } - - save = mtr->get_savepoint(); - } - - if (latch_leaves) { - latch_leaves->savepoints[1] = mtr->get_savepoint(); - latch_leaves->blocks[1] = block; + btr_block_get(*cursor->index(), left_page_no, RW_X_LATCH, + true, mtr); } - block->page.fix(); - block->page.lock.x_lock(); - - mtr->memo_push(block, MTR_MEMO_PAGE_X_FIX); + mtr->x_latch_at_savepoint(block_savepoint, block); #ifdef BTR_CUR_HASH_ADAPT - ut_ad(!btr_search_check_marked_free_index(block)); + btr_search_drop_page_hash_index(block, true); #endif - if (UNIV_LIKELY_NULL(rtr_info)) { - rtr_info->tree_savepoints[RTR_MAX_LEVELS + 1] = save; - rtr_info->tree_blocks[RTR_MAX_LEVELS + 1] = block; - } - right_page_no = btr_page_get_next(block->page.frame); if (right_page_no != FIL_NULL) { - save = mtr->get_savepoint(); - - buf_block_t* b = btr_block_get( - *cursor->index(), right_page_no, RW_X_LATCH, - true, mtr); - if (latch_leaves) { - latch_leaves->savepoints[2] = save; - latch_leaves->blocks[2] = b; - } - - if (UNIV_LIKELY_NULL(rtr_info)) { - rtr_info->tree_savepoints[RTR_MAX_LEVELS + 2] - = save; - rtr_info->tree_blocks[RTR_MAX_LEVELS + 2] = b; - } + btr_block_get(*cursor->index(), right_page_no, + RW_X_LATCH, true, mtr); } - - return; + break; case BTR_SEARCH_PREV: case BTR_MODIFY_PREV: - ut_ad(!rtr_info); - static_assert(BTR_SEARCH_PREV & BTR_SEARCH_LEAF, ""); static_assert(BTR_MODIFY_PREV & BTR_MODIFY_LEAF, ""); - static_assert((BTR_SEARCH_PREV ^ BTR_MODIFY_PREV) - == (RW_S_LATCH ^ RW_X_LATCH), ""); - + static_assert(BTR_SEARCH_PREV & BTR_SEARCH_LEAF, ""); + ut_ad(cursor->index()->is_ibuf() + || mtr->memo_contains_flagged(&cursor->index()->lock, + MTR_MEMO_X_LOCK + | MTR_MEMO_SX_LOCK)); /* Because we are holding index->lock, no page splits or merges may run concurrently, and we may read FIL_PAGE_PREV from a buffer-fixed, unlatched page. */ left_page_no = btr_page_get_prev(block->page.frame); if (left_page_no != FIL_NULL) { - save = mtr->get_savepoint(); cursor->left_block = btr_block_get( *cursor->index(), left_page_no, mode, true, mtr); - if (latch_leaves) { - latch_leaves->savepoints[0] = save; - latch_leaves->blocks[0] = cursor->left_block; - } } - goto latch_block; - case BTR_CONT_MODIFY_TREE: - ut_ad(cursor->index()->is_spatial()); - return; - } + if (latch_mode == BTR_SEARCH_PREV) { + goto s_latch_block; + } - MY_ASSERT_UNREACHABLE(); + /* fall through */ + case BTR_MODIFY_LEAF: + mtr->x_latch_at_savepoint(block_savepoint, block); +#ifdef BTR_CUR_HASH_ADAPT + btr_search_drop_page_hash_index(block, true); +#endif + } } /** Load the instant ALTER TABLE metadata from the clustered index @@ -729,98 +671,6 @@ bool btr_cur_instant_root_init(dict_index_t* index, const page_t* page) return index->n_core_null_bytes > 128; } -/** Optimistically latches the leaf page or pages requested. -@param[in] block guessed buffer block -@param[in] modify_clock modify clock value -@param[in,out] latch_mode BTR_SEARCH_LEAF, ... -@param[in,out] cursor cursor -@param[in] mtr mini-transaction -@return true if success */ -TRANSACTIONAL_TARGET -bool -btr_cur_optimistic_latch_leaves( - buf_block_t* block, - ib_uint64_t modify_clock, - btr_latch_mode* latch_mode, - btr_cur_t* cursor, - mtr_t* mtr) -{ - ut_ad(block->page.buf_fix_count()); - ut_ad(block->page.in_file()); - ut_ad(block->page.frame); - - switch (*latch_mode) { - default: - MY_ASSERT_UNREACHABLE(); - return(false); - case BTR_SEARCH_LEAF: - case BTR_MODIFY_LEAF: - return(buf_page_optimistic_get(*latch_mode, block, - modify_clock, mtr)); - case BTR_SEARCH_PREV: /* btr_pcur_move_backward_from_page() */ - case BTR_MODIFY_PREV: /* Ditto, or ibuf_insert() */ - uint32_t curr_page_no, left_page_no; - { - transactional_shared_lock_guard g{ - block->page.lock}; - if (block->modify_clock != modify_clock) { - return false; - } - curr_page_no = block->page.id().page_no(); - left_page_no = btr_page_get_prev(block->page.frame); - } - - static_assert(BTR_SEARCH_PREV & BTR_SEARCH_LEAF, ""); - static_assert(BTR_MODIFY_PREV & BTR_MODIFY_LEAF, ""); - static_assert((BTR_SEARCH_PREV ^ BTR_MODIFY_PREV) - == (RW_S_LATCH ^ RW_X_LATCH), ""); - - const rw_lock_type_t mode = rw_lock_type_t( - *latch_mode & (RW_X_LATCH | RW_S_LATCH)); - - if (left_page_no != FIL_NULL) { - cursor->left_block = buf_page_get_gen( - page_id_t(cursor->index()->table->space_id, - left_page_no), - cursor->index()->table->space->zip_size(), - mode, nullptr, BUF_GET_POSSIBLY_FREED, mtr); - - if (cursor->left_block - && btr_page_get_next( - cursor->left_block->page.frame) - != curr_page_no) { -release_left_block: - mtr->release_last_page(); - return false; - } - } else { - cursor->left_block = nullptr; - } - - if (buf_page_optimistic_get(mode, block, modify_clock, mtr)) { - if (btr_page_get_prev(block->page.frame) - == left_page_no) { - /* block was already buffer-fixed while - entering the function and - buf_page_optimistic_get() buffer-fixes - it again. */ - ut_ad(2 <= block->page.buf_fix_count()); - *latch_mode = btr_latch_mode(mode); - return(true); - } - - mtr->release_last_page(); - } - - ut_ad(block->page.buf_fix_count()); - if (cursor->left_block) { - goto release_left_block; - } - } - - return false; -} - /** Gets intention in btr_intention_t from latch_mode, and cleares the intention at the latch_mode. @@ -848,38 +698,6 @@ btr_intention_t btr_cur_get_and_clear_intention(btr_latch_mode *latch_mode) return(intention); } -/** -Gets the desired latch type for the root leaf (root page is root leaf) -at the latch mode. -@param latch_mode in: BTR_SEARCH_LEAF, ... -@return latch type */ -static -rw_lock_type_t -btr_cur_latch_for_root_leaf( - ulint latch_mode) -{ - switch (latch_mode) { - case BTR_SEARCH_LEAF: - case BTR_SEARCH_TREE: - case BTR_SEARCH_PREV: - return(RW_S_LATCH); - case BTR_MODIFY_LEAF: - case BTR_MODIFY_TREE: - case BTR_MODIFY_PREV: - return(RW_X_LATCH); - case BTR_CONT_MODIFY_TREE: - case BTR_CONT_SEARCH_TREE: - /* A root page should be latched already, - and don't need to be latched here. - fall through (RW_NO_LATCH) */ - case BTR_NO_LATCHES: - return(RW_NO_LATCH); - } - - MY_ASSERT_UNREACHABLE(); - return(RW_NO_LATCH); /* avoid compiler warnings */ -} - /** @return whether the distance between two records is at most the specified value */ static bool @@ -1197,1223 +1015,841 @@ static ulint btr_node_ptr_max_size(const dict_index_t* index) return rec_max_size; } -/********************************************************************//** -Searches an index tree and positions a tree cursor on a given level. -NOTE: n_fields_cmp in tuple must be set so that it cannot be compared -to node pointer page number fields on the upper levels of the tree! -Note that if mode is PAGE_CUR_LE, which is used in inserts, then -cursor->up_match and cursor->low_match both will have sensible values. -If mode is PAGE_CUR_GE, then up_match will a have a sensible value. - -If mode is PAGE_CUR_LE , cursor is left at the place where an insert of the -search tuple should be performed in the B-tree. InnoDB does an insert -immediately after the cursor. Thus, the cursor may end up on a user record, -or on a page infimum record. -@param level the tree level of search -@param tuple data tuple; NOTE: n_fields_cmp in tuple must be set so that - it cannot get compared to the node ptr page number field! -@param mode PAGE_CUR_L, ...; NOTE that if the search is made using a - unique prefix of a record, mode should be PAGE_CUR_LE, not - PAGE_CUR_GE, as the latter may end up on the previous page of - the record! Inserts should always be made using PAGE_CUR_LE - to search the position! -@param latch_mode BTR_SEARCH_LEAF, ..., ORed with at most one of BTR_INSERT, - BTR_DELETE_MARK, or BTR_DELETE; - cursor->left_block is used to store a pointer to the left - neighbor page -@param cursor tree cursor; the cursor page is s- or x-latched, but see also - above! -@param mtr mini-transaction -@param autoinc PAGE_ROOT_AUTO_INC to be written (0 if none) -@return DB_SUCCESS on success or error code otherwise */ -TRANSACTIONAL_TARGET -dberr_t btr_cur_search_to_nth_level(ulint level, - const dtuple_t *tuple, - page_cur_mode_t mode, - btr_latch_mode latch_mode, - btr_cur_t *cursor, mtr_t *mtr, - ib_uint64_t autoinc) +/** @return a B-tree search mode suitable for non-leaf pages +@param mode leaf page search mode */ +static inline page_cur_mode_t btr_cur_nonleaf_mode(page_cur_mode_t mode) { - page_t* page = NULL; /* remove warning */ - buf_block_t* block; - buf_block_t* guess; - ulint height; - ulint up_match; - ulint up_bytes; - ulint low_match; - ulint low_bytes; - ulint rw_latch; - page_cur_mode_t page_mode; - page_cur_mode_t search_mode = PAGE_CUR_UNSUPP; - ulint buf_mode; - ulint node_ptr_max_size = srv_page_size / 2; - page_cur_t* page_cursor; - btr_op_t btr_op; - ulint root_height = 0; /* remove warning */ - - btr_intention_t lock_intention; - buf_block_t* tree_blocks[BTR_MAX_LEVELS]; - ulint tree_savepoints[BTR_MAX_LEVELS]; - ulint n_blocks = 0; - ulint n_releases = 0; - bool detected_same_key_root = false; - - ulint leftmost_from_level = 0; - buf_block_t** prev_tree_blocks = NULL; - ulint* prev_tree_savepoints = NULL; - ulint prev_n_blocks = 0; - ulint prev_n_releases = 0; - bool need_path = true; - bool rtree_parent_modified = false; - bool mbr_adj = false; - bool found = false; - dict_index_t * const index = cursor->index(); - - DBUG_ENTER("btr_cur_search_to_nth_level"); - -#ifdef BTR_CUR_ADAPT - btr_search_t* info; -#endif /* BTR_CUR_ADAPT */ - mem_heap_t* heap = NULL; - rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; - rec_offs* offsets = offsets_; - rec_offs offsets2_[REC_OFFS_NORMAL_SIZE]; - rec_offs* offsets2 = offsets2_; - rec_offs_init(offsets_); - rec_offs_init(offsets2_); - /* Currently, PAGE_CUR_LE is the only search mode used for searches - ending to upper levels */ - - ut_ad(level == 0 || mode == PAGE_CUR_LE - || RTREE_SEARCH_MODE(mode)); - ut_ad(dict_index_check_search_tuple(index, tuple)); - ut_ad(!dict_index_is_ibuf(index) || ibuf_inside(mtr)); - ut_ad(dtuple_check_typed(tuple)); - ut_ad(!(index->type & DICT_FTS)); - ut_ad(index->page != FIL_NULL); - - MEM_UNDEFINED(&cursor->up_match, sizeof cursor->up_match); - MEM_UNDEFINED(&cursor->up_bytes, sizeof cursor->up_bytes); - MEM_UNDEFINED(&cursor->low_match, sizeof cursor->low_match); - MEM_UNDEFINED(&cursor->low_bytes, sizeof cursor->low_bytes); -#ifdef UNIV_DEBUG - cursor->up_match = ULINT_UNDEFINED; - cursor->low_match = ULINT_UNDEFINED; -#endif /* UNIV_DEBUG */ - - const bool latch_by_caller = latch_mode & BTR_ALREADY_S_LATCHED; - - ut_ad(!latch_by_caller - || srv_read_only_mode - || mtr->memo_contains_flagged(&index->lock, MTR_MEMO_S_LOCK - | MTR_MEMO_SX_LOCK)); - - /* These flags are mutually exclusive, they are lumped together - with the latch mode for historical reasons. It's possible for - none of the flags to be set. */ - switch (UNIV_EXPECT(latch_mode & BTR_DELETE, 0)) { - default: - btr_op = BTR_NO_OP; - break; - case BTR_INSERT: - btr_op = (latch_mode & BTR_IGNORE_SEC_UNIQUE) - ? BTR_INSERT_IGNORE_UNIQUE_OP - : BTR_INSERT_OP; - break; - case BTR_DELETE: - btr_op = BTR_DELETE_OP; - ut_a(cursor->purge_node); - break; - case BTR_DELETE_MARK: - btr_op = BTR_DELMARK_OP; - break; - } + if (mode > PAGE_CUR_GE) + { + ut_ad(mode == PAGE_CUR_L || mode == PAGE_CUR_LE); + return mode; + } + if (mode == PAGE_CUR_GE) + return PAGE_CUR_L; + ut_ad(mode == PAGE_CUR_G); + return PAGE_CUR_LE; +} - /* Operations on the insert buffer tree cannot be buffered. */ - ut_ad(btr_op == BTR_NO_OP || !dict_index_is_ibuf(index)); - /* Operations on the clustered index cannot be buffered. */ - ut_ad(btr_op == BTR_NO_OP || !dict_index_is_clust(index)); - /* Operations on the temporary table(indexes) cannot be buffered. */ - ut_ad(btr_op == BTR_NO_OP || !index->table->is_temporary()); - /* Operation on the spatial index cannot be buffered. */ - ut_ad(btr_op == BTR_NO_OP || !dict_index_is_spatial(index)); +dberr_t btr_cur_t::search_leaf(const dtuple_t *tuple, page_cur_mode_t mode, + btr_latch_mode latch_mode, mtr_t *mtr) +{ + ut_ad(index()->is_btree() || index()->is_ibuf()); + ut_ad(!index()->is_ibuf() || ibuf_inside(mtr)); - lock_intention = btr_cur_get_and_clear_intention(&latch_mode); + buf_block_t *guess; + btr_op_t btr_op; + btr_intention_t lock_intention; + bool detected_same_key_root= false; - /* Turn the flags unrelated to the latch mode off. */ - latch_mode = BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); + mem_heap_t* heap = NULL; + rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; + rec_offs* offsets = offsets_; + rec_offs offsets2_[REC_OFFS_NORMAL_SIZE]; + rec_offs* offsets2 = offsets2_; + rec_offs_init(offsets_); + rec_offs_init(offsets2_); + + ut_ad(dict_index_check_search_tuple(index(), tuple)); + ut_ad(dtuple_check_typed(tuple)); + ut_ad(index()->page != FIL_NULL); + + MEM_UNDEFINED(&up_match, sizeof up_match); + MEM_UNDEFINED(&up_bytes, sizeof up_bytes); + MEM_UNDEFINED(&low_match, sizeof low_match); + MEM_UNDEFINED(&low_bytes, sizeof low_bytes); + ut_d(up_match= ULINT_UNDEFINED); + ut_d(low_match= ULINT_UNDEFINED); + + ut_ad(!(latch_mode & BTR_ALREADY_S_LATCHED) || + mtr->memo_contains_flagged(&index()->lock, + MTR_MEMO_S_LOCK | MTR_MEMO_SX_LOCK | + MTR_MEMO_X_LOCK)); + + /* These flags are mutually exclusive, they are lumped together + with the latch mode for historical reasons. It's possible for + none of the flags to be set. */ + switch (UNIV_EXPECT(latch_mode & BTR_DELETE, 0)) { + default: + btr_op= BTR_NO_OP; + break; + case BTR_INSERT: + btr_op= (latch_mode & BTR_IGNORE_SEC_UNIQUE) + ? BTR_INSERT_IGNORE_UNIQUE_OP + : BTR_INSERT_OP; + break; + case BTR_DELETE: + btr_op= BTR_DELETE_OP; + ut_a(purge_node); + break; + case BTR_DELETE_MARK: + btr_op= BTR_DELMARK_OP; + break; + } - ut_ad(!latch_by_caller - || latch_mode == BTR_SEARCH_LEAF - || latch_mode == BTR_SEARCH_TREE - || latch_mode == BTR_MODIFY_LEAF); + /* Operations on the insert buffer tree cannot be buffered. */ + ut_ad(btr_op == BTR_NO_OP || !index()->is_ibuf()); + /* Operations on the clustered index cannot be buffered. */ + ut_ad(btr_op == BTR_NO_OP || !index()->is_clust()); + /* Operations on the temporary table(indexes) cannot be buffered. */ + ut_ad(btr_op == BTR_NO_OP || !index()->table->is_temporary()); - ut_ad(autoinc == 0 || dict_index_is_clust(index)); - ut_ad(autoinc == 0 - || latch_mode == BTR_MODIFY_TREE - || latch_mode == BTR_MODIFY_LEAF); - ut_ad(autoinc == 0 || level == 0); + const bool latch_by_caller= latch_mode & BTR_ALREADY_S_LATCHED; + lock_intention= btr_cur_get_and_clear_intention(&latch_mode); + latch_mode= BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); - cursor->flag = BTR_CUR_BINARY; + ut_ad(!latch_by_caller + || latch_mode == BTR_SEARCH_LEAF + || latch_mode == BTR_MODIFY_LEAF + || latch_mode == BTR_MODIFY_TREE + || latch_mode == BTR_MODIFY_ROOT_AND_LEAF); + flag= BTR_CUR_BINARY; #ifndef BTR_CUR_ADAPT - guess = NULL; + guess= nullptr; #else - info = btr_search_get_info(index); - guess = info->root_guess; - -#ifdef BTR_CUR_HASH_ADAPT + btr_search_t *info= btr_search_get_info(index()); + guess= info->root_guess; + +# ifdef BTR_CUR_HASH_ADAPT +# ifdef UNIV_SEARCH_PERF_STAT + info->n_searches++; +# endif + /* We do a dirty read of btr_search_enabled below, + and btr_search_guess_on_hash() will have to check it again. */ + if (!btr_search_enabled); + else if (btr_search_guess_on_hash(index(), info, tuple, mode, + latch_mode, this, mtr)) + { + /* Search using the hash index succeeded */ + ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_GE); + ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); + ut_ad(low_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); + ++btr_cur_n_sea; -# ifdef UNIV_SEARCH_PERF_STAT - info->n_searches++; + return DB_SUCCESS; + } + else + ++btr_cur_n_non_sea; # endif - /* We do a dirty read of btr_search_enabled below, - and btr_search_guess_on_hash() will have to check it again. */ - if (!btr_search_enabled) { - } else if (autoinc == 0 - && latch_mode <= BTR_MODIFY_LEAF -# ifdef PAGE_CUR_LE_OR_EXTENDS - && mode != PAGE_CUR_LE_OR_EXTENDS -# endif /* PAGE_CUR_LE_OR_EXTENDS */ - && info->last_hash_succ - && !(tuple->info_bits & REC_INFO_MIN_REC_FLAG) - && !index->is_spatial() && !index->table->is_temporary() - && btr_search_guess_on_hash(index, info, tuple, mode, - latch_mode, cursor, mtr)) { - - /* Search using the hash index succeeded */ - - ut_ad(cursor->up_match != ULINT_UNDEFINED - || mode != PAGE_CUR_GE); - ut_ad(cursor->up_match != ULINT_UNDEFINED - || mode != PAGE_CUR_LE); - ut_ad(cursor->low_match != ULINT_UNDEFINED - || mode != PAGE_CUR_LE); - ++btr_cur_n_sea; - - DBUG_RETURN(DB_SUCCESS); - } else { - ++btr_cur_n_non_sea; - } -# endif /* BTR_CUR_HASH_ADAPT */ -#endif /* BTR_CUR_ADAPT */ - - /* If the hash search did not succeed, do binary search down the - tree */ - - /* Store the position of the tree latch we push to mtr so that we - know how to release it when we have latched leaf node(s) */ - - ulint savepoint = mtr_set_savepoint(mtr); - - rw_lock_type_t upper_rw_latch; - - switch (latch_mode) { - case BTR_MODIFY_TREE: - /* Most of delete-intended operations are purging. - Free blocks and read IO bandwidth should be prior - for them, when the history list is glowing huge. */ - if (lock_intention == BTR_INTENTION_DELETE - && buf_pool.n_pend_reads - && trx_sys.history_size_approx() - > BTR_CUR_FINE_HISTORY_LENGTH) { -x_latch_index: - mtr_x_lock_index(index, mtr); - } else if (index->is_spatial() - && lock_intention <= BTR_INTENTION_BOTH) { - /* X lock the if there is possibility of - pessimistic delete on spatial index. As we could - lock upward for the tree */ - goto x_latch_index; - } else { - mtr_sx_lock_index(index, mtr); - } - upper_rw_latch = RW_X_LATCH; - break; - case BTR_CONT_MODIFY_TREE: - ut_ad(srv_read_only_mode - || mtr->memo_contains_flagged(&index->lock, - MTR_MEMO_X_LOCK - | MTR_MEMO_SX_LOCK)); - if (index->is_spatial()) { - /* If we are about to locate parent page for split - and/or merge operation for R-Tree index, X latch - the parent */ - upper_rw_latch = RW_X_LATCH; - break; - } - /* fall through */ - case BTR_CONT_SEARCH_TREE: - /* Do nothing */ - ut_ad(srv_read_only_mode - || mtr->memo_contains_flagged(&index->lock, - MTR_MEMO_X_LOCK - | MTR_MEMO_SX_LOCK)); - upper_rw_latch = RW_NO_LATCH; - break; - default: - if (!srv_read_only_mode) { - if (!latch_by_caller) { - ut_ad(latch_mode != BTR_SEARCH_TREE); - mtr_s_lock_index(index, mtr); - } - upper_rw_latch = RW_S_LATCH; - } else { - upper_rw_latch = RW_NO_LATCH; - } - } - const rw_lock_type_t root_leaf_rw_latch = btr_cur_latch_for_root_leaf( - latch_mode); - - page_cursor = btr_cur_get_page_cur(cursor); - page_cursor->index = index; - - const ulint zip_size = index->table->space->zip_size(); - - /* Start with the root page. */ - page_id_t page_id(index->table->space_id, index->page); - - if (root_leaf_rw_latch == RW_X_LATCH) { - node_ptr_max_size = btr_node_ptr_max_size(index); - } - - up_match = 0; - up_bytes = 0; - low_match = 0; - low_bytes = 0; - - height = ULINT_UNDEFINED; - - /* We use these modified search modes on non-leaf levels of the - B-tree. These let us end up in the right B-tree leaf. In that leaf - we use the original search mode. */ - - switch (mode) { - case PAGE_CUR_GE: - page_mode = PAGE_CUR_L; - break; - case PAGE_CUR_G: - page_mode = PAGE_CUR_LE; - break; - default: -#ifdef PAGE_CUR_LE_OR_EXTENDS - ut_ad(mode == PAGE_CUR_L || mode == PAGE_CUR_LE - || RTREE_SEARCH_MODE(mode) - || mode == PAGE_CUR_LE_OR_EXTENDS); -#else /* PAGE_CUR_LE_OR_EXTENDS */ - ut_ad(mode == PAGE_CUR_L || mode == PAGE_CUR_LE - || RTREE_SEARCH_MODE(mode)); -#endif /* PAGE_CUR_LE_OR_EXTENDS */ - page_mode = mode; - break; - } - - /* Loop and search until we arrive at the desired level */ - btr_latch_leaves_t latch_leaves = {{NULL, NULL, NULL}, {0, 0, 0}}; - -search_loop: - buf_mode = BUF_GET; - rw_latch = RW_NO_LATCH; - rtree_parent_modified = false; - - if (height != 0) { - /* We are about to fetch the root or a non-leaf page. */ - if ((latch_mode != BTR_MODIFY_TREE || height == level) - && !prev_tree_blocks) { - /* If doesn't have SX or X latch of index, - each pages should be latched before reading. */ - if (height == ULINT_UNDEFINED - && upper_rw_latch == RW_S_LATCH - && autoinc) { - /* needs sx-latch of root page - for writing PAGE_ROOT_AUTO_INC */ - rw_latch = RW_SX_LATCH; - } else { - rw_latch = upper_rw_latch; - } - } - } else if (latch_mode <= BTR_MODIFY_LEAF) { - rw_latch = latch_mode; - - if (btr_op != BTR_NO_OP - && ibuf_should_try(index, btr_op != BTR_INSERT_OP)) { - - /* Try to buffer the operation if the leaf - page is not in the buffer pool. */ - - buf_mode = btr_op == BTR_DELETE_OP - ? BUF_GET_IF_IN_POOL_OR_WATCH - : BUF_GET_IF_IN_POOL; - } - } - -retry_page_get: - ut_ad(n_blocks < BTR_MAX_LEVELS); - tree_savepoints[n_blocks] = mtr_set_savepoint(mtr); - dberr_t err; - block = buf_page_get_gen(page_id, zip_size, rw_latch, guess, - buf_mode, mtr, &err, - height == 0 && !index->is_clust()); - if (!block) { - switch (err) { - case DB_SUCCESS: - /* change buffering */ - break; - case DB_DECRYPTION_FAILED: - btr_decryption_failed(*index); - /* fall through */ - default: - goto func_exit; - } - - /* This must be a search to perform an insert/delete - mark/ delete; try using the insert/delete buffer */ - - ut_ad(height == 0); - ut_ad(cursor->thr); - - switch (btr_op) { - default: - MY_ASSERT_UNREACHABLE(); - break; - case BTR_INSERT_OP: - case BTR_INSERT_IGNORE_UNIQUE_OP: - ut_ad(buf_mode == BUF_GET_IF_IN_POOL); - ut_ad(!dict_index_is_spatial(index)); - - if (ibuf_insert(IBUF_OP_INSERT, tuple, index, - page_id, zip_size, cursor->thr)) { - - cursor->flag = BTR_CUR_INSERT_TO_IBUF; - - goto func_exit; - } - break; - - case BTR_DELMARK_OP: - ut_ad(buf_mode == BUF_GET_IF_IN_POOL); - ut_ad(!dict_index_is_spatial(index)); - - if (ibuf_insert(IBUF_OP_DELETE_MARK, tuple, - index, page_id, zip_size, - cursor->thr)) { - - cursor->flag = BTR_CUR_DEL_MARK_IBUF; - - goto func_exit; - } - - break; - - case BTR_DELETE_OP: - ut_ad(buf_mode == BUF_GET_IF_IN_POOL_OR_WATCH); - ut_ad(!dict_index_is_spatial(index)); - auto& chain = buf_pool.page_hash.cell_get( - page_id.fold()); - - if (!row_purge_poss_sec(cursor->purge_node, - index, tuple)) { - - /* The record cannot be purged yet. */ - cursor->flag = BTR_CUR_DELETE_REF; - } else if (ibuf_insert(IBUF_OP_DELETE, tuple, - index, page_id, zip_size, - cursor->thr)) { - - /* The purge was buffered. */ - cursor->flag = BTR_CUR_DELETE_IBUF; - } else { - /* The purge could not be buffered. */ - buf_pool.watch_unset(page_id, chain); - break; - } - - buf_pool.watch_unset(page_id, chain); - goto func_exit; - } - - /* Insert to the insert/delete buffer did not succeed, we - must read the page from disk. */ - - buf_mode = BUF_GET; - - goto retry_page_get; - } - - tree_blocks[n_blocks] = block; - - if (height && prev_tree_blocks) { - /* also latch left sibling */ - ut_ad(rw_latch == RW_NO_LATCH); - - rw_latch = upper_rw_latch; - - /* Because we are holding index->lock, no page splits - or merges may run concurrently, and we may read - FIL_PAGE_PREV from a buffer-fixed, unlatched page. */ - uint32_t left_page_no = btr_page_get_prev(block->page.frame); - - if (left_page_no != FIL_NULL) { - ut_ad(prev_n_blocks < leftmost_from_level); - - prev_tree_savepoints[prev_n_blocks] - = mtr_set_savepoint(mtr); - buf_block_t* get_block = buf_page_get_gen( - page_id_t(page_id.space(), left_page_no), - zip_size, rw_latch, NULL, buf_mode, - mtr, &err); - if (!get_block) { - if (err == DB_DECRYPTION_FAILED) { - btr_decryption_failed(*index); - } - goto func_exit; - } - - prev_tree_blocks[prev_n_blocks++] = get_block; - /* BTR_MODIFY_TREE doesn't update prev/next_page_no, - without their parent page's lock. So, not needed to - retry here, because we have the parent page's lock. */ - } - - mtr->s_lock_register(tree_savepoints[n_blocks]); - block->page.lock.s_lock(); - } - - page = buf_block_get_frame(block); - - if (height == ULINT_UNDEFINED - && page_is_leaf(page) - && rw_latch != RW_NO_LATCH - && rw_latch != root_leaf_rw_latch) { - /* The root page is also a leaf page (root_leaf). - We should reacquire the page, because the root page - is latched differently from leaf pages. */ - ut_ad(root_leaf_rw_latch != RW_NO_LATCH); - ut_ad(rw_latch == RW_S_LATCH || rw_latch == RW_SX_LATCH); - ut_ad(rw_latch == RW_S_LATCH || autoinc); - ut_ad(!autoinc || root_leaf_rw_latch == RW_X_LATCH); - - ut_ad(n_blocks == 0); - mtr_release_block_at_savepoint( - mtr, tree_savepoints[n_blocks], - tree_blocks[n_blocks]); - - upper_rw_latch = root_leaf_rw_latch; - goto search_loop; - } - -#ifdef UNIV_ZIP_DEBUG - if (rw_latch != RW_NO_LATCH) { - const page_zip_des_t* page_zip - = buf_block_get_page_zip(block); - ut_a(!page_zip || page_zip_validate(page_zip, page, index)); - } -#endif /* UNIV_ZIP_DEBUG */ - - ut_ad(fil_page_index_page_check(page)); - ut_ad(index->id == btr_page_get_index_id(page)); - - if (height == ULINT_UNDEFINED) { - /* We are in the root node */ - - height = btr_page_get_level(page); - root_height = height; - cursor->tree_height = root_height + 1; - - if (dict_index_is_spatial(index)) { - ut_ad(cursor->rtr_info); - - /* If SSN in memory is not initialized, fetch - it from root page */ - if (!rtr_get_current_ssn_id(index)) { - /* FIXME: do this in dict_load_table_one() */ - index->set_ssn(page_get_ssn_id(page) + 1); - } - - /* Save the MBR */ - cursor->rtr_info->thr = cursor->thr; - rtr_get_mbr_from_tuple(tuple, &cursor->rtr_info->mbr); - } - -#ifdef BTR_CUR_ADAPT - info->root_guess = block; #endif - } - - if (height == 0) { - if (rw_latch == RW_NO_LATCH) { - btr_cur_latch_leaves(block, latch_mode, cursor, mtr, - &latch_leaves); - } - - switch (latch_mode) { - case BTR_MODIFY_TREE: - case BTR_CONT_MODIFY_TREE: - case BTR_CONT_SEARCH_TREE: - break; - default: - if (!latch_by_caller - && !srv_read_only_mode) { - /* Release the tree s-latch */ - mtr_release_s_latch_at_savepoint( - mtr, savepoint, - &index->lock); - } - - /* release upper blocks */ - if (prev_tree_blocks) { - ut_ad(!autoinc); - for (; - prev_n_releases < prev_n_blocks; - prev_n_releases++) { - mtr_release_block_at_savepoint( - mtr, - prev_tree_savepoints[ - prev_n_releases], - prev_tree_blocks[ - prev_n_releases]); - } - } - - for (; n_releases < n_blocks; n_releases++) { - if (n_releases == 0 - && (autoinc)) { - /* keep the root page latch */ - ut_ad(mtr->memo_contains_flagged( - tree_blocks[n_releases], - MTR_MEMO_PAGE_SX_FIX - | MTR_MEMO_PAGE_X_FIX)); - continue; - } - - mtr_release_block_at_savepoint( - mtr, tree_savepoints[n_releases], - tree_blocks[n_releases]); - } - } - - page_mode = mode; - } - - if (dict_index_is_spatial(index)) { - /* Remember the page search mode */ - search_mode = page_mode; - - /* Some adjustment on search mode, when the - page search mode is PAGE_CUR_RTREE_LOCATE - or PAGE_CUR_RTREE_INSERT, as we are searching - with MBRs. When it is not the target level, we - should search all sub-trees that "CONTAIN" the - search range/MBR. When it is at the target - level, the search becomes PAGE_CUR_LE */ - if (page_mode == PAGE_CUR_RTREE_LOCATE - && level == height) { - if (level == 0) { - page_mode = PAGE_CUR_LE; - } else { - page_mode = PAGE_CUR_RTREE_GET_FATHER; - } - } - - if (page_mode == PAGE_CUR_RTREE_INSERT) { - page_mode = (level == height) - ? PAGE_CUR_LE - : PAGE_CUR_RTREE_INSERT; - - ut_ad(!page_is_leaf(page) || page_mode == PAGE_CUR_LE); - } - - /* "need_path" indicates if we need to tracking the parent - pages, if it is not spatial comparison, then no need to - track it */ - if (page_mode < PAGE_CUR_CONTAIN) { - need_path = false; - } - - up_match = 0; - low_match = 0; - - if (latch_mode == BTR_MODIFY_TREE - || latch_mode == BTR_CONT_MODIFY_TREE - || latch_mode == BTR_CONT_SEARCH_TREE) { - /* Tree are locked, no need for Page Lock to protect - the "path" */ - cursor->rtr_info->need_page_lock = false; - } - } - page_cursor->block = block; + /* If the hash search did not succeed, do binary search down the + tree */ - if (dict_index_is_spatial(index) && page_mode >= PAGE_CUR_CONTAIN) { - ut_ad(need_path); - found = rtr_cur_search_with_match( - block, index, tuple, page_mode, page_cursor, - cursor->rtr_info); + /* Store the position of the tree latch we push to mtr so that we + know how to release it when we have latched leaf node(s) */ - /* Need to use BTR_MODIFY_TREE to do the MBR adjustment */ - if (search_mode == PAGE_CUR_RTREE_INSERT - && cursor->rtr_info->mbr_adj) { - static_assert(BTR_MODIFY_TREE - == (8 | BTR_MODIFY_LEAF), ""); + const ulint savepoint = mtr->get_savepoint(); - if (!(latch_mode & 8)) { - /* Parent MBR needs updated, should retry - with BTR_MODIFY_TREE */ - goto func_exit; - } + ulint node_ptr_max_size= 0; + rw_lock_type_t rw_latch= RW_S_LATCH; - rtree_parent_modified = true; - cursor->rtr_info->mbr_adj = false; - mbr_adj = true; - } + switch (latch_mode) { + case BTR_MODIFY_TREE: + rw_latch= RW_X_LATCH; + node_ptr_max_size= btr_node_ptr_max_size(index()); + if (latch_by_caller) + { + ut_ad(mtr->memo_contains_flagged(&index()->lock, MTR_MEMO_X_LOCK)); + break; + } + if (lock_intention == BTR_INTENTION_DELETE && buf_pool.n_pend_reads && + trx_sys.history_size_approx() > BTR_CUR_FINE_HISTORY_LENGTH) + /* Most delete-intended operations are due to the purge of history. + Prioritize them when the history list is growing huge. */ + mtr_x_lock_index(index(), mtr); + else + mtr_sx_lock_index(index(), mtr); + break; +#ifdef UNIV_DEBUG + case BTR_CONT_MODIFY_TREE: + ut_ad("invalid mode" == 0); + break; +#endif + case BTR_MODIFY_ROOT_AND_LEAF: + rw_latch= RW_SX_LATCH; + /* fall through */ + default: + if (!latch_by_caller) + mtr_s_lock_index(index(), mtr); + } - if (found && page_mode == PAGE_CUR_RTREE_GET_FATHER) { - cursor->low_match = - DICT_INDEX_SPATIAL_NODEPTR_SIZE + 1; - } -#ifdef BTR_CUR_HASH_ADAPT - } else if (height == 0 && btr_search_enabled - && !(tuple->info_bits & REC_INFO_MIN_REC_FLAG) - && index->is_btree()) { - /* The adaptive hash index is only used when searching - for leaf pages (height==0), but not in r-trees. - We only need the byte prefix comparison for the purpose - of updating the adaptive hash index. */ - if (page_cur_search_with_match_bytes( - tuple, page_mode, &up_match, &up_bytes, - &low_match, &low_bytes, page_cursor)) { - err = DB_CORRUPTION; - goto func_exit; - } -#endif /* BTR_CUR_HASH_ADAPT */ - } else { - /* Search for complete index fields. */ - up_bytes = low_bytes = 0; - if (page_cur_search_with_match( - tuple, page_mode, &up_match, - &low_match, page_cursor, - need_path ? cursor->rtr_info : nullptr)) { - err = DB_CORRUPTION; - goto func_exit; - } - } + const ulint zip_size= index()->table->space->zip_size(); - /* If this is the desired level, leave the loop */ + /* Start with the root page. */ + page_id_t page_id(index()->table->space_id, index()->page); - ut_ad(height == btr_page_get_level(page_cur_get_page(page_cursor))); + const page_cur_mode_t page_mode= btr_cur_nonleaf_mode(mode); + ulint height= ULINT_UNDEFINED; + up_match= 0; + up_bytes= 0; + low_match= 0; + low_bytes= 0; + ulint buf_mode= BUF_GET; + search_loop: + dberr_t err; + auto block_savepoint= mtr->get_savepoint(); + buf_block_t *block= + buf_page_get_gen(page_id, zip_size, rw_latch, guess, buf_mode, mtr, + &err, height == 0 && !index()->is_clust()); + if (!block) + { + switch (err) { + case DB_DECRYPTION_FAILED: + btr_decryption_failed(*index()); + /* fall through */ + default: + func_exit: + if (UNIV_LIKELY_NULL(heap)) + mem_heap_free(heap); + return err; + case DB_SUCCESS: + /* This must be a search to perform an insert, delete mark, or delete; + try using the change buffer */ + ut_ad(height == 0); + ut_ad(thr); + break; + } - /* Add Predicate lock if it is serializable isolation - and only if it is in the search case */ - if (dict_index_is_spatial(index) - && cursor->rtr_info->need_prdt_lock - && mode != PAGE_CUR_RTREE_INSERT - && mode != PAGE_CUR_RTREE_LOCATE - && mode >= PAGE_CUR_CONTAIN) { - lock_prdt_t prdt; + switch (btr_op) { + default: + MY_ASSERT_UNREACHABLE(); + break; + case BTR_INSERT_OP: + case BTR_INSERT_IGNORE_UNIQUE_OP: + ut_ad(buf_mode == BUF_GET_IF_IN_POOL); - { - trx_t* trx = thr_get_trx(cursor->thr); - TMLockTrxGuard g{TMLockTrxArgs(*trx)}; - lock_init_prdt_from_mbr( - &prdt, &cursor->rtr_info->mbr, mode, - trx->lock.lock_heap); - } + if (ibuf_insert(IBUF_OP_INSERT, tuple, index(), page_id, zip_size, thr)) + { + flag= BTR_CUR_INSERT_TO_IBUF; + goto func_exit; + } + break; - if (rw_latch == RW_NO_LATCH && height != 0) { - block->page.lock.s_lock(); - } + case BTR_DELMARK_OP: + ut_ad(buf_mode == BUF_GET_IF_IN_POOL); - lock_prdt_lock(block, &prdt, index, LOCK_S, - LOCK_PREDICATE, cursor->thr); + if (ibuf_insert(IBUF_OP_DELETE_MARK, tuple, + index(), page_id, zip_size, thr)) + { + flag = BTR_CUR_DEL_MARK_IBUF; + goto func_exit; + } - if (rw_latch == RW_NO_LATCH && height != 0) { - block->page.lock.s_unlock(); - } - } + break; - if (level != height) { + case BTR_DELETE_OP: + ut_ad(buf_mode == BUF_GET_IF_IN_POOL_OR_WATCH); + auto& chain = buf_pool.page_hash.cell_get(page_id.fold()); + + if (!row_purge_poss_sec(purge_node, index(), tuple)) + /* The record cannot be purged yet. */ + flag= BTR_CUR_DELETE_REF; + else if (ibuf_insert(IBUF_OP_DELETE, tuple, index(), + page_id, zip_size, thr)) + /* The purge was buffered. */ + flag= BTR_CUR_DELETE_IBUF; + else + { + /* The purge could not be buffered. */ + buf_pool.watch_unset(page_id, chain); + break; + } - const rec_t* node_ptr; - ut_ad(height > 0); + buf_pool.watch_unset(page_id, chain); + goto func_exit; + } - height--; - guess = NULL; + /* Change buffering did not succeed, we must read the page. */ + buf_mode= BUF_GET; + goto search_loop; + } - node_ptr = page_cur_get_rec(page_cursor); + if (!!page_is_comp(block->page.frame) != index()->table->not_redundant() || + btr_page_get_index_id(block->page.frame) != index()->id || + fil_page_get_type(block->page.frame) == FIL_PAGE_RTREE || + !fil_page_index_page_check(block->page.frame)) + { + corrupted: + ut_ad("corrupted" == 0); // FIXME: remove this + err= DB_CORRUPTION; + goto func_exit; + } - offsets = rec_get_offsets(node_ptr, index, offsets, 0, - ULINT_UNDEFINED, &heap); + page_cur.block= block; + ut_ad(block == mtr->at_savepoint(block_savepoint)); + const page_t *page= buf_block_get_frame(block); +#ifdef UNIV_ZIP_DEBUG + if (rw_latch != RW_NO_LATCH) + { + const page_zip_des_t *page_zip= buf_block_get_page_zip(block); + ut_a(!page_zip || page_zip_validate(page_zip, page, index())); + } +#endif /* UNIV_ZIP_DEBUG */ + const uint32_t page_level= btr_page_get_level(page); - /* If the rec is the first or last in the page for - pessimistic delete intention, it might cause node_ptr insert - for the upper level. We should change the intention and retry. - */ - if (latch_mode == BTR_MODIFY_TREE - && btr_cur_need_opposite_intention( - page, lock_intention, node_ptr)) { + if (height == ULINT_UNDEFINED) + { + /* We are in the B-tree index root page. */ +#ifdef BTR_CUR_ADAPT + info->root_guess= block; +#endif + height= page_level; + tree_height= height + 1; -need_opposite_intention: - ut_ad(upper_rw_latch == RW_X_LATCH); + if (!height) + { + /* The root page is also a leaf page. + We may have to reacquire the page latch in a different mode. */ + switch (rw_latch) { + case RW_S_LATCH: + if ((latch_mode & ~12) != RW_S_LATCH) + { + rw_latch= rw_lock_type_t(latch_mode & ~12); + ut_ad(rw_latch == RW_X_LATCH || rw_latch == RW_SX_LATCH); + goto relatch; + } + if (latch_mode != BTR_MODIFY_PREV) + { + if (!latch_by_caller) + /* Release the tree s-latch */ + mtr->rollback_to_savepoint(savepoint, savepoint + 1); + goto reached_latched_leaf; + } + /* fall through */ + case RW_SX_LATCH: + ut_ad(rw_latch == RW_S_LATCH || + latch_mode == BTR_MODIFY_ROOT_AND_LEAF); + rw_latch= RW_X_LATCH; + relatch: + mtr->rollback_to_savepoint(block_savepoint); + height= ULINT_UNDEFINED; + goto search_loop; + case RW_X_LATCH: + if (latch_mode == BTR_MODIFY_TREE) + goto reached_index_root_and_leaf; + goto reached_root_and_leaf; + case RW_NO_LATCH: + ut_ad(mtr->memo_contains_flagged(&index()->lock, MTR_MEMO_X_LOCK)); + } + goto reached_leaf; + } + } + else if (UNIV_UNLIKELY(height != page_level)) + goto corrupted; + else + switch (latch_mode) { + case BTR_MODIFY_TREE: + break; + case BTR_MODIFY_ROOT_AND_LEAF: + ut_ad((mtr->at_savepoint(block_savepoint - 1)->page.id().page_no() == + index()->page) == (tree_height <= height + 2)); + if (tree_height <= height + 2) + /* Retain the root page latch. */ + break; + /* fall through */ + default: + /* Release the parent page latch. */ + ut_ad(block_savepoint > savepoint); + mtr->rollback_to_savepoint(block_savepoint - 1, block_savepoint); + block_savepoint--; + } - if (n_releases > 0) { - /* release root block */ - mtr_release_block_at_savepoint( - mtr, tree_savepoints[0], - tree_blocks[0]); - } + if (!height) + { + reached_leaf: + /* We reached the leaf level. */ + ut_ad(block == mtr->at_savepoint(block_savepoint)); - /* release all blocks */ - for (; n_releases <= n_blocks; n_releases++) { - mtr_release_block_at_savepoint( - mtr, tree_savepoints[n_releases], - tree_blocks[n_releases]); - } + if (latch_mode == BTR_MODIFY_ROOT_AND_LEAF) + { + reached_root_and_leaf: + if (!latch_by_caller) + mtr->rollback_to_savepoint(savepoint, savepoint + 1); + reached_index_root_and_leaf: + ut_ad(rw_latch == RW_X_LATCH); +#ifdef BTR_CUR_HASH_ADAPT + btr_search_drop_page_hash_index(block, true); +#endif + if (page_cur_search_with_match(tuple, mode, &up_match, &low_match, + &page_cur, nullptr)) + goto corrupted; + ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_GE); + ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); + ut_ad(low_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); + goto func_exit; + } - lock_intention = BTR_INTENTION_BOTH; + if (rw_latch == RW_NO_LATCH) + btr_cur_latch_leaves(block_savepoint, latch_mode, this, mtr); - page_id.set_page_no(index->page); - up_match = 0; - low_match = 0; - height = ULINT_UNDEFINED; + if (latch_mode != BTR_MODIFY_TREE) + { + if (!latch_by_caller) + { + /* Release the tree s-latch */ + block_savepoint--; + mtr->rollback_to_savepoint(savepoint, savepoint + 1); + } + /* release upper blocks */ + if (savepoint < block_savepoint) + mtr->rollback_to_savepoint(savepoint, block_savepoint); + } + else + ut_ad(rw_latch == RW_NO_LATCH); - n_blocks = 0; - n_releases = 0; + reached_latched_leaf: +#ifdef BTR_CUR_HASH_ADAPT + if (btr_search_enabled && !(tuple->info_bits & REC_INFO_MIN_REC_FLAG)) + { + if (page_cur_search_with_match_bytes(tuple, mode, + &up_match, &up_bytes, + &low_match, &low_bytes, &page_cur)) + goto corrupted; + } + else +#endif /* BTR_CUR_HASH_ADAPT */ + if (page_cur_search_with_match(tuple, mode, &up_match, &low_match, + &page_cur, nullptr)) + goto corrupted; - goto search_loop; - } + ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_GE); + ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); + ut_ad(low_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); - if (dict_index_is_spatial(index)) { - if (page_rec_is_supremum(node_ptr)) { - cursor->low_match = 0; - cursor->up_match = 0; - goto func_exit; - } +#ifdef BTR_CUR_HASH_ADAPT + /* We do a dirty read of btr_search_enabled here. We will + properly check btr_search_enabled again in + btr_search_build_page_hash_index() before building a page hash + index, while holding search latch. */ + if (!btr_search_enabled); + else if (tuple->info_bits & REC_INFO_MIN_REC_FLAG) + /* This may be a search tuple for btr_pcur_t::restore_position(). */ + ut_ad(tuple->is_metadata() || + (tuple->is_metadata(tuple->info_bits ^ REC_STATUS_INSTANT))); + else if (index()->table->is_temporary()); + else if (!rec_is_metadata(page_cur.rec, *index())) + btr_search_info_update(index(), this); +#endif /* BTR_CUR_HASH_ADAPT */ - /* If we are doing insertion or record locating, - remember the tree nodes we visited */ - if (page_mode == PAGE_CUR_RTREE_INSERT - || (search_mode == PAGE_CUR_RTREE_LOCATE - && (latch_mode != BTR_MODIFY_LEAF))) { - bool add_latch = false; - - if (latch_mode == BTR_MODIFY_TREE - && rw_latch == RW_NO_LATCH) { - ut_ad(mtr->memo_contains_flagged( - &index->lock, MTR_MEMO_X_LOCK - | MTR_MEMO_SX_LOCK)); - block->page.lock.s_lock(); - add_latch = true; - } + goto func_exit; + } - /* Store the parent cursor location */ -#ifdef UNIV_DEBUG - ulint num_stored = rtr_store_parent_path( - block, cursor, latch_mode, - height + 1, mtr); -#else - rtr_store_parent_path( - block, cursor, latch_mode, - height + 1, mtr); -#endif + guess= nullptr; + if (page_cur_search_with_match(tuple, page_mode, &up_match, &low_match, + &page_cur, nullptr)) + goto corrupted; + offsets= rec_get_offsets(page_cur.rec, index(), offsets, 0, ULINT_UNDEFINED, + &heap); - if (page_mode == PAGE_CUR_RTREE_INSERT) { - btr_pcur_t* r_cursor = - rtr_get_parent_cursor( - cursor, height + 1, - true); - /* If it is insertion, there should - be only one parent for each level - traverse */ -#ifdef UNIV_DEBUG - ut_ad(num_stored == 1); -#endif + ut_ad(block == mtr->at_savepoint(block_savepoint)); - node_ptr = btr_pcur_get_rec(r_cursor); + switch (latch_mode) { + default: + break; + case BTR_MODIFY_TREE: + if (btr_cur_need_opposite_intention(page, lock_intention, page_cur.rec)) + { + /* If the rec is the first or last in the page for pessimistic + delete intention, it might cause node_ptr insert for the upper + level. We should change the intention and retry. */ + need_opposite_intention: + return pessimistic_search_leaf(tuple, mode, mtr); + } - } + if (detected_same_key_root || lock_intention != BTR_INTENTION_BOTH || + index()->is_unique() || + (up_match <= rec_offs_n_fields(offsets) && + low_match <= rec_offs_n_fields(offsets))) + break; - if (add_latch) { - block->page.lock.s_unlock(); - } + /* If the first or the last record of the page or the same key + value to the first record or last record, then another page might + be chosen when BTR_CONT_MODIFY_TREE. So, the parent page should + not released to avoiding deadlock with blocking the another search + with the same key value. */ + const rec_t *first= page_rec_get_next_const(page_get_infimum_rec(page)); + ulint matched_fields; - ut_ad(!page_rec_is_supremum(node_ptr)); - } + if (UNIV_UNLIKELY(!first)) + goto corrupted; + if (page_cur.rec == first || page_rec_is_last(page_cur.rec, page)) + { + same_key_root: + detected_same_key_root= true; + break; + } - ut_ad(page_mode == search_mode - || (page_mode == PAGE_CUR_WITHIN - && search_mode == PAGE_CUR_RTREE_LOCATE)); + matched_fields= 0; + offsets2= rec_get_offsets(first, index(), offsets2, 0, ULINT_UNDEFINED, + &heap); + cmp_rec_rec(page_cur.rec, first, offsets, offsets2, index(), false, + &matched_fields); + if (matched_fields >= rec_offs_n_fields(offsets) - 1) + goto same_key_root; + if (const rec_t* last= + page_rec_get_prev_const(page_get_supremum_rec(page))) + { + matched_fields= 0; + offsets2= rec_get_offsets(last, index(), offsets2, 0, ULINT_UNDEFINED, + &heap); + cmp_rec_rec(page_cur.rec, last, offsets, offsets2, index(), false, + &matched_fields); + if (matched_fields >= rec_offs_n_fields(offsets) - 1) + goto same_key_root; + } + else + goto corrupted; - page_mode = search_mode; - } + /* Release the non-root parent page unless it may need to be modified. */ + if (tree_height > height + 1 && + !btr_cur_will_modify_tree(index(), page, lock_intention, + page_cur.rec, node_ptr_max_size, + zip_size, mtr)) + { + mtr->rollback_to_savepoint(block_savepoint - 1, block_savepoint); + block_savepoint--; + } + } - /* If the first or the last record of the page - or the same key value to the first record or last record, - the another page might be chosen when BTR_CONT_MODIFY_TREE. - So, the parent page should not released to avoiding deadlock - with blocking the another search with the same key value. */ - if (!detected_same_key_root - && lock_intention == BTR_INTENTION_BOTH - && !dict_index_is_unique(index) - && latch_mode == BTR_MODIFY_TREE - && (up_match >= rec_offs_n_fields(offsets) - 1 - || low_match >= rec_offs_n_fields(offsets) - 1)) { - const rec_t* first_rec = page_rec_get_next_const( - page_get_infimum_rec(page)); - ulint matched_fields; + /* Go to the child node */ + page_id.set_page_no(btr_node_ptr_get_child_page_no(page_cur.rec, offsets)); - ut_ad(upper_rw_latch == RW_X_LATCH); + if (!--height) + { + /* We are about to access the leaf level. */ + rw_latch= RW_NO_LATCH; - if (UNIV_UNLIKELY(!first_rec)) { - corrupted: - err = DB_CORRUPTION; - goto func_exit; - } - if (node_ptr == first_rec - || page_rec_is_last(node_ptr, page)) { - detected_same_key_root = true; - } else { - matched_fields = 0; - - offsets2 = rec_get_offsets( - first_rec, index, offsets2, - 0, ULINT_UNDEFINED, &heap); - cmp_rec_rec(node_ptr, first_rec, - offsets, offsets2, index, false, - &matched_fields); - - if (matched_fields - >= rec_offs_n_fields(offsets) - 1) { - detected_same_key_root = true; - } else if (const rec_t* last_rec - = page_rec_get_prev_const( - page_get_supremum_rec( - page))) { - matched_fields = 0; - - offsets2 = rec_get_offsets( - last_rec, index, offsets2, - 0, ULINT_UNDEFINED, &heap); - cmp_rec_rec( - node_ptr, last_rec, - offsets, offsets2, index, - false, &matched_fields); - if (matched_fields - >= rec_offs_n_fields(offsets) - 1) { - detected_same_key_root = true; - } - } else { - goto corrupted; - } - } - } + switch (latch_mode) { + case BTR_MODIFY_ROOT_AND_LEAF: + rw_latch= RW_X_LATCH; + break; + default: + break; + case BTR_MODIFY_PREV: + /* This is almost exclusively for ibuf_insert(), but also for + btr_pcur_move_to_prev(); the latter is not exercised by mtr */ + case BTR_SEARCH_PREV: + if (page_has_prev(page) && page_rec_is_first(page_cur.rec, page)) + { + ut_ad(block_savepoint + 1 == mtr->get_savepoint()); + /* Latch the previous page if the node pointer is the leftmost + of the current page. */ + buf_block_t *left= btr_block_get(*index(), btr_page_get_prev(page), + RW_NO_LATCH, false, mtr, &err); + if (!left) + goto func_exit; + static_assert(mtr_memo_type_t(BTR_MODIFY_PREV & ~4) == + MTR_MEMO_PAGE_X_FIX, ""); + static_assert(mtr_memo_type_t(BTR_SEARCH_PREV & ~4) == + MTR_MEMO_PAGE_S_FIX, ""); + mtr->lock_register(block_savepoint + 1, + mtr_memo_type_t(latch_mode & ~4)); + /* Because we are violating the latching order here, we will + have to temporarily release the right page latch if the left + page latch cannot be acquired without waiting. Concurrent page + splits or merges are impossible because we are holding a latch + on the parent of these sibling pages. */ + if (latch_mode == BTR_MODIFY_PREV) + { + if (!left->page.lock.x_lock_try()) + { + block->page.lock.x_unlock(); + left->page.lock.x_lock(); + } + } + else if (!left->page.lock.s_lock_try()) + { + block->page.lock.s_unlock(); + left->page.lock.s_lock(); + } +#ifdef BTR_CUR_HASH_ADAPT + btr_search_drop_page_hash_index(left, true); +#endif + } + break; + case BTR_MODIFY_LEAF: + case BTR_SEARCH_LEAF: + if (index()->is_ibuf()) + break; + rw_latch= rw_lock_type_t(latch_mode); + if (btr_op != BTR_NO_OP && + ibuf_should_try(index(), btr_op != BTR_INSERT_OP)) + /* Try to buffer the operation if the leaf page + is not in the buffer pool. */ + buf_mode= btr_op == BTR_DELETE_OP + ? BUF_GET_IF_IN_POOL_OR_WATCH + : BUF_GET_IF_IN_POOL; + break; + case BTR_MODIFY_TREE: + if (index()->is_ibuf()) + break; + if (lock_intention == BTR_INTENTION_INSERT && page_has_next(page) && + page_rec_is_last(page_cur.rec, page)) + { + /* btr_insert_into_right_sibling() might cause deleting node_ptr + at upper level */ + mtr->rollback_to_savepoint(block_savepoint); + goto need_opposite_intention; + } + } + } - /* If the page might cause modify_tree, - we should not release the parent page's lock. */ - if (!detected_same_key_root - && latch_mode == BTR_MODIFY_TREE - && !btr_cur_will_modify_tree( - index, page, lock_intention, node_ptr, - node_ptr_max_size, zip_size, mtr) - && !rtree_parent_modified) { - ut_ad(upper_rw_latch == RW_X_LATCH); - ut_ad(n_releases <= n_blocks); - - /* we can release upper blocks */ - for (; n_releases < n_blocks; n_releases++) { - if (n_releases == 0) { - /* we should not release root page - to pin to same block. */ - continue; - } + goto search_loop; +} - /* release unused blocks to unpin */ - mtr_release_block_at_savepoint( - mtr, tree_savepoints[n_releases], - tree_blocks[n_releases]); - } - } +ATTRIBUTE_COLD +dberr_t btr_cur_t::pessimistic_search_leaf(const dtuple_t *tuple, + page_cur_mode_t mode, mtr_t *mtr) +{ + ut_ad(index()->is_btree() || index()->is_ibuf()); + ut_ad(!index()->is_ibuf() || ibuf_inside(mtr)); - if (height == level - && latch_mode == BTR_MODIFY_TREE) { - ut_ad(upper_rw_latch == RW_X_LATCH); - /* we should sx-latch root page, if released already. - It contains seg_header. */ - if (n_releases > 0) { - mtr->sx_latch_at_savepoint( - tree_savepoints[0], - tree_blocks[0]); - } + rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; + rec_offs* offsets = offsets_; + rec_offs_init(offsets_); - /* x-latch the branch blocks not released yet. */ - for (ulint i = n_releases; i <= n_blocks; i++) { - mtr->x_latch_at_savepoint( - tree_savepoints[i], - tree_blocks[i]); - } - } + ut_ad(flag == BTR_CUR_BINARY); + ut_ad(dict_index_check_search_tuple(index(), tuple)); + ut_ad(dtuple_check_typed(tuple)); + buf_block_t *block= mtr->at_savepoint(1); + ut_ad(block->page.id().page_no() == index()->page); + block->page.fix(); + mtr->rollback_to_savepoint(1); + ut_ad(mtr->memo_contains_flagged(&index()->lock, + MTR_MEMO_SX_LOCK | MTR_MEMO_X_LOCK)); + + const page_cur_mode_t page_mode{btr_cur_nonleaf_mode(mode)}; + + mtr->page_lock(block, RW_X_LATCH); + + up_match= 0; + up_bytes= 0; + low_match= 0; + low_bytes= 0; + ulint height= btr_page_get_level(block->page.frame); + tree_height= height + 1; + mem_heap_t *heap= nullptr; - /* We should consider prev_page of parent page, if the node_ptr - is the leftmost of the page. because BTR_SEARCH_PREV and - BTR_MODIFY_PREV latches prev_page of the leaf page. */ - if ((latch_mode == BTR_SEARCH_PREV - || latch_mode == BTR_MODIFY_PREV) - && !prev_tree_blocks) { - /* block should be latched for consistent - btr_page_get_prev() */ - ut_ad(mtr->memo_contains_flagged( - block, MTR_MEMO_PAGE_S_FIX - | MTR_MEMO_PAGE_X_FIX)); + search_loop: + dberr_t err; + page_cur.block= block; - if (page_has_prev(page) - && page_rec_is_first(node_ptr, page)) { + if (UNIV_UNLIKELY(!height)) + { + if (page_cur_search_with_match(tuple, mode, &up_match, &low_match, + &page_cur, nullptr)) + corrupted: + err= DB_CORRUPTION; + else + { + ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_GE); + ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); + ut_ad(low_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); - if (leftmost_from_level == 0) { - leftmost_from_level = height + 1; - } - } else { - leftmost_from_level = 0; - } +#ifdef BTR_CUR_HASH_ADAPT + /* We do a dirty read of btr_search_enabled here. We will + properly check btr_search_enabled again in + btr_search_build_page_hash_index() before building a page hash + index, while holding search latch. */ + if (!btr_search_enabled); + else if (tuple->info_bits & REC_INFO_MIN_REC_FLAG) + /* This may be a search tuple for btr_pcur_t::restore_position(). */ + ut_ad(tuple->is_metadata() || + (tuple->is_metadata(tuple->info_bits ^ REC_STATUS_INSTANT))); + else if (index()->table->is_temporary()); + else if (!rec_is_metadata(page_cur.rec, *index())) + btr_search_info_update(index(), this); +#endif /* BTR_CUR_HASH_ADAPT */ + err= DB_SUCCESS; + } - if (height == 0 && leftmost_from_level > 0) { - /* should retry to get also prev_page - from level==leftmost_from_level. */ - prev_tree_blocks = static_cast( - ut_malloc_nokey(sizeof(buf_block_t*) - * leftmost_from_level)); - - prev_tree_savepoints = static_cast( - ut_malloc_nokey(sizeof(ulint) - * leftmost_from_level)); - - /* back to the level (leftmost_from_level+1) */ - ulint idx = n_blocks - - (leftmost_from_level - 1); - - page_id.set_page_no( - tree_blocks[idx]->page.id().page_no()); - - for (ulint i = n_blocks - - (leftmost_from_level - 1); - i <= n_blocks; i++) { - mtr_release_block_at_savepoint( - mtr, tree_savepoints[i], - tree_blocks[i]); - } + func_exit: + if (UNIV_LIKELY_NULL(heap)) + mem_heap_free(heap); + return err; + } - n_blocks -= (leftmost_from_level - 1); - height = leftmost_from_level; - ut_ad(n_releases == 0); - - /* replay up_match, low_match */ - up_match = 0; - low_match = 0; - rtr_info_t* rtr_info = need_path - ? cursor->rtr_info : NULL; - - for (ulint i = 0; i < n_blocks; i++) { - page_cursor->block = tree_blocks[i]; - if (page_cur_search_with_match( - tuple, - page_mode, &up_match, - &low_match, page_cursor, - rtr_info)) { - err = DB_CORRUPTION; - goto func_exit; - } - } + if (page_cur_search_with_match(tuple, page_mode, &up_match, &low_match, + &page_cur, nullptr)) + goto corrupted; - goto search_loop; - } - } + page_id_t page_id{block->page.id()}; - /* Go to the child node */ - page_id.set_page_no( - btr_node_ptr_get_child_page_no(node_ptr, offsets)); + offsets= rec_get_offsets(page_cur.rec, index(), offsets, 0, ULINT_UNDEFINED, + &heap); + /* Go to the child node */ + page_id.set_page_no(btr_node_ptr_get_child_page_no(page_cur.rec, offsets)); - n_blocks++; + const auto block_savepoint= mtr->get_savepoint(); + block= + buf_page_get_gen(page_id, block->zip_size(), RW_NO_LATCH, nullptr, BUF_GET, + mtr, &err, !--height && !index()->is_clust()); - if (UNIV_UNLIKELY(height == 0 && dict_index_is_ibuf(index))) { - /* We're doing a search on an ibuf tree and we're one - level above the leaf page. */ + if (!block) + { + if (err == DB_DECRYPTION_FAILED) + btr_decryption_failed(*index()); + goto func_exit; + } - ut_ad(level == 0); + if (!!page_is_comp(block->page.frame) != index()->table->not_redundant() || + btr_page_get_index_id(block->page.frame) != index()->id || + fil_page_get_type(block->page.frame) == FIL_PAGE_RTREE || + !fil_page_index_page_check(block->page.frame)) + goto corrupted; - buf_mode = BUF_GET; - rw_latch = RW_NO_LATCH; - goto retry_page_get; - } + if (height != btr_page_get_level(block->page.frame)) + goto corrupted; - if (dict_index_is_spatial(index) - && page_mode >= PAGE_CUR_CONTAIN - && page_mode != PAGE_CUR_RTREE_INSERT) { - ut_ad(need_path); - rtr_node_path_t* path = - cursor->rtr_info->path; - - if (!path->empty() && found) { - ut_ad(path->back().page_no - == page_id.page_no()); - path->pop_back(); -#ifdef UNIV_DEBUG - if (page_mode == PAGE_CUR_RTREE_LOCATE - && (latch_mode != BTR_MODIFY_LEAF)) { - btr_pcur_t* cur - = cursor->rtr_info->parent_path->back( - ).cursor; - rec_t* my_node_ptr - = btr_pcur_get_rec(cur); - - offsets = rec_get_offsets( - my_node_ptr, index, offsets, - 0, ULINT_UNDEFINED, &heap); - - ulint my_page_no - = btr_node_ptr_get_child_page_no( - my_node_ptr, offsets); - - ut_ad(page_id.page_no() == my_page_no); - } + if (page_has_prev(block->page.frame) && + !btr_block_get(*index(), btr_page_get_prev(block->page.frame), + RW_X_LATCH, false, mtr, &err)) + goto func_exit; + mtr->x_latch_at_savepoint(block_savepoint, block); +#ifdef BTR_CUR_HASH_ADAPT + btr_search_drop_page_hash_index(block, true); #endif - } - } - - goto search_loop; - } else if (!dict_index_is_spatial(index) - && latch_mode == BTR_MODIFY_TREE - && lock_intention == BTR_INTENTION_INSERT - && page_has_next(page) - && page_rec_is_last(page_cur_get_rec(page_cursor), page)) { - - /* btr_insert_into_right_sibling() might cause - deleting node_ptr at upper level */ - - guess = NULL; - - if (height == 0) { - /* release the leaf pages if latched */ - for (uint i = 0; i < 3; i++) { - if (latch_leaves.blocks[i] != NULL) { - mtr_release_block_at_savepoint( - mtr, latch_leaves.savepoints[i], - latch_leaves.blocks[i]); - latch_leaves.blocks[i] = NULL; - } - } - } - - goto need_opposite_intention; - } +#ifdef UNIV_ZIP_DEBUG + const page_zip_des_t *page_zip= buf_block_get_page_zip(block); + ut_a(!page_zip || page_zip_validate(page_zip, page, index())); +#endif /* UNIV_ZIP_DEBUG */ + if (page_has_next(block->page.frame) && + !btr_block_get(*index(), btr_page_get_next(block->page.frame), + RW_X_LATCH, false, mtr, &err)) + goto func_exit; + goto search_loop; +} - if (level != 0) { - ut_ad(!autoinc); +/********************************************************************//** +Searches an index tree and positions a tree cursor on a given non-leaf level. +NOTE: n_fields_cmp in tuple must be set so that it cannot be compared +to node pointer page number fields on the upper levels of the tree! +cursor->up_match and cursor->low_match both will have sensible values. +Cursor is left at the place where an insert of the +search tuple should be performed in the B-tree. InnoDB does an insert +immediately after the cursor. Thus, the cursor may end up on a user record, +or on a page infimum record. +@param level the tree level of search +@param tuple data tuple; NOTE: n_fields_cmp in tuple must be set so that + it cannot get compared to the node ptr page number field! +@param latch RW_S_LATCH or RW_X_LATCH +@param cursor tree cursor; the cursor page is s- or x-latched, but see also + above! +@param mtr mini-transaction +@return DB_SUCCESS on success or error code otherwise */ +TRANSACTIONAL_TARGET +dberr_t btr_cur_search_to_nth_level(ulint level, + const dtuple_t *tuple, + rw_lock_type_t rw_latch, + btr_cur_t *cursor, mtr_t *mtr) +{ + dict_index_t *const index= cursor->index(); - if (upper_rw_latch == RW_NO_LATCH) { - ut_ad(latch_mode == BTR_CONT_MODIFY_TREE - || latch_mode == BTR_CONT_SEARCH_TREE); - btr_block_get( - *index, page_id.page_no(), - latch_mode == BTR_CONT_MODIFY_TREE - ? RW_X_LATCH : RW_SX_LATCH, false, mtr, &err); - } else { - ut_ad(mtr->memo_contains_flagged(block, - upper_rw_latch)); - - if (latch_by_caller) { - ut_ad(latch_mode == BTR_SEARCH_TREE); - /* to exclude modifying tree operations - should sx-latch the index. */ - ut_ad(mtr->memo_contains(index->lock, - MTR_MEMO_SX_LOCK)); - /* because has sx-latch of index, - can release upper blocks. */ - for (; n_releases < n_blocks; n_releases++) { - mtr_release_block_at_savepoint( - mtr, - tree_savepoints[n_releases], - tree_blocks[n_releases]); - } - } - } + ut_ad(index->is_btree() || index->is_ibuf()); + mem_heap_t *heap= nullptr; + rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; + rec_offs *offsets= offsets_; + rec_offs_init(offsets_); + ut_ad(level); + ut_ad(dict_index_check_search_tuple(index, tuple)); + ut_ad(index->is_ibuf() ? ibuf_inside(mtr) : index->is_btree()); + ut_ad(dtuple_check_typed(tuple)); + ut_ad(index->page != FIL_NULL); + + MEM_UNDEFINED(&cursor->up_bytes, sizeof cursor->up_bytes); + MEM_UNDEFINED(&cursor->low_bytes, sizeof cursor->low_bytes); + cursor->up_match= 0; + cursor->low_match= 0; + cursor->flag= BTR_CUR_BINARY; - if (page_mode <= PAGE_CUR_LE) { - cursor->low_match = low_match; - cursor->up_match = up_match; - } - } else { - cursor->low_match = low_match; - cursor->low_bytes = low_bytes; - cursor->up_match = up_match; - cursor->up_bytes = up_bytes; +#ifndef BTR_CUR_ADAPT + buf_block_t *block= nullptr; +#else + btr_search_t *info= btr_search_get_info(index); + buf_block_t *block= info->root_guess; +#endif /* BTR_CUR_ADAPT */ - if (autoinc) { - page_set_autoinc(tree_blocks[0], autoinc, mtr, false); - } + ut_ad(mtr->memo_contains_flagged(&index->lock, + MTR_MEMO_X_LOCK | MTR_MEMO_SX_LOCK)); -#ifdef BTR_CUR_HASH_ADAPT - /* We do a dirty read of btr_search_enabled here. We - will properly check btr_search_enabled again in - btr_search_build_page_hash_index() before building a - page hash index, while holding search latch. */ - if (!btr_search_enabled) { - } else if (tuple->info_bits & REC_INFO_MIN_REC_FLAG) { - /* This may be a search tuple for - btr_pcur_t::restore_position(). */ - ut_ad(tuple->is_metadata() - || (tuple->is_metadata(tuple->info_bits - ^ REC_STATUS_INSTANT))); - } else if (index->is_spatial()) { - } else if (index->table->is_temporary()) { - } else if (rec_is_metadata(btr_cur_get_rec(cursor), *index)) { - /* Only user records belong in the adaptive - hash index. */ - } else { - btr_search_info_update(index, cursor); - } -#endif /* BTR_CUR_HASH_ADAPT */ - ut_ad(cursor->up_match != ULINT_UNDEFINED - || mode != PAGE_CUR_GE); - ut_ad(cursor->up_match != ULINT_UNDEFINED - || mode != PAGE_CUR_LE); - ut_ad(cursor->low_match != ULINT_UNDEFINED - || mode != PAGE_CUR_LE); - } - - /* For spatial index, remember what blocks are still latched */ - if (dict_index_is_spatial(index) - && (latch_mode == BTR_MODIFY_TREE - || latch_mode == BTR_MODIFY_LEAF)) { - for (ulint i = 0; i < n_releases; i++) { - cursor->rtr_info->tree_blocks[i] = NULL; - cursor->rtr_info->tree_savepoints[i] = 0; - } + const ulint zip_size= index->table->space->zip_size(); - for (ulint i = n_releases; i <= n_blocks; i++) { - cursor->rtr_info->tree_blocks[i] = tree_blocks[i]; - cursor->rtr_info->tree_savepoints[i] = tree_savepoints[i]; - } - } + /* Start with the root page. */ + page_id_t page_id(index->table->space_id, index->page); + ulint height= ULINT_UNDEFINED; -func_exit: +search_loop: + dberr_t err= DB_SUCCESS; + if (buf_block_t *b= + mtr->get_already_latched(page_id, mtr_memo_type_t(rw_latch))) + block= b; + else if (!(block= buf_page_get_gen(page_id, zip_size, rw_latch, + block, BUF_GET, mtr, &err))) + { + if (err == DB_DECRYPTION_FAILED) + btr_decryption_failed(*index); + goto func_exit; + } - if (UNIV_LIKELY_NULL(heap)) { - mem_heap_free(heap); - } +#ifdef UNIV_ZIP_DEBUG + if (const page_zip_des_t *page_zip= buf_block_get_page_zip(block)) + ut_a(page_zip_validate(page_zip, block->page.frame, index)); +#endif /* UNIV_ZIP_DEBUG */ - ut_free(prev_tree_blocks); - ut_free(prev_tree_savepoints); + if (!!page_is_comp(block->page.frame) != index->table->not_redundant() || + btr_page_get_index_id(block->page.frame) != index->id || + fil_page_get_type(block->page.frame) == FIL_PAGE_RTREE || + !fil_page_index_page_check(block->page.frame)) + { + corrupted: + err= DB_CORRUPTION; + func_exit: + if (UNIV_LIKELY_NULL(heap)) + mem_heap_free(heap); + return err; + } - if (mbr_adj) { - /* remember that we will need to adjust parent MBR */ - cursor->rtr_info->mbr_adj = true; - } + const uint32_t page_level= btr_page_get_level(block->page.frame); - DBUG_RETURN(err); + if (height == ULINT_UNDEFINED) + { + /* We are in the root node */ + height= page_level; + if (!height) + goto corrupted; + cursor->tree_height= height + 1; + } + else if (height != ulint{page_level}) + goto corrupted; + + cursor->page_cur.block= block; + + /* Search for complete index fields. */ + if (page_cur_search_with_match(tuple, PAGE_CUR_LE, &cursor->up_match, + &cursor->low_match, &cursor->page_cur, + nullptr)) + goto corrupted; + + /* If this is the desired level, leave the loop */ + if (level == height) + goto func_exit; + + ut_ad(height > level); + height--; + + offsets = rec_get_offsets(cursor->page_cur.rec, index, offsets, 0, + ULINT_UNDEFINED, &heap); + /* Go to the child node */ + page_id.set_page_no(btr_node_ptr_get_child_page_no(cursor->page_cur.rec, + offsets)); + block= nullptr; + goto search_loop; } dberr_t btr_cur_t::open_leaf(bool first, dict_index_t *index, btr_latch_mode latch_mode, mtr_t *mtr) { - ulint node_ptr_max_size= srv_page_size / 2; btr_intention_t lock_intention; ulint n_blocks= 0; mem_heap_t *heap= nullptr; @@ -2424,29 +1860,21 @@ dberr_t btr_cur_t::open_leaf(bool first, dict_index_t *index, rec_offs_init(offsets_); const bool latch_by_caller= latch_mode & BTR_ALREADY_S_LATCHED; - latch_mode = btr_latch_mode(latch_mode & ~BTR_ALREADY_S_LATCHED); + latch_mode= btr_latch_mode(latch_mode & ~BTR_ALREADY_S_LATCHED); lock_intention= btr_cur_get_and_clear_intention(&latch_mode); - /* This function doesn't need to lock left page of the leaf page */ - if (latch_mode == BTR_SEARCH_PREV) - latch_mode= BTR_SEARCH_LEAF; - else if (latch_mode == BTR_MODIFY_PREV) - latch_mode= BTR_MODIFY_LEAF; - /* Store the position of the tree latch we push to mtr so that we know how to release it when we have latched the leaf node */ auto savepoint= mtr->get_savepoint(); rw_lock_type_t upper_rw_latch= RW_X_LATCH; + ulint node_ptr_max_size= 0; - switch (latch_mode) { - case BTR_CONT_MODIFY_TREE: - case BTR_CONT_SEARCH_TREE: - abort(); - break; - case BTR_MODIFY_TREE: + if (latch_mode == BTR_MODIFY_TREE) + { + node_ptr_max_size= btr_node_ptr_max_size(index); /* Most of delete-intended operations are purging. Free blocks and read IO bandwidth should be prioritized for them, when the history list is growing huge. */ @@ -2457,32 +1885,35 @@ dberr_t btr_cur_t::open_leaf(bool first, dict_index_t *index, mtr_x_lock_index(index, mtr); else mtr_sx_lock_index(index, mtr); - break; - default: + } + else + { + static_assert(int{BTR_CONT_MODIFY_TREE} == (12 | BTR_MODIFY_LEAF), ""); + ut_ad(!(latch_mode & 8)); + /* This function doesn't need to lock left page of the leaf page */ + static_assert(int{BTR_SEARCH_PREV} == (4 | BTR_SEARCH_LEAF), ""); + static_assert(int{BTR_MODIFY_PREV} == (4 | BTR_MODIFY_LEAF), ""); + latch_mode= btr_latch_mode(latch_mode & ~4); ut_ad(!latch_by_caller || mtr->memo_contains_flagged(&index->lock, MTR_MEMO_SX_LOCK | MTR_MEMO_S_LOCK)); upper_rw_latch= RW_S_LATCH; - if (latch_by_caller) - break; - ut_ad(latch_mode != BTR_SEARCH_TREE); - savepoint++; - mtr_s_lock_index(index, mtr); + if (!latch_by_caller) + { + savepoint++; + mtr_s_lock_index(index, mtr); + } } ut_ad(savepoint == mtr->get_savepoint()); - const rw_lock_type_t root_leaf_rw_latch= - btr_cur_latch_for_root_leaf(latch_mode); + const rw_lock_type_t root_leaf_rw_latch= rw_lock_type_t(latch_mode & ~12); page_cur.index = index; uint32_t page= index->page; const auto zip_size= index->table->space->zip_size(); - if (root_leaf_rw_latch == RW_X_LATCH) - node_ptr_max_size= btr_node_ptr_max_size(index); - for (ulint height= ULINT_UNDEFINED;;) { ut_ad(n_blocks < BTR_MAX_LEVELS); @@ -2531,20 +1962,15 @@ dberr_t btr_cur_t::open_leaf(bool first, dict_index_t *index, reached_leaf: const auto leaf_savepoint= mtr->get_savepoint(); ut_ad(leaf_savepoint); + ut_ad(block == mtr->at_savepoint(leaf_savepoint - 1)); if (rw_latch == RW_NO_LATCH) - btr_cur_latch_leaves(block, latch_mode, this, mtr); + btr_cur_latch_leaves(leaf_savepoint - 1, latch_mode, this, mtr); - switch (latch_mode) { - case BTR_MODIFY_TREE: - case BTR_CONT_MODIFY_TREE: - case BTR_CONT_SEARCH_TREE: - break; - default: + if (latch_mode != BTR_MODIFY_TREE) /* Release index->lock if needed, and the non-leaf pages. */ mtr->rollback_to_savepoint(savepoint - !latch_by_caller, leaf_savepoint - 1); - } break; } } @@ -4669,16 +4095,15 @@ btr_cur_pessimistic_update( } } - if (!srv_read_only_mode - && !big_rec_vec +#if 0 // FIXME: this used to be a no-op, and will cause trouble if enabled + if (!big_rec_vec && page_is_leaf(block->page.frame) && !dict_index_is_online_ddl(index)) { -#if 0 // FIXME: this used to be a no-op, and will cause trouble if enabled mtr->release(index->lock); -#endif /* NOTE: We cannot release root block latch here, because it has segment header and already modified in most of cases.*/ } +#endif err = DB_SUCCESS; goto return_after_reservations; @@ -5420,15 +4845,14 @@ return_after_reservations: err_exit: mem_heap_free(heap); - if (!srv_read_only_mode - && page_is_leaf(page) - && !dict_index_is_online_ddl(index)) { #if 0 // FIXME: this used to be a no-op, and will cause trouble if enabled + if (page_is_leaf(page) + && !dict_index_is_online_ddl(index)) { mtr->release(index->lock); -#endif /* NOTE: We cannot release root block latch here, because it has segment header and already modified in most of cases.*/ } +#endif index->table->space->release_free_extents(n_reserved); return(ret); @@ -5545,16 +4969,18 @@ public: buf_block_t *parent_block= m_block; ulint parent_savepoint= m_savepoint; - m_savepoint= mtr_set_savepoint(&mtr); m_block= btr_block_get(*index(), m_page_id.page_no(), RW_S_LATCH, !level, &mtr, nullptr); + if (!m_block) + return false; if (parent_block && parent_block != right_parent) - mtr_release_block_at_savepoint(&mtr, parent_savepoint, parent_block); + mtr.rollback_to_savepoint(parent_savepoint, parent_savepoint + 1); - return m_block && - (level == ULINT_UNDEFINED || - btr_page_get_level(buf_block_get_frame(m_block)) == level); + m_savepoint= mtr.get_savepoint() - 1; + + return level == ULINT_UNDEFINED || + btr_page_get_level(m_block->page.frame) == level; } /** Sets page mode for leaves */ @@ -5761,14 +5187,18 @@ static ha_rows btr_estimate_n_rows_in_range_on_level( buf_block_t *prev_block= block; ulint prev_savepoint= savepoint; - savepoint= mtr_set_savepoint(&mtr); + savepoint= mtr.get_savepoint(); /* Fetch the page. */ block= btr_block_get(*index, page_id.page_no(), RW_S_LATCH, !level, &mtr, nullptr); if (prev_block) - mtr_release_block_at_savepoint(&mtr, prev_savepoint, prev_block); + { + mtr.rollback_to_savepoint(prev_savepoint, prev_savepoint + 1); + if (block) + savepoint--; + } if (!block || btr_page_get_level(buf_block_get_frame(block)) != level) goto inexact; @@ -5797,14 +5227,20 @@ static ha_rows btr_estimate_n_rows_in_range_on_level( } while (page_id.page_no() != right_page_no); if (block) - mtr_release_block_at_savepoint(&mtr, savepoint, block); + { + ut_ad(block == mtr.at_savepoint(savepoint)); + mtr.rollback_to_savepoint(savepoint, savepoint + 1); + } return (n_rows); inexact: if (block) - mtr_release_block_at_savepoint(&mtr, savepoint, block); + { + ut_ad(block == mtr.at_savepoint(savepoint)); + mtr.rollback_to_savepoint(savepoint, savepoint + 1); + } is_n_rows_exact= false; @@ -5863,9 +5299,7 @@ ha_rows btr_estimate_n_rows_in_range(dict_index_t *index, mtr.start(); - /* Store the position of the tree latch we push to mtr so that we - know how to release it when we have latched leaf node(s) */ - ulint savepoint= mtr_set_savepoint(&mtr); + ut_ad(mtr.get_savepoint() == 0); mtr_s_lock_index(index, &mtr); ha_rows table_n_rows= dict_table_get_n_rows(index->table); @@ -5920,10 +5354,10 @@ search_loop: } if (height == 0) - /* There is no need to unlach non-leaf pages here as they must already be + /* There is no need to release non-leaf pages here as they must already be unlatched in btr_est_cur_t::fetch_child(). Try to search on pages after - index->lock unlatching to decrease contention. */ - mtr_release_s_latch_at_savepoint(&mtr, savepoint, &index->lock); + releasing the index latch, to decrease contention. */ + mtr.rollback_to_savepoint(0, 1); /* There is no need to search on left page if divergence_height != ULINT_UNDEFINED, as it was already searched before diff --git a/storage/innobase/btr/btr0defragment.cc b/storage/innobase/btr/btr0defragment.cc index 76b173359da..4e0a7d1f86a 100644 --- a/storage/innobase/btr/btr0defragment.cc +++ b/storage/innobase/btr/btr0defragment.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (C) 2012, 2014 Facebook, Inc. All Rights Reserved. -Copyright (C) 2014, 2022, MariaDB Corporation. +Copyright (C) 2014, 2023, 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 @@ -280,6 +280,70 @@ btr_defragment_calc_n_recs_for_size( return n_recs; } +MY_ATTRIBUTE((nonnull(2,3,4), warn_unused_result)) +/************************************************************//** +Returns the upper level node pointer to a page. It is assumed that mtr holds +an sx-latch on the tree. +@return rec_get_offsets() of the node pointer record */ +static +rec_offs* +btr_page_search_father_node_ptr( + rec_offs* offsets,/*!< in: work area for the return value */ + mem_heap_t* heap, /*!< in: memory heap to use */ + btr_cur_t* cursor, /*!< in: cursor pointing to user record, + out: cursor on node pointer record, + its page x-latched */ + mtr_t* mtr) /*!< in: mtr */ +{ + const uint32_t page_no = btr_cur_get_block(cursor)->page.id().page_no(); + dict_index_t* index = btr_cur_get_index(cursor); + ut_ad(!index->is_spatial()); + + ut_ad(mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK + | MTR_MEMO_SX_LOCK)); + ut_ad(dict_index_get_page(index) != page_no); + + const auto level = btr_page_get_level(btr_cur_get_page(cursor)); + + const rec_t* user_rec = btr_cur_get_rec(cursor); + ut_a(page_rec_is_user_rec(user_rec)); + + if (btr_cur_search_to_nth_level(level + 1, + dict_index_build_node_ptr(index, + user_rec, 0, + heap, level), + RW_X_LATCH, + cursor, mtr) != DB_SUCCESS) { + return nullptr; + } + + const rec_t* node_ptr = btr_cur_get_rec(cursor); + ut_ad(!btr_cur_get_block(cursor)->page.lock.not_recursive() + || mtr->memo_contains(index->lock, MTR_MEMO_X_LOCK)); + + offsets = rec_get_offsets(node_ptr, index, offsets, 0, + ULINT_UNDEFINED, &heap); + + if (btr_node_ptr_get_child_page_no(node_ptr, offsets) != page_no) { + offsets = nullptr; + } + + return(offsets); +} + +static bool btr_page_search_father(mtr_t *mtr, btr_cur_t *cursor) +{ + rec_t *rec= + page_rec_get_next(page_get_infimum_rec(cursor->block()->page.frame)); + if (UNIV_UNLIKELY(!rec)) + return false; + cursor->page_cur.rec= rec; + mem_heap_t *heap= mem_heap_create(100); + const bool got= btr_page_search_father_node_ptr(nullptr, heap, cursor, mtr); + mem_heap_free(heap); + return got; +} + /*************************************************************//** Merge as many records from the from_block to the to_block. Delete the from_block if all records are successfully merged to to_block. @@ -408,7 +472,7 @@ btr_defragment_merge_pages( parent.page_cur.index = index; parent.page_cur.block = from_block; - if (!btr_page_get_father(mtr, &parent)) { + if (!btr_page_search_father(mtr, &parent)) { to_block = nullptr; } else if (n_recs_to_move == n_recs) { /* The whole page is merged with the previous page, diff --git a/storage/innobase/btr/btr0pcur.cc b/storage/innobase/btr/btr0pcur.cc index d731bcbb893..68699ede469 100644 --- a/storage/innobase/btr/btr0pcur.cc +++ b/storage/innobase/btr/btr0pcur.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2022, MariaDB Corporation. +Copyright (c) 2016, 2023, 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 @@ -212,24 +212,100 @@ btr_pcur_copy_stored_position( pcur_receive->old_n_fields = pcur_donate->old_n_fields; } +/** Optimistically latches the leaf page or pages requested. +@param[in] block guessed buffer block +@param[in,out] latch_mode BTR_SEARCH_LEAF, ... +@param[in,out] pcur cursor +@param[in,out] mtr mini-transaction +@return true if success */ +TRANSACTIONAL_TARGET +static bool btr_pcur_optimistic_latch_leaves(buf_block_t *block, + btr_pcur_t *pcur, + btr_latch_mode *latch_mode, + mtr_t *mtr) +{ + ut_ad(block->page.buf_fix_count()); + ut_ad(block->page.in_file()); + ut_ad(block->page.frame); + + static_assert(BTR_SEARCH_PREV & BTR_SEARCH_LEAF, ""); + static_assert(BTR_MODIFY_PREV & BTR_MODIFY_LEAF, ""); + static_assert((BTR_SEARCH_PREV ^ BTR_MODIFY_PREV) == + (RW_S_LATCH ^ RW_X_LATCH), ""); + + const rw_lock_type_t mode= + rw_lock_type_t(*latch_mode & (RW_X_LATCH | RW_S_LATCH)); + + switch (*latch_mode) { + default: + ut_ad(*latch_mode == BTR_SEARCH_LEAF || *latch_mode == BTR_MODIFY_LEAF); + return buf_page_optimistic_get(mode, block, pcur->modify_clock, mtr); + case BTR_SEARCH_PREV: /* btr_pcur_move_backward_from_page() */ + case BTR_MODIFY_PREV: /* Ditto, or ibuf_insert() */ + page_id_t id{0}; + uint32_t left_page_no; + ulint zip_size; + { + transactional_shared_lock_guard g{block->page.lock}; + if (block->modify_clock != pcur->modify_clock) + return false; + id= block->page.id(); + zip_size= block->zip_size(); + left_page_no= btr_page_get_prev(block->page.frame); + } + + if (left_page_no != FIL_NULL) + { + pcur->btr_cur.left_block= + buf_page_get_gen(page_id_t(id.space(), left_page_no), zip_size, + mode, nullptr, BUF_GET_POSSIBLY_FREED, mtr); + + if (pcur->btr_cur.left_block && + btr_page_get_next(pcur->btr_cur.left_block->page.frame) != + id.page_no()) + { +release_left_block: + mtr->release_last_page(); + return false; + } + } + else + pcur->btr_cur.left_block= nullptr; + + if (buf_page_optimistic_get(mode, block, pcur->modify_clock, mtr)) + { + if (btr_page_get_prev(block->page.frame) == left_page_no) + { + /* block was already buffer-fixed while entering the function and + buf_page_optimistic_get() buffer-fixes it again. */ + ut_ad(2 <= block->page.buf_fix_count()); + *latch_mode= btr_latch_mode(mode); + return true; + } + + mtr->release_last_page(); + } + + ut_ad(block->page.buf_fix_count()); + if (pcur->btr_cur.left_block) + goto release_left_block; + return false; + } +} + /** Structure acts as functor to do the latching of leaf pages. It returns true if latching of leaf pages succeeded and false otherwise. */ struct optimistic_latch_leaves { btr_pcur_t *const cursor; - btr_latch_mode *latch_mode; + btr_latch_mode *const latch_mode; mtr_t *const mtr; - optimistic_latch_leaves(btr_pcur_t *cursor, btr_latch_mode *latch_mode, - mtr_t *mtr) - : cursor(cursor), latch_mode(latch_mode), mtr(mtr) {} - bool operator() (buf_block_t *hint) const { - return hint && btr_cur_optimistic_latch_leaves( - hint, cursor->modify_clock, latch_mode, - btr_pcur_get_btr_cur(cursor), mtr); + return hint && + btr_pcur_optimistic_latch_leaves(hint, cursor, latch_mode, mtr); } }; @@ -303,8 +379,8 @@ btr_pcur_t::restore_position(btr_latch_mode restore_latch_mode, mtr_t *mtr) /* Try optimistic restoration. */ if (block_when_stored.run_with_hint( - optimistic_latch_leaves(this, &restore_latch_mode, - mtr))) { + optimistic_latch_leaves{this, &restore_latch_mode, + mtr})) { pos_state = BTR_PCUR_IS_POSITIONED; latch_mode = restore_latch_mode; @@ -465,18 +541,9 @@ btr_pcur_move_to_next_page( return DB_CORRUPTION; } - ulint mode = cursor->latch_mode; - switch (mode) { - case BTR_SEARCH_TREE: - mode = BTR_SEARCH_LEAF; - break; - case BTR_MODIFY_TREE: - mode = BTR_MODIFY_LEAF; - } - dberr_t err; buf_block_t* next_block = btr_block_get( - *cursor->index(), next_page_no, mode, + *cursor->index(), next_page_no, cursor->latch_mode & ~12, page_is_leaf(page), mtr, &err); if (UNIV_UNLIKELY(!next_block)) { diff --git a/storage/innobase/btr/btr0sea.cc b/storage/innobase/btr/btr0sea.cc index fc890f9233b..a1609248512 100644 --- a/storage/innobase/btr/btr0sea.cc +++ b/storage/innobase/btr/btr0sea.cc @@ -1055,26 +1055,24 @@ btr_search_guess_on_hash( index_id_t index_id; ut_ad(mtr->is_active()); + ut_ad(index->is_btree() || index->is_ibuf()); - if (!btr_search_enabled) { + /* Note that, for efficiency, the struct info may not be protected by + any latch here! */ + + if (latch_mode > BTR_MODIFY_LEAF + || !info->last_hash_succ || !info->n_hash_potential + || (tuple->info_bits & REC_INFO_MIN_REC_FLAG)) { return false; } - ut_ad(!index->is_ibuf()); + ut_ad(index->is_btree()); + ut_ad(!index->table->is_temporary()); + ut_ad(latch_mode == BTR_SEARCH_LEAF || latch_mode == BTR_MODIFY_LEAF); compile_time_assert(ulint{BTR_SEARCH_LEAF} == ulint{RW_S_LATCH}); compile_time_assert(ulint{BTR_MODIFY_LEAF} == ulint{RW_X_LATCH}); - /* Not supported for spatial index */ - ut_ad(!dict_index_is_spatial(index)); - - /* Note that, for efficiency, the struct info may not be protected by - any latch here! */ - - if (info->n_hash_potential == 0) { - return false; - } - cursor->n_fields = info->n_fields; cursor->n_bytes = info->n_bytes; diff --git a/storage/innobase/dict/dict0crea.cc b/storage/innobase/dict/dict0crea.cc index daf3bc9a664..614048b7ba0 100644 --- a/storage/innobase/dict/dict0crea.cc +++ b/storage/innobase/dict/dict0crea.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2022, MariaDB Corporation. +Copyright (c) 2017, 2023, 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 @@ -722,7 +722,7 @@ dict_build_field_def_step( } /***************************************************************//** -Creates an index tree for the index if it is not a member of a cluster. +Creates an index tree for the index. @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ static MY_ATTRIBUTE((nonnull, warn_unused_result)) dberr_t @@ -755,9 +755,8 @@ dict_create_index_tree_step( pcur.btr_cur.page_cur.index = UT_LIST_GET_FIRST(dict_sys.sys_indexes->indexes); - dberr_t err = - btr_pcur_open(search_tuple, PAGE_CUR_L, BTR_MODIFY_LEAF, - &pcur, 0, &mtr); + dberr_t err = btr_pcur_open(search_tuple, PAGE_CUR_L, BTR_MODIFY_LEAF, + &pcur, &mtr); if (err != DB_SUCCESS) { func_exit: @@ -768,10 +767,25 @@ func_exit: btr_pcur_move_to_next_user_rec(&pcur, &mtr); if (UNIV_UNLIKELY(btr_pcur_is_after_last_on_page(&pcur))) { +corrupted: err = DB_CORRUPTION; goto func_exit; } + ulint len; + byte* data = rec_get_nth_field_old(btr_pcur_get_rec(&pcur), + DICT_FLD__SYS_INDEXES__ID, + &len); + if (UNIV_UNLIKELY(len != 8 || mach_read_from_8(data) != index->id)) { + goto corrupted; + } + + data = rec_get_nth_field_old(btr_pcur_get_rec(&pcur), + DICT_FLD__SYS_INDEXES__PAGE_NO, &len); + if (len != 4) { + goto corrupted; + } + if (index->is_readable()) { index->set_modified(mtr); @@ -784,11 +798,6 @@ func_exit: err = DB_OUT_OF_FILE_SPACE; ); } - ulint len; - byte* data = rec_get_nth_field_old(btr_pcur_get_rec(&pcur), - DICT_FLD__SYS_INDEXES__PAGE_NO, - &len); - ut_ad(len == 4); mtr.write<4,mtr_t::MAYBE_NOP>(*btr_pcur_get_block(&pcur), data, node->page_no); goto func_exit; diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index 21efb525fa8..53d1031d270 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -2,7 +2,7 @@ Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2013, 2022, MariaDB Corporation. +Copyright (c) 2013, 2023, 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 @@ -4143,8 +4143,7 @@ void dict_set_corrupted(dict_index_t *index, const char *ctx) dict_index_copy_types(tuple, sys_index, 2); cursor.page_cur.index = sys_index; - if (btr_cur_search_to_nth_level(0, tuple, PAGE_CUR_LE, - BTR_MODIFY_LEAF, &cursor, &mtr) + if (cursor.search_leaf(tuple, PAGE_CUR_LE, BTR_MODIFY_LEAF, &mtr) != DB_SUCCESS) { goto fail; } @@ -4219,8 +4218,7 @@ dict_index_set_merge_threshold( dict_index_copy_types(tuple, sys_index, 2); cursor.page_cur.index = sys_index; - if (btr_cur_search_to_nth_level(0, tuple, PAGE_CUR_GE, - BTR_MODIFY_LEAF, &cursor, &mtr) + if (cursor.search_leaf(tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF, &mtr) != DB_SUCCESS) { goto func_exit; } diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc index 004b00615e8..9910a000b5b 100644 --- a/storage/innobase/dict/dict0load.cc +++ b/storage/innobase/dict/dict0load.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2022, MariaDB Corporation. +Copyright (c) 2016, 2023, 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 @@ -1321,7 +1321,7 @@ static dberr_t dict_load_columns(dict_table_t *table, unsigned use_uncommitted, dict_index_copy_types(&tuple, sys_index, 1); pcur.btr_cur.page_cur.index = sys_index; - dberr_t err = btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, + dberr_t err = btr_pcur_open_on_user_rec(&tuple, BTR_SEARCH_LEAF, &pcur, &mtr); if (err != DB_SUCCESS) { goto func_exit; @@ -1452,7 +1452,7 @@ dict_load_virtual_col(dict_table_t *table, bool uncommitted, ulint nth_v_col) dict_index_copy_types(&tuple, sys_virtual_index, 2); pcur.btr_cur.page_cur.index = sys_virtual_index; - dberr_t err = btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, + dberr_t err = btr_pcur_open_on_user_rec(&tuple, BTR_SEARCH_LEAF, &pcur, &mtr); if (err != DB_SUCCESS) { goto func_exit; @@ -1688,8 +1688,7 @@ static dberr_t dict_load_fields(dict_index_t *index, bool uncommitted, dict_index_copy_types(&tuple, sys_index, 1); pcur.btr_cur.page_cur.index = sys_index; - dberr_t error = btr_pcur_open_on_user_rec(&tuple, - PAGE_CUR_GE, BTR_SEARCH_LEAF, + dberr_t error = btr_pcur_open_on_user_rec(&tuple, BTR_SEARCH_LEAF, &pcur, &mtr); if (error != DB_SUCCESS) { goto func_exit; @@ -1947,8 +1946,7 @@ dberr_t dict_load_indexes(dict_table_t *table, bool uncommitted, dict_index_copy_types(&tuple, sys_index, 1); pcur.btr_cur.page_cur.index = sys_index; - dberr_t error = btr_pcur_open_on_user_rec(&tuple, - PAGE_CUR_GE, BTR_SEARCH_LEAF, + dberr_t error = btr_pcur_open_on_user_rec(&tuple, BTR_SEARCH_LEAF, &pcur, &mtr); if (error != DB_SUCCESS) { goto func_exit; @@ -2349,7 +2347,7 @@ static dict_table_t *dict_load_table_one(const span &name, bool uncommitted = false; reload: mtr.start(); - dberr_t err = btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, + dberr_t err = btr_pcur_open_on_user_rec(&tuple, BTR_SEARCH_LEAF, &pcur, &mtr); if (err != DB_SUCCESS || !btr_pcur_is_on_user_rec(&pcur)) { @@ -2607,8 +2605,7 @@ dict_load_table_on_id( dict_table_t* table = nullptr; - if (btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, - BTR_SEARCH_LEAF, &pcur, &mtr) + if (btr_pcur_open_on_user_rec(&tuple, BTR_SEARCH_LEAF, &pcur, &mtr) == DB_SUCCESS && btr_pcur_is_on_user_rec(&pcur)) { /*---------------------------------------------------*/ @@ -2714,7 +2711,7 @@ static dberr_t dict_load_foreign_cols(dict_foreign_t *foreign, trx_id_t trx_id) pcur.btr_cur.page_cur.index = sys_index; mem_heap_t* heap = nullptr; - dberr_t err = btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, + dberr_t err = btr_pcur_open_on_user_rec(&tuple, BTR_SEARCH_LEAF, &pcur, &mtr); if (err != DB_SUCCESS) { goto func_exit; @@ -2891,7 +2888,7 @@ dict_load_foreign( mtr.start(); mem_heap_t* heap = nullptr; - dberr_t err = btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, + dberr_t err = btr_pcur_open_on_user_rec(&tuple, BTR_SEARCH_LEAF, &pcur, &mtr); if (err != DB_SUCCESS) { goto err_exit; @@ -3102,7 +3099,7 @@ start_load: dict_index_copy_types(&tuple, sec_index, 1); pcur.btr_cur.page_cur.index = sec_index; - dberr_t err = btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, + dberr_t err = btr_pcur_open_on_user_rec(&tuple, BTR_SEARCH_LEAF, &pcur, &mtr); if (err != DB_SUCCESS) { DBUG_RETURN(err); diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index 44fcf9f2c18..845f133f1a6 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2009, 2019, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2015, 2022, MariaDB Corporation. +Copyright (c) 2015, 2023, 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 @@ -1697,7 +1697,7 @@ static dberr_t page_cur_open_level(page_cur_t *page_cur, ulint level, static dberr_t btr_pcur_open_level(btr_pcur_t *pcur, ulint level, mtr_t *mtr, dict_index_t *index) { - pcur->latch_mode= BTR_SEARCH_TREE; + pcur->latch_mode= BTR_SEARCH_LEAF; pcur->search_mode= PAGE_CUR_G; pcur->pos_state= BTR_PCUR_IS_POSITIONED; pcur->btr_cur.page_cur.index= index; diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index 12e9a6913ba..e9f3106feb0 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2022, MariaDB Corporation. +Copyright (c) 2017, 2023, 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 @@ -122,15 +122,22 @@ MY_ATTRIBUTE((nonnull, warn_unused_result)) static buf_block_t *fsp_get_header(const fil_space_t *space, mtr_t *mtr, dberr_t *err) { - buf_block_t *block= buf_page_get_gen(page_id_t(space->id, 0), - space->zip_size(), RW_SX_LATCH, - nullptr, BUF_GET_POSSIBLY_FREED, - mtr, err); - if (block && space->id != mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_ID + - block->page.frame)) + const page_id_t id{space->id, 0}; + buf_block_t *block= mtr->get_already_latched(id, MTR_MEMO_PAGE_SX_FIX); + if (block) + *err= DB_SUCCESS; + else { - *err= DB_CORRUPTION; - block= nullptr; + block= buf_page_get_gen(id, space->zip_size(), RW_SX_LATCH, + nullptr, BUF_GET_POSSIBLY_FREED, + mtr, err); + if (block && + space->id != mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_ID + + block->page.frame)) + { + *err= DB_CORRUPTION; + block= nullptr; + } } return block; } diff --git a/storage/innobase/gis/gis0sea.cc b/storage/innobase/gis/gis0sea.cc index 207d49abeba..9eeeb9aeed3 100644 --- a/storage/innobase/gis/gis0sea.cc +++ b/storage/innobase/gis/gis0sea.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2016, 2018, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2022, MariaDB Corporation. +Copyright (c) 2017, 2023, 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 @@ -44,7 +44,6 @@ Created 2014/01/16 Jimmy Yang static bool rtr_cur_restore_position( - ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ btr_cur_t* cursor, /*!< in: detached persistent cursor */ ulint level, /*!< in: index level */ mtr_t* mtr); /*!< in: mtr */ @@ -135,6 +134,7 @@ rtr_pcur_getnext_from_path( && (my_latch_mode | 4) == BTR_CONT_MODIFY_TREE; if (!index_locked) { + ut_ad(mtr->is_empty()); mtr_s_lock_index(index, mtr); } else { ut_ad(mtr->memo_contains_flagged(&index->lock, @@ -154,14 +154,12 @@ rtr_pcur_getnext_from_path( node_seq_t path_ssn; const page_t* page; rw_lock_type_t rw_latch; - ulint tree_idx; mysql_mutex_lock(&rtr_info->rtr_path_mutex); next_rec = rtr_info->path->back(); rtr_info->path->pop_back(); level = next_rec.level; path_ssn = next_rec.seq_no; - tree_idx = btr_cur->tree_height - level - 1; /* Maintain the parent path info as well, if needed */ if (need_parent && !skip_parent && !new_split) { @@ -223,37 +221,15 @@ rtr_pcur_getnext_from_path( rw_latch = RW_X_LATCH; } - /* Release previous locked blocks */ - if (my_latch_mode != BTR_SEARCH_LEAF) { - for (ulint idx = 0; idx < btr_cur->tree_height; - idx++) { - if (rtr_info->tree_blocks[idx]) { - mtr_release_block_at_savepoint( - mtr, - rtr_info->tree_savepoints[idx], - rtr_info->tree_blocks[idx]); - rtr_info->tree_blocks[idx] = NULL; - } - } - for (ulint idx = RTR_MAX_LEVELS; idx < RTR_MAX_LEVELS + 3; - idx++) { - if (rtr_info->tree_blocks[idx]) { - mtr_release_block_at_savepoint( - mtr, - rtr_info->tree_savepoints[idx], - rtr_info->tree_blocks[idx]); - rtr_info->tree_blocks[idx] = NULL; - } - } + if (my_latch_mode == BTR_MODIFY_LEAF) { + mtr->rollback_to_savepoint(1); } - /* set up savepoint to record any locks to be taken */ - rtr_info->tree_savepoints[tree_idx] = mtr_set_savepoint(mtr); - ut_ad((my_latch_mode | 4) == BTR_CONT_MODIFY_TREE || !page_is_leaf(btr_cur_get_page(btr_cur)) || !btr_cur->page_cur.block->page.lock.have_any()); + const auto block_savepoint = mtr->get_savepoint(); block = buf_page_get_gen( page_id_t(index->table->space_id, next_rec.page_no), zip_size, @@ -264,8 +240,6 @@ rtr_pcur_getnext_from_path( break; } - rtr_info->tree_blocks[tree_idx] = block; - page = buf_block_get_frame(block); page_ssn = page_get_ssn_id(page); @@ -396,24 +370,23 @@ rtr_pcur_getnext_from_path( if (found) { if (level == target_level) { - page_cur_t* r_cur;; + ut_ad(block + == mtr->at_savepoint(block_savepoint)); if (my_latch_mode == BTR_MODIFY_TREE && level == 0) { ut_ad(rw_latch == RW_NO_LATCH); btr_cur_latch_leaves( - block, + block_savepoint, BTR_MODIFY_TREE, btr_cur, mtr); } - r_cur = btr_cur_get_page_cur(btr_cur); - page_cur_position( page_cur_get_rec(page_cursor), page_cur_get_block(page_cursor), - r_cur); + btr_cur_get_page_cur(btr_cur)); btr_cur->low_match = level != 0 ? DICT_INDEX_SPATIAL_NODEPTR_SIZE + 1 @@ -425,13 +398,7 @@ rtr_pcur_getnext_from_path( last node just located */ skip_parent = true; } else { - /* Release latch on the current page */ - ut_ad(rtr_info->tree_blocks[tree_idx]); - - mtr_release_block_at_savepoint( - mtr, rtr_info->tree_savepoints[tree_idx], - rtr_info->tree_blocks[tree_idx]); - rtr_info->tree_blocks[tree_idx] = NULL; + mtr->release_last_page(); } } while (!rtr_info->path->empty()); @@ -509,50 +476,524 @@ static void rtr_compare_cursor_rec(const rec_t *rec, dict_index_t *index, } #endif +TRANSACTIONAL_TARGET +dberr_t rtr_search_to_nth_level(ulint level, const dtuple_t *tuple, + page_cur_mode_t mode, + btr_latch_mode latch_mode, + btr_cur_t *cur, mtr_t *mtr) +{ + page_cur_mode_t page_mode; + page_cur_mode_t search_mode= PAGE_CUR_UNSUPP; + + bool mbr_adj= false; + bool found= false; + dict_index_t *const index= cur->index(); + + mem_heap_t *heap= nullptr; + rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; + rec_offs *offsets= offsets_; + rec_offs_init(offsets_); + ut_ad(level == 0 || mode == PAGE_CUR_LE || RTREE_SEARCH_MODE(mode)); + ut_ad(dict_index_check_search_tuple(index, tuple)); + ut_ad(dtuple_check_typed(tuple)); + ut_ad(index->is_spatial()); + ut_ad(index->page != FIL_NULL); + + MEM_UNDEFINED(&cur->up_match, sizeof cur->up_match); + MEM_UNDEFINED(&cur->up_bytes, sizeof cur->up_bytes); + MEM_UNDEFINED(&cur->low_match, sizeof cur->low_match); + MEM_UNDEFINED(&cur->low_bytes, sizeof cur->low_bytes); + ut_d(cur->up_match= ULINT_UNDEFINED); + ut_d(cur->low_match= ULINT_UNDEFINED); + + const bool latch_by_caller= latch_mode & BTR_ALREADY_S_LATCHED; + + ut_ad(!latch_by_caller + || mtr->memo_contains_flagged(&index->lock, MTR_MEMO_S_LOCK + | MTR_MEMO_SX_LOCK)); + latch_mode= BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); + + ut_ad(!latch_by_caller || latch_mode == BTR_SEARCH_LEAF || + latch_mode == BTR_MODIFY_LEAF); + + cur->flag= BTR_CUR_BINARY; + +#ifndef BTR_CUR_ADAPT + buf_block_t *guess= nullptr; +#else + btr_search_t *const info= btr_search_get_info(index); + buf_block_t *guess= info->root_guess; +#endif + + /* Store the position of the tree latch we push to mtr so that we + know how to release it when we have latched leaf node(s) */ + + const ulint savepoint= mtr->get_savepoint(); + + rw_lock_type_t upper_rw_latch, root_leaf_rw_latch= RW_NO_LATCH; + + switch (latch_mode) { + case BTR_MODIFY_TREE: + mtr_x_lock_index(index, mtr); + upper_rw_latch= root_leaf_rw_latch= RW_X_LATCH; + break; + case BTR_CONT_MODIFY_TREE: + ut_ad(mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK | + MTR_MEMO_SX_LOCK)); + upper_rw_latch= RW_X_LATCH; + break; + default: + ut_ad(latch_mode != BTR_MODIFY_PREV); + ut_ad(latch_mode != BTR_SEARCH_PREV); + if (!latch_by_caller) + mtr_s_lock_index(index, mtr); + upper_rw_latch= root_leaf_rw_latch= RW_S_LATCH; + if (latch_mode == BTR_MODIFY_LEAF) + root_leaf_rw_latch= RW_X_LATCH; + } + + auto root_savepoint= mtr->get_savepoint(); + const ulint zip_size= index->table->space->zip_size(); + + /* Start with the root page. */ + page_id_t page_id(index->table->space_id, index->page); + + ulint up_match= 0, up_bytes= 0, low_match= 0, low_bytes= 0; + ulint height= ULINT_UNDEFINED; + + /* We use these modified search modes on non-leaf levels of the + B-tree. These let us end up in the right B-tree leaf. In that leaf + we use the original search mode. */ + + switch (mode) { + case PAGE_CUR_GE: + page_mode= PAGE_CUR_L; + break; + case PAGE_CUR_G: + page_mode= PAGE_CUR_LE; + break; + default: +#ifdef PAGE_CUR_LE_OR_EXTENDS + ut_ad(mode == PAGE_CUR_L || mode == PAGE_CUR_LE + || RTREE_SEARCH_MODE(mode) + || mode == PAGE_CUR_LE_OR_EXTENDS); +#else /* PAGE_CUR_LE_OR_EXTENDS */ + ut_ad(mode == PAGE_CUR_L || mode == PAGE_CUR_LE + || RTREE_SEARCH_MODE(mode)); +#endif /* PAGE_CUR_LE_OR_EXTENDS */ + page_mode= mode; + break; + } + + search_loop: + auto buf_mode= BUF_GET; + ulint rw_latch= RW_NO_LATCH; + + if (height) + { + /* We are about to fetch the root or a non-leaf page. */ + if (latch_mode != BTR_MODIFY_TREE || height == level) + /* If doesn't have SX or X latch of index, + each page should be latched before reading. */ + rw_latch= upper_rw_latch; + } + else if (latch_mode <= BTR_MODIFY_LEAF) + rw_latch= latch_mode; + + dberr_t err; + auto block_savepoint= mtr->get_savepoint(); + buf_block_t *block= buf_page_get_gen(page_id, zip_size, rw_latch, guess, + buf_mode, mtr, &err, false); + if (!block) + { + if (err == DB_DECRYPTION_FAILED) + btr_decryption_failed(*index); + func_exit: + if (UNIV_LIKELY_NULL(heap)) + mem_heap_free(heap); + + if (mbr_adj) + /* remember that we will need to adjust parent MBR */ + cur->rtr_info->mbr_adj= true; + + return err; + } + + const page_t *page= buf_block_get_frame(block); +#ifdef UNIV_ZIP_DEBUG + if (rw_latch != RW_NO_LATCH) { + const page_zip_des_t *page_zip= buf_block_get_page_zip(block); + ut_a(!page_zip || page_zip_validate(page_zip, page, index)); + } +#endif /* UNIV_ZIP_DEBUG */ + + ut_ad(fil_page_index_page_check(page)); + ut_ad(index->id == btr_page_get_index_id(page)); + + if (height != ULINT_UNDEFINED); + else if (page_is_leaf(page) && + rw_latch != RW_NO_LATCH && rw_latch != root_leaf_rw_latch) + { + /* The root page is also a leaf page (root_leaf). + We should reacquire the page, because the root page + is latched differently from leaf pages. */ + ut_ad(root_leaf_rw_latch != RW_NO_LATCH); + ut_ad(rw_latch == RW_S_LATCH || rw_latch == RW_SX_LATCH); + + ut_ad(block == mtr->at_savepoint(block_savepoint)); + mtr->rollback_to_savepoint(block_savepoint); + + upper_rw_latch= root_leaf_rw_latch; + goto search_loop; + } + else + { + /* We are in the root node */ + + height= btr_page_get_level(page); + cur->tree_height= height + 1; + + ut_ad(cur->rtr_info); + + /* If SSN in memory is not initialized, fetch it from root page */ + if (!rtr_get_current_ssn_id(index)) + /* FIXME: do this in dict_load_table_one() */ + index->set_ssn(page_get_ssn_id(page) + 1); + + /* Save the MBR */ + cur->rtr_info->thr= cur->thr; + rtr_get_mbr_from_tuple(tuple, &cur->rtr_info->mbr); + +#ifdef BTR_CUR_ADAPT + info->root_guess= block; +#endif + } + + if (height == 0) { + if (rw_latch == RW_NO_LATCH) + { + ut_ad(block == mtr->at_savepoint(block_savepoint)); + btr_cur_latch_leaves(block_savepoint, latch_mode, cur, mtr); + } + + switch (latch_mode) { + case BTR_MODIFY_TREE: + case BTR_CONT_MODIFY_TREE: + break; + default: + if (!latch_by_caller) + { + /* Release the tree s-latch */ + mtr->rollback_to_savepoint(savepoint, + savepoint + 1); + block_savepoint--; + root_savepoint--; + } + /* release upper blocks */ + if (savepoint < block_savepoint) + mtr->rollback_to_savepoint(savepoint, block_savepoint); + } + + page_mode= mode; + } + + /* Remember the page search mode */ + search_mode= page_mode; + + /* Some adjustment on search mode, when the page search mode is + PAGE_CUR_RTREE_LOCATE or PAGE_CUR_RTREE_INSERT, as we are searching + with MBRs. When it is not the target level, we should search all + sub-trees that "CONTAIN" the search range/MBR. When it is at the + target level, the search becomes PAGE_CUR_LE */ + + if (page_mode == PAGE_CUR_RTREE_INSERT) + { + page_mode= (level == height) + ? PAGE_CUR_LE + : PAGE_CUR_RTREE_INSERT; + + ut_ad(!page_is_leaf(page) || page_mode == PAGE_CUR_LE); + } + else if (page_mode == PAGE_CUR_RTREE_LOCATE && level == height) + page_mode= level == 0 ? PAGE_CUR_LE : PAGE_CUR_RTREE_GET_FATHER; + + up_match= 0; + low_match= 0; + + if (latch_mode == BTR_MODIFY_TREE || latch_mode == BTR_CONT_MODIFY_TREE) + /* Tree are locked, no need for Page Lock to protect the "path" */ + cur->rtr_info->need_page_lock= false; + + cur->page_cur.block= block; + + if (page_mode >= PAGE_CUR_CONTAIN) + { + found= rtr_cur_search_with_match(block, index, tuple, page_mode, + &cur->page_cur, cur->rtr_info); + + /* Need to use BTR_MODIFY_TREE to do the MBR adjustment */ + if (search_mode == PAGE_CUR_RTREE_INSERT && cur->rtr_info->mbr_adj) { + static_assert(BTR_MODIFY_TREE == (8 | BTR_MODIFY_LEAF), ""); + + if (!(latch_mode & 8)) + /* Parent MBR needs updated, should retry with BTR_MODIFY_TREE */ + goto func_exit; + + cur->rtr_info->mbr_adj= false; + mbr_adj= true; + } + + if (found && page_mode == PAGE_CUR_RTREE_GET_FATHER) + cur->low_match= DICT_INDEX_SPATIAL_NODEPTR_SIZE + 1; + } + else + { + /* Search for complete index fields. */ + up_bytes= low_bytes= 0; + if (page_cur_search_with_match(tuple, page_mode, &up_match, + &low_match, &cur->page_cur, nullptr)) { + err= DB_CORRUPTION; + goto func_exit; + } + } + + /* If this is the desired level, leave the loop */ + + ut_ad(height == btr_page_get_level(btr_cur_get_page(cur))); + + /* Add Predicate lock if it is serializable isolation + and only if it is in the search case */ + if (mode >= PAGE_CUR_CONTAIN && mode != PAGE_CUR_RTREE_INSERT && + mode != PAGE_CUR_RTREE_LOCATE && cur->rtr_info->need_prdt_lock) + { + lock_prdt_t prdt; + + { + trx_t* trx= thr_get_trx(cur->thr); + TMLockTrxGuard g{TMLockTrxArgs(*trx)}; + lock_init_prdt_from_mbr(&prdt, &cur->rtr_info->mbr, mode, + trx->lock.lock_heap); + } + + if (rw_latch == RW_NO_LATCH && height != 0) + block->page.lock.s_lock(); + + lock_prdt_lock(block, &prdt, index, LOCK_S, LOCK_PREDICATE, cur->thr); + + if (rw_latch == RW_NO_LATCH && height != 0) + block->page.lock.s_unlock(); + } + + if (level != height) + { + ut_ad(height > 0); + + height--; + guess= nullptr; + + const rec_t *node_ptr= btr_cur_get_rec(cur); + + offsets= rec_get_offsets(node_ptr, index, offsets, 0, + ULINT_UNDEFINED, &heap); + + if (page_rec_is_supremum(node_ptr)) + { + cur->low_match= 0; + cur->up_match= 0; + goto func_exit; + } + + /* If we are doing insertion or record locating, + remember the tree nodes we visited */ + if (page_mode == PAGE_CUR_RTREE_INSERT || + (search_mode == PAGE_CUR_RTREE_LOCATE && + latch_mode != BTR_MODIFY_LEAF)) + { + const bool add_latch= latch_mode == BTR_MODIFY_TREE && + rw_latch == RW_NO_LATCH; + + if (add_latch) + { + ut_ad(mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK | + MTR_MEMO_SX_LOCK)); + block->page.lock.s_lock(); + } + + /* Store the parent cursor location */ + ut_d(auto num_stored=) + rtr_store_parent_path(block, cur, latch_mode, height + 1, mtr); + + if (page_mode == PAGE_CUR_RTREE_INSERT) + { + btr_pcur_t *r_cursor= rtr_get_parent_cursor(cur, height + 1, true); + /* If it is insertion, there should be only one parent for + each level traverse */ + ut_ad(num_stored == 1); + node_ptr= btr_pcur_get_rec(r_cursor); + } + + if (add_latch) + block->page.lock.s_unlock(); + + ut_ad(!page_rec_is_supremum(node_ptr)); + } + + ut_ad(page_mode == search_mode || + (page_mode == PAGE_CUR_WITHIN && + search_mode == PAGE_CUR_RTREE_LOCATE)); + page_mode= search_mode; + + if (height == level && latch_mode == BTR_MODIFY_TREE) + { + ut_ad(upper_rw_latch == RW_X_LATCH); + for (auto i= root_savepoint, n= mtr->get_savepoint(); i < n; i++) + mtr->x_latch_at_savepoint(i, mtr->at_savepoint(i)); + } + + /* Go to the child node */ + page_id.set_page_no(btr_node_ptr_get_child_page_no(node_ptr, offsets)); + + if (page_mode >= PAGE_CUR_CONTAIN && page_mode != PAGE_CUR_RTREE_INSERT) + { + rtr_node_path_t *path= cur->rtr_info->path; + + if (found && !path->empty()) + { + ut_ad(path->back().page_no == page_id.page_no()); + path->pop_back(); +#ifdef UNIV_DEBUG + if (page_mode == PAGE_CUR_RTREE_LOCATE && + latch_mode != BTR_MODIFY_LEAF) + { + btr_pcur_t* pcur= cur->rtr_info->parent_path->back().cursor; + rec_t *my_node_ptr= btr_pcur_get_rec(pcur); + + offsets= rec_get_offsets(my_node_ptr, index, offsets, + 0, ULINT_UNDEFINED, &heap); + + ut_ad(page_id.page_no() == + btr_node_ptr_get_child_page_no(my_node_ptr, offsets)); + } +#endif + } + } + + goto search_loop; + } + + if (level) + { + if (upper_rw_latch == RW_NO_LATCH) + { + ut_ad(latch_mode == BTR_CONT_MODIFY_TREE); + btr_block_get(*index, page_id.page_no(), RW_X_LATCH, false, mtr, &err); + } + else + { + ut_ad(mtr->memo_contains_flagged(block, upper_rw_latch)); + ut_ad(!latch_by_caller); + } + + if (page_mode <= PAGE_CUR_LE) + { + cur->low_match= low_match; + cur->up_match= up_match; + } + } + else + { + cur->low_match= low_match; + cur->low_bytes= low_bytes; + cur->up_match= up_match; + cur->up_bytes= up_bytes; + + ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_GE); + ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); + ut_ad(low_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); + } + + goto func_exit; +} + +dberr_t rtr_search_leaf(btr_cur_t *cur, const dtuple_t *tuple, + btr_latch_mode latch_mode, + mtr_t *mtr, page_cur_mode_t mode) +{ + return rtr_search_to_nth_level(0, tuple, mode, latch_mode, cur, mtr); +} + +/** Search for a spatial index leaf page record. +@param pcur cursor +@param tuple search tuple +@param mode search mode +@param mtr mini-transaction */ +dberr_t rtr_search_leaf(btr_pcur_t *pcur, const dtuple_t *tuple, + page_cur_mode_t mode, mtr_t *mtr) +{ +#ifdef UNIV_DEBUG + switch (mode) { + case PAGE_CUR_CONTAIN: + case PAGE_CUR_INTERSECT: + case PAGE_CUR_WITHIN: + case PAGE_CUR_DISJOINT: + case PAGE_CUR_MBR_EQUAL: + break; + default: + ut_ad("invalid mode" == 0); + } +#endif + pcur->latch_mode= BTR_SEARCH_LEAF; + pcur->search_mode= mode; + pcur->pos_state= BTR_PCUR_IS_POSITIONED; + pcur->trx_if_known= nullptr; + return rtr_search_leaf(&pcur->btr_cur, tuple, BTR_SEARCH_LEAF, mtr, mode); +} + /**************************************************************//** Initializes and opens a persistent cursor to an index tree. It should be -closed with btr_pcur_close. Mainly called by row_search_index_entry() */ -bool -rtr_pcur_open( - dict_index_t* index, /*!< in: index */ +closed with btr_pcur_close. */ +bool rtr_search( const dtuple_t* tuple, /*!< in: tuple on which search done */ - btr_latch_mode latch_mode,/*!< in: BTR_SEARCH_LEAF, ... */ + btr_latch_mode latch_mode,/*!< in: BTR_MODIFY_LEAF, ... */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ mtr_t* mtr) /*!< in: mtr */ { static_assert(BTR_MODIFY_TREE == (8 | BTR_MODIFY_LEAF), ""); ut_ad(latch_mode & BTR_MODIFY_LEAF); + ut_ad(!(latch_mode & BTR_ALREADY_S_LATCHED)); + ut_ad(mtr->is_empty()); /* Initialize the cursor */ btr_pcur_init(cursor); cursor->latch_mode = BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); - cursor->search_mode = PAGE_CUR_RTREE_LOCATE; - cursor->trx_if_known = NULL; + cursor->search_mode = PAGE_CUR_RTREE_LOCATE; + cursor->trx_if_known = nullptr; + + if (latch_mode & 8) { + mtr_x_lock_index(cursor->index(), mtr); + } else { + latch_mode + = btr_latch_mode(latch_mode | BTR_ALREADY_S_LATCHED); + mtr_sx_lock_index(cursor->index(), mtr); + } /* Search with the tree cursor */ btr_cur_t* btr_cursor = btr_pcur_get_btr_cur(cursor); - btr_cursor->page_cur.index = index; - btr_cursor->rtr_info = rtr_create_rtr_info(false, false, - btr_cursor, index); + btr_cursor->rtr_info + = rtr_create_rtr_info(false, false, + btr_cursor, cursor->index()); - /* Purge will SX lock the tree instead of take Page Locks */ if (btr_cursor->thr) { btr_cursor->rtr_info->need_page_lock = true; btr_cursor->rtr_info->thr = btr_cursor->thr; } - if ((latch_mode & 8) && index->lock.have_u_not_x()) { - index->lock.u_x_upgrade(SRW_LOCK_CALL); - mtr->lock_upgrade(index->lock); - } - - if (btr_cur_search_to_nth_level(0, tuple, PAGE_CUR_RTREE_LOCATE, - latch_mode, - btr_cursor, mtr) != DB_SUCCESS) { + if (rtr_search_leaf(btr_cursor, tuple, latch_mode, mtr) + != DB_SUCCESS) { return true; } @@ -560,7 +1001,8 @@ rtr_pcur_open( const rec_t* rec = btr_pcur_get_rec(cursor); - const bool d= rec_get_deleted_flag(rec, index->table->not_redundant()); + const bool d= rec_get_deleted_flag( + rec, cursor->index()->table->not_redundant()); if (page_rec_is_infimum(rec) || btr_pcur_get_low_match(cursor) != dtuple_get_n_fields(tuple) @@ -571,26 +1013,12 @@ rtr_pcur_open( btr_cursor->rtr_info->fd_del = true; btr_cursor->low_match = 0; } - /* Did not find matched row in first dive. Release - latched block if any before search more pages */ - if (!(latch_mode & 8)) { - ulint tree_idx = btr_cursor->tree_height - 1; - rtr_info_t* rtr_info = btr_cursor->rtr_info; - - if (rtr_info->tree_blocks[tree_idx]) { - mtr_release_block_at_savepoint( - mtr, - rtr_info->tree_savepoints[tree_idx], - rtr_info->tree_blocks[tree_idx]); - rtr_info->tree_blocks[tree_idx] = NULL; - } - } + + mtr->rollback_to_savepoint(1); if (!rtr_pcur_getnext_from_path(tuple, PAGE_CUR_RTREE_LOCATE, btr_cursor, 0, latch_mode, - latch_mode - & (8 | BTR_ALREADY_S_LATCHED), - mtr)) { + true, mtr)) { return true; } @@ -598,6 +1026,10 @@ rtr_pcur_open( == dtuple_get_n_fields(tuple)); } + if (!(latch_mode & 8)) { + mtr->rollback_to_savepoint(0, 1); + } + return false; } @@ -641,8 +1073,7 @@ static const rec_t* rtr_get_father_node( if (sea_cur && sea_cur->tree_height > level) { ut_ad(mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK | MTR_MEMO_SX_LOCK)); - if (rtr_cur_restore_position(BTR_CONT_MODIFY_TREE, sea_cur, - level, mtr)) { + if (rtr_cur_restore_position(sea_cur, level, mtr)) { btr_pcur_t* r_cursor = rtr_get_parent_cursor( sea_cur, level, false); @@ -668,9 +1099,8 @@ static const rec_t* rtr_get_father_node( btr_cur->rtr_info = rtr_create_rtr_info(false, false, btr_cur, index); - if (btr_cur_search_to_nth_level(level, tuple, - PAGE_CUR_RTREE_LOCATE, - BTR_CONT_MODIFY_TREE, btr_cur, mtr) + if (rtr_search_to_nth_level(level, tuple, PAGE_CUR_RTREE_LOCATE, + BTR_CONT_MODIFY_TREE, btr_cur, mtr) != DB_SUCCESS) { } else if (sea_cur && sea_cur->tree_height == level) { rec = btr_cur_get_rec(btr_cur); @@ -729,9 +1159,8 @@ rtr_page_get_father_node_ptr( page_no = btr_cur_get_block(cursor)->page.id().page_no(); index = btr_cur_get_index(cursor); - ut_ad(srv_read_only_mode - || mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK - | MTR_MEMO_SX_LOCK)); + ut_ad(mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK + | MTR_MEMO_SX_LOCK)); ut_ad(dict_index_get_page(index) != page_no); @@ -879,32 +1308,10 @@ rtr_init_rtr_info( if (!reinit) { /* Reset all members. */ - rtr_info->path = NULL; - rtr_info->parent_path = NULL; - rtr_info->matches = NULL; - + memset(rtr_info, 0, sizeof *rtr_info); + static_assert(PAGE_CUR_UNSUPP == 0, "compatibility"); mysql_mutex_init(rtr_path_mutex_key, &rtr_info->rtr_path_mutex, nullptr); - - memset(rtr_info->tree_blocks, 0x0, - sizeof(rtr_info->tree_blocks)); - memset(rtr_info->tree_savepoints, 0x0, - sizeof(rtr_info->tree_savepoints)); - rtr_info->mbr.xmin = 0.0; - rtr_info->mbr.xmax = 0.0; - rtr_info->mbr.ymin = 0.0; - rtr_info->mbr.ymax = 0.0; - rtr_info->thr = NULL; - rtr_info->heap = NULL; - rtr_info->cursor = NULL; - rtr_info->index = NULL; - rtr_info->need_prdt_lock = false; - rtr_info->need_page_lock = false; - rtr_info->allocated = false; - rtr_info->mbr_adj = false; - rtr_info->fd_del = false; - rtr_info->search_tuple = NULL; - rtr_info->search_mode = PAGE_CUR_UNSUPP; } ut_ad(!rtr_info->matches || rtr_info->matches->matched_recs->empty()); @@ -1130,7 +1537,6 @@ struct optimistic_get static bool rtr_cur_restore_position( - ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ btr_cur_t* btr_cur, /*!< in: detached persistent cursor */ ulint level, /*!< in: index level */ mtr_t* mtr) /*!< in: mtr */ @@ -1158,8 +1564,6 @@ rtr_cur_restore_position( r_cursor->modify_clock = 100; ); - ut_ad(latch_mode == BTR_CONT_MODIFY_TREE); - if (r_cursor->block_when_stored.run_with_hint( optimistic_get(r_cursor, mtr))) { ut_ad(r_cursor->pos_state == BTR_PCUR_IS_POSITIONED); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 949a9eae0d7..244d535f020 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1549,8 +1549,7 @@ static void innodb_drop_database(handlerton*, char *path) mtr_t mtr; mtr.start(); pcur.btr_cur.page_cur.index = sys_index; - err= btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, - BTR_SEARCH_LEAF, &pcur, &mtr); + err= btr_pcur_open_on_user_rec(&tuple, BTR_SEARCH_LEAF, &pcur, &mtr); if (err != DB_SUCCESS) goto err_exit; diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 7c162b9af6a..8ff36bc4bdd 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -6081,7 +6081,8 @@ func_exit: que_thr_t* thr = pars_complete_graph_for_exec( NULL, trx, ctx->heap, NULL); - const bool is_root = block->page.id().page_no() == index->page; + page_id_t id{block->page.id()}; + const bool is_root = id.page_no() == index->page; if (rec_is_metadata(rec, *index)) { ut_ad(page_rec_is_user_rec(rec)); @@ -6098,8 +6099,10 @@ func_exit: } /* Ensure that the root page is in the correct format. */ - buf_block_t* root = btr_root_block_get(index, RW_X_LATCH, - &mtr, &err); + id.set_page_no(index->page); + buf_block_t* root = mtr.get_already_latched( + id, MTR_MEMO_PAGE_SX_FIX); + if (UNIV_UNLIKELY(!root)) { goto func_exit; } diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc index 5a97ea4ebe0..c3dddf8251d 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.cc +++ b/storage/innobase/ibuf/ibuf0ibuf.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2022, MariaDB Corporation. +Copyright (c) 2016, 2023, 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 @@ -2297,7 +2297,7 @@ loop: btr_pcur_t pcur; pcur.btr_cur.page_cur.index= ibuf.index; ibuf_mtr_start(&mtr); - if (btr_pcur_open(&tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF, &pcur, 0, &mtr)) + if (btr_pcur_open(&tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF, &pcur, &mtr)) goto func_exit; if (!btr_pcur_is_on_user_rec(&pcur)) { @@ -2493,8 +2493,8 @@ ibuf_merge_space( /* Position the cursor on the first matching record. */ pcur.btr_cur.page_cur.index = ibuf.index; - dberr_t err = btr_pcur_open(&tuple, PAGE_CUR_GE, - BTR_SEARCH_LEAF, &pcur, 0, &mtr); + dberr_t err = btr_pcur_open(&tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, + &pcur, &mtr); ut_ad(err != DB_SUCCESS || page_validate(btr_pcur_get_page(&pcur), ibuf.index)); @@ -3238,7 +3238,7 @@ ibuf_insert_low( ibuf_mtr_start(&mtr); pcur.btr_cur.page_cur.index = ibuf.index; - err = btr_pcur_open(ibuf_entry, PAGE_CUR_LE, mode, &pcur, 0, &mtr); + err = btr_pcur_open(ibuf_entry, PAGE_CUR_LE, mode, &pcur, &mtr); if (err != DB_SUCCESS) { func_exit: ibuf_mtr_commit(&mtr); @@ -4037,12 +4037,12 @@ bool ibuf_delete_rec(const page_id_t page_id, btr_pcur_t* pcur, ibuf_mtr_start(mtr); mysql_mutex_lock(&ibuf_mutex); + ibuf.index->lock.u_lock(SRW_LOCK_ARGS(__FILE__, __LINE__)); if (!ibuf_restore_pos(page_id, search_tuple, BTR_PURGE_TREE, pcur, mtr)) { - mysql_mutex_unlock(&ibuf_mutex); - ut_ad(mtr->has_committed()); + ibuf.index->lock.u_unlock(); goto func_exit; } @@ -4053,13 +4053,11 @@ bool ibuf_delete_rec(const page_id_t page_id, btr_pcur_t* pcur, ut_a(err == DB_SUCCESS); ibuf_size_update(ibuf_root->page.frame); - mysql_mutex_unlock(&ibuf_mutex); - ibuf.empty = page_is_empty(ibuf_root->page.frame); - } else { - mysql_mutex_unlock(&ibuf_mutex); } + mysql_mutex_unlock(&ibuf_mutex); + ibuf.index->lock.u_unlock(); ibuf_btr_pcur_commit_specify_mtr(pcur, mtr); func_exit: @@ -4237,7 +4235,7 @@ loop: /* Position pcur in the insert buffer at the first entry for this index page */ - if (btr_pcur_open_on_user_rec(search_tuple, PAGE_CUR_GE, + if (btr_pcur_open_on_user_rec(search_tuple, BTR_MODIFY_LEAF, &pcur, &mtr) != DB_SUCCESS) { err = DB_CORRUPTION; @@ -4454,7 +4452,7 @@ loop: /* Position pcur in the insert buffer at the first entry for the space */ - if (btr_pcur_open_on_user_rec(&search_tuple, PAGE_CUR_GE, + if (btr_pcur_open_on_user_rec(&search_tuple, BTR_MODIFY_LEAF, &pcur, &mtr) != DB_SUCCESS) { goto leave_loop; diff --git a/storage/innobase/include/btr0btr.h b/storage/innobase/include/btr0btr.h index a2aa46b62da..a1cc10b05db 100644 --- a/storage/innobase/include/btr0btr.h +++ b/storage/innobase/include/btr0btr.h @@ -2,7 +2,7 @@ Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2014, 2022, MariaDB Corporation. +Copyright (c) 2014, 2023, 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 @@ -211,13 +211,12 @@ btr_write_autoinc(dict_index_t* index, ib_uint64_t autoinc, bool reset = false) @param[in,out] mtr mini-transaction */ void btr_set_instant(buf_block_t* root, const dict_index_t& index, mtr_t* mtr); -ATTRIBUTE_COLD __attribute__((nonnull, warn_unused_result)) +ATTRIBUTE_COLD __attribute__((nonnull)) /** Reset the table to the canonical format on ROLLBACK of instant ALTER TABLE. @param[in] index clustered index with instant ALTER TABLE @param[in] all whether to reset FIL_PAGE_TYPE as well -@param[in,out] mtr mini-transaction -@return error code */ -dberr_t btr_reset_instant(const dict_index_t &index, bool all, mtr_t *mtr); +@param[in,out] mtr mini-transaction */ +void btr_reset_instant(const dict_index_t &index, bool all, mtr_t *mtr); /*************************************************************//** Makes tree one level higher by splitting the root, and inserts @@ -241,7 +240,7 @@ btr_root_raise_and_insert( ulint n_ext, /*!< in: number of externally stored columns */ mtr_t* mtr, /*!< in: mtr */ dberr_t* err) /*!< out: error code */ - MY_ATTRIBUTE((warn_unused_result)); + MY_ATTRIBUTE((nonnull, warn_unused_result)); /*************************************************************//** Reorganizes an index page. diff --git a/storage/innobase/include/btr0cur.h b/storage/innobase/include/btr0cur.h index 49bc8a4ff1b..e9c44d6d9bf 100644 --- a/storage/innobase/include/btr0cur.h +++ b/storage/innobase/include/btr0cur.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2022, MariaDB Corporation. +Copyright (c) 2017, 2023, 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 @@ -63,12 +63,6 @@ enum { BTR_KEEP_IBUF_BITMAP = 32 }; -/* btr_cur_latch_leaves() returns latched blocks and savepoints. */ -struct btr_latch_leaves_t { - buf_block_t* blocks[3]; - ulint savepoints[3]; -}; - #include "que0types.h" #include "row0types.h" @@ -126,51 +120,28 @@ bool btr_cur_instant_root_init(dict_index_t* index, const page_t* page) ATTRIBUTE_COLD __attribute__((nonnull, warn_unused_result)); -/** Optimistically latches the leaf page or pages requested. -@param[in] block guessed buffer block -@param[in] modify_clock modify clock value -@param[in,out] latch_mode BTR_SEARCH_LEAF, ... -@param[in,out] cursor cursor -@param[in] mtr mini-transaction -@return true if success */ -bool -btr_cur_optimistic_latch_leaves( - buf_block_t* block, - ib_uint64_t modify_clock, - btr_latch_mode* latch_mode, - btr_cur_t* cursor, - mtr_t* mtr); - MY_ATTRIBUTE((warn_unused_result)) -/** Searches an index tree and positions a tree cursor on a given level. +/********************************************************************//** +Searches an index tree and positions a tree cursor on a given non-leaf level. NOTE: n_fields_cmp in tuple must be set so that it cannot be compared to node pointer page number fields on the upper levels of the tree! -Note that if mode is PAGE_CUR_LE, which is used in inserts, then cursor->up_match and cursor->low_match both will have sensible values. -If mode is PAGE_CUR_GE, then up_match will a have a sensible value. +Cursor is left at the place where an insert of the +search tuple should be performed in the B-tree. InnoDB does an insert +immediately after the cursor. Thus, the cursor may end up on a user record, +or on a page infimum record. @param level the tree level of search @param tuple data tuple; NOTE: n_fields_cmp in tuple must be set so that it cannot get compared to the node ptr page number field! -@param mode PAGE_CUR_L, ...; NOTE that if the search is made using a - unique prefix of a record, mode should be PAGE_CUR_LE, not - PAGE_CUR_GE, as the latter may end up on the previous page of - the record! Inserts should always be made using PAGE_CUR_LE - to search the position! -@param latch_mode BTR_SEARCH_LEAF, ..., ORed with at most one of BTR_INSERT, - BTR_DELETE_MARK, or BTR_DELETE; - cursor->left_block is used to store a pointer to the left - neighbor page +@param latch RW_S_LATCH or RW_X_LATCH @param cursor tree cursor; the cursor page is s- or x-latched, but see also above! @param mtr mini-transaction -@param autoinc PAGE_ROOT_AUTO_INC to be written (0 if none) @return DB_SUCCESS on success or error code otherwise */ dberr_t btr_cur_search_to_nth_level(ulint level, const dtuple_t *tuple, - page_cur_mode_t mode, - btr_latch_mode latch_mode, - btr_cur_t *cursor, mtr_t *mtr, - ib_uint64_t autoinc= 0); + rw_lock_type_t rw_latch, + btr_cur_t *cursor, mtr_t *mtr); /*************************************************************//** Tries to perform an insert to a page in an index tree, next to cursor. @@ -657,15 +628,13 @@ btr_rec_copy_externally_stored_field( @param[in] block leaf page where the search converged @param[in] latch_mode BTR_SEARCH_LEAF, ... @param[in] cursor cursor -@param[in,out] mtr mini-transaction -@param[out] latch_leaves latched blocks and savepoints */ +@param[in,out] mtr mini-transaction */ void btr_cur_latch_leaves( - buf_block_t* block, + ulint block_savepoint, btr_latch_mode latch_mode, btr_cur_t* cursor, - mtr_t* mtr, - btr_latch_leaves_t* latch_leaves = nullptr); + mtr_t* mtr); /*######################################################################*/ @@ -734,14 +703,14 @@ struct btr_cur_t { BTR_MODIFY_PREV */ /*------------------------------*/ que_thr_t* thr; /*!< this field is only used - when btr_cur_search_to_nth_level + when search_leaf() is called for an index entry insertion: the calling query thread is passed here to be used in the insert buffer */ /*------------------------------*/ /** The following fields are used in - btr_cur_search_to_nth_level to pass information: */ + search_leaf() to pass information: */ /* @{ */ enum btr_cur_method flag; /*!< Search method used */ ulint tree_height; /*!< Tree height if the search is done @@ -750,8 +719,7 @@ struct btr_cur_t { ulint up_match; /*!< If the search mode was PAGE_CUR_LE, the number of matched fields to the the first user record to the right of - the cursor record after - btr_cur_search_to_nth_level; + the cursor record after search_leaf(); for the mode PAGE_CUR_GE, the matched fields to the first user record AT THE CURSOR or to the right of it; @@ -768,8 +736,7 @@ struct btr_cur_t { ulint low_match; /*!< if search mode was PAGE_CUR_LE, the number of matched fields to the first user record AT THE CURSOR or - to the left of it after - btr_cur_search_to_nth_level; + to the left of it after search_leaf(); NOT defined for PAGE_CUR_GE or any other search modes; see also the NOTE in up_match! */ @@ -803,6 +770,24 @@ struct btr_cur_t { dberr_t open_leaf(bool first, dict_index_t *index, btr_latch_mode latch_mode, mtr_t *mtr); + /** Search the leaf page record corresponding to a key. + @param tuple key to search for, with correct n_fields_cmp + @param mode search mode; PAGE_CUR_LE for unique prefix or for inserting + @param latch_mode latch mode + @param mtr mini-transaction + @return error code */ + dberr_t search_leaf(const dtuple_t *tuple, page_cur_mode_t mode, + btr_latch_mode latch_mode, mtr_t *mtr); + + /** Search the leaf page record corresponding to a key, exclusively latching + all sibling pages on the way. + @param tuple key to search for, with correct n_fields_cmp + @param mode search mode; PAGE_CUR_LE for unique prefix or for inserting + @param mtr mini-transaction + @return error code */ + dberr_t pessimistic_search_leaf(const dtuple_t *tuple, page_cur_mode_t mode, + mtr_t *mtr); + /** Open the cursor at a random leaf page record. @param offsets temporary memory for rec_get_offsets() @param heap memory heap for rec_get_offsets() @@ -862,14 +847,14 @@ inherited external field. */ #define BTR_EXTERN_INHERITED_FLAG 64U #ifdef BTR_CUR_HASH_ADAPT -/** Number of searches down the B-tree in btr_cur_search_to_nth_level(). */ +/** Number of searches down the B-tree in btr_cur_t::search_leaf(). */ extern ib_counter_t btr_cur_n_non_sea; /** Old value of btr_cur_n_non_sea. Copied by srv_refresh_innodb_monitor_stats(). Referenced by srv_printf_innodb_monitor(). */ extern ulint btr_cur_n_non_sea_old; /** Number of successful adaptive hash index lookups in -btr_cur_search_to_nth_level(). */ +btr_cur_t::search_leaf(). */ extern ib_counter_t btr_cur_n_sea; /** Old value of btr_cur_n_sea. Copied by srv_refresh_innodb_monitor_stats(). Referenced by diff --git a/storage/innobase/include/btr0pcur.h b/storage/innobase/include/btr0pcur.h index cd8eacdc212..a25704ede53 100644 --- a/storage/innobase/include/btr0pcur.h +++ b/storage/innobase/include/btr0pcur.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2022, MariaDB Corporation. +Copyright (c) 2017, 2023, 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 @@ -70,24 +70,6 @@ btr_pcur_init( /*==========*/ btr_pcur_t* pcur); /*!< in: persistent cursor */ -/**************************************************************//** -Initializes and opens a persistent cursor to an index tree. */ -inline -dberr_t -btr_pcur_open( - const dtuple_t* tuple, /*!< in: tuple on which search done */ - page_cur_mode_t mode, /*!< in: PAGE_CUR_L, ...; - NOTE that if the search is made using a unique - prefix of a record, mode should be - PAGE_CUR_LE, not PAGE_CUR_GE, as the latter - may end up on the previous page from the - record! */ - btr_latch_mode latch_mode,/*!< in: BTR_SEARCH_LEAF, ... */ - btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ - ib_uint64_t autoinc,/*!< in: PAGE_ROOT_AUTO_INC to be written - (0 if none) */ - mtr_t* mtr) /*!< in: mtr */ - MY_ATTRIBUTE((nonnull, warn_unused_result)); /** Opens an persistent cursor to an index tree without initializing the cursor. @param tuple tuple on which search done @@ -100,8 +82,7 @@ cursor. @param mtr mini-transaction @return DB_SUCCESS on success or error code otherwise. */ inline -dberr_t btr_pcur_open_with_no_init(const dtuple_t *tuple, - page_cur_mode_t mode, +dberr_t btr_pcur_open_with_no_init(const dtuple_t *tuple, page_cur_mode_t mode, btr_latch_mode latch_mode, btr_pcur_t *cursor, mtr_t *mtr); @@ -409,8 +390,7 @@ struct btr_pcur_t pos_state= BTR_PCUR_IS_POSITIONED; old_rec= nullptr; - return btr_cur.open_leaf(first, index, - BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode), mtr); + return btr_cur.open_leaf(first, index, this->latch_mode, mtr); } }; @@ -433,6 +413,24 @@ inline rec_t *btr_pcur_get_rec(const btr_pcur_t *cursor) return cursor->btr_cur.page_cur.rec; } +/**************************************************************//** +Initializes and opens a persistent cursor to an index tree. */ +inline +dberr_t +btr_pcur_open( + const dtuple_t* tuple, /*!< in: tuple on which search done */ + page_cur_mode_t mode, /*!< in: PAGE_CUR_LE, ... */ + btr_latch_mode latch_mode,/*!< in: BTR_SEARCH_LEAF, ... */ + btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ + mtr_t* mtr) /*!< in: mtr */ +{ + cursor->latch_mode= BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); + cursor->search_mode= mode; + cursor->pos_state= BTR_PCUR_IS_POSITIONED; + cursor->trx_if_known= nullptr; + return cursor->btr_cur.search_leaf(tuple, mode, latch_mode, mtr); +} + /** Open a cursor on the first user record satisfying the search condition; in case of no match, after the last index record. */ MY_ATTRIBUTE((nonnull, warn_unused_result)) @@ -440,16 +438,15 @@ inline dberr_t btr_pcur_open_on_user_rec( const dtuple_t* tuple, /*!< in: tuple on which search done */ - page_cur_mode_t mode, /*!< in: PAGE_CUR_L, ... */ btr_latch_mode latch_mode, /*!< in: BTR_SEARCH_LEAF or BTR_MODIFY_LEAF */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ mtr_t* mtr) /*!< in: mtr */ { - ut_ad(mode == PAGE_CUR_GE || mode == PAGE_CUR_G); ut_ad(latch_mode == BTR_SEARCH_LEAF || latch_mode == BTR_MODIFY_LEAF); - if (dberr_t err= btr_pcur_open(tuple, mode, latch_mode, cursor, 0, mtr)) + if (dberr_t err= + btr_pcur_open(tuple, PAGE_CUR_GE, latch_mode, cursor, mtr)) return err; if (!btr_pcur_is_after_last_on_page(cursor) || btr_pcur_is_after_last_in_tree(cursor)) diff --git a/storage/innobase/include/btr0pcur.inl b/storage/innobase/include/btr0pcur.inl index 551f8f20fca..b827d70dc47 100644 --- a/storage/innobase/include/btr0pcur.inl +++ b/storage/innobase/include/btr0pcur.inl @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2015, 2022, MariaDB Corporation. +Copyright (c) 2015, 2023, 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 @@ -299,38 +299,10 @@ btr_pcur_init( pcur->btr_cur.rtr_info = NULL; } -/**************************************************************//** -Initializes and opens a persistent cursor to an index tree. */ -inline -dberr_t -btr_pcur_open( - const dtuple_t* tuple, /*!< in: tuple on which search done */ - page_cur_mode_t mode, /*!< in: PAGE_CUR_L, ...; - NOTE that if the search is made using a unique - prefix of a record, mode should be - PAGE_CUR_LE, not PAGE_CUR_GE, as the latter - may end up on the previous page from the - record! */ - btr_latch_mode latch_mode,/*!< in: BTR_SEARCH_LEAF, ... */ - btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ - ib_uint64_t autoinc,/*!< in: PAGE_ROOT_AUTO_INC to be written - (0 if none) */ - mtr_t* mtr) /*!< in: mtr */ -{ - ut_ad(!cursor->index()->is_spatial()); - cursor->latch_mode= BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); - cursor->search_mode= mode; - cursor->pos_state= BTR_PCUR_IS_POSITIONED; - cursor->trx_if_known= nullptr; - return btr_cur_search_to_nth_level(0, tuple, mode, latch_mode, - btr_pcur_get_btr_cur(cursor), - mtr, autoinc); -} - /** Opens an persistent cursor to an index tree without initializing the cursor. @param tuple tuple on which search done -@param mode PAGE_CUR_L, ...; NOTE that if the search is made using a +@param mode search mode; NOTE that if the search is made using a unique prefix of a record, mode should be PAGE_CUR_LE, not PAGE_CUR_GE, as the latter may end up on the previous page of the record! @@ -339,8 +311,7 @@ cursor. @param mtr mini-transaction @return DB_SUCCESS on success or error code otherwise. */ inline -dberr_t btr_pcur_open_with_no_init(const dtuple_t *tuple, - page_cur_mode_t mode, +dberr_t btr_pcur_open_with_no_init(const dtuple_t *tuple, page_cur_mode_t mode, btr_latch_mode latch_mode, btr_pcur_t *cursor, mtr_t *mtr) { @@ -348,10 +319,7 @@ dberr_t btr_pcur_open_with_no_init(const dtuple_t *tuple, cursor->search_mode= mode; cursor->pos_state= BTR_PCUR_IS_POSITIONED; cursor->trx_if_known= nullptr; - - /* Search with the tree cursor */ - return btr_cur_search_to_nth_level(0, tuple, mode, latch_mode, - btr_pcur_get_btr_cur(cursor), mtr); + return cursor->btr_cur.search_leaf(tuple, mode, latch_mode, mtr); } /**************************************************************//** diff --git a/storage/innobase/include/btr0types.h b/storage/innobase/include/btr0types.h index 6118bfbc128..912c022c64f 100644 --- a/storage/innobase/include/btr0types.h +++ b/storage/innobase/include/btr0types.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2018, 2022, MariaDB Corporation. +Copyright (c) 2018, 2023, 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 @@ -55,25 +55,26 @@ in the index record. */ #define BTR_EXTERN_LOCAL_STORED_MAX_SIZE \ (BTR_EXTERN_FIELD_REF_SIZE * 2) -/** Latching modes for btr_cur_search_to_nth_level(). */ +/** Latching modes for btr_cur_t::search_leaf(). */ enum btr_latch_mode { /** Search a record on a leaf page and S-latch it. */ BTR_SEARCH_LEAF = RW_S_LATCH, /** (Prepare to) modify a record on a leaf page and X-latch it. */ BTR_MODIFY_LEAF = RW_X_LATCH, + /** U-latch root and X-latch a leaf page */ + BTR_MODIFY_ROOT_AND_LEAF = RW_SX_LATCH, /** Obtain no latches. */ BTR_NO_LATCHES = RW_NO_LATCH, - /** Search the previous record. */ + /** Search the previous record. + Used in btr_pcur_move_backward_from_page(). */ BTR_SEARCH_PREV = 4 | BTR_SEARCH_LEAF, - /** Modify the previous record. */ + /** Modify the previous record. + Used in btr_pcur_move_backward_from_page() and ibuf_insert(). */ BTR_MODIFY_PREV = 4 | BTR_MODIFY_LEAF, - /** Start searching the entire B-tree. */ - BTR_SEARCH_TREE = 8 | BTR_SEARCH_LEAF, - /** Start modifying1 the entire B-tree. */ + /** Start modifying the entire B-tree. */ BTR_MODIFY_TREE = 8 | BTR_MODIFY_LEAF, - /** Continue searching the entire B-tree. */ - BTR_CONT_SEARCH_TREE = 4 | BTR_SEARCH_TREE, - /** Continue modifying the entire B-tree. */ + /** Continue modifying the entire R-tree. + Only used by rtr_search_to_nth_level(). */ BTR_CONT_MODIFY_TREE = 4 | BTR_MODIFY_TREE, /* BTR_INSERT, BTR_DELETE and BTR_DELETE_MARK are mutually @@ -98,14 +99,14 @@ enum btr_latch_mode { dict_index_t::lock S-latch is being held. */ BTR_SEARCH_LEAF_ALREADY_S_LATCHED = BTR_SEARCH_LEAF | BTR_ALREADY_S_LATCHED, - /** Search the entire index tree, assuming that the - dict_index_t::lock S-latch is being held. */ - BTR_SEARCH_TREE_ALREADY_S_LATCHED = BTR_SEARCH_TREE - | BTR_ALREADY_S_LATCHED, /** Search and X-latch a leaf page, assuming that the dict_index_t::lock is being held in non-exclusive mode. */ BTR_MODIFY_LEAF_ALREADY_LATCHED = BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED, + /** U-latch root and X-latch a leaf page, assuming that + dict_index_t::lock is being held in U mode. */ + BTR_MODIFY_ROOT_AND_LEAF_ALREADY_LATCHED = BTR_MODIFY_ROOT_AND_LEAF + | BTR_ALREADY_S_LATCHED, /** Attempt to delete-mark a secondary index record. */ BTR_DELETE_MARK_LEAF = BTR_MODIFY_LEAF | BTR_DELETE_MARK, @@ -132,6 +133,9 @@ enum btr_latch_mode { /** Attempt to delete a record in the tree. */ BTR_PURGE_TREE = BTR_MODIFY_TREE | BTR_LATCH_FOR_DELETE, + /** Attempt to delete a record in an x-latched tree. */ + BTR_PURGE_TREE_ALREADY_LATCHED = BTR_PURGE_TREE + | BTR_ALREADY_S_LATCHED, /** Attempt to insert a record into the tree. */ BTR_INSERT_TREE = BTR_MODIFY_TREE | BTR_LATCH_FOR_INSERT, diff --git a/storage/innobase/include/gis0rtree.h b/storage/innobase/include/gis0rtree.h index 777f2432c93..b07261ce042 100644 --- a/storage/innobase/include/gis0rtree.h +++ b/storage/innobase/include/gis0rtree.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2014, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2022, MariaDB Corporation. +Copyright (c) 2017, 2023, 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 @@ -59,6 +59,44 @@ Created 2013/03/27 Jimmy Yang and Allen Lai /* Geometry data header */ #define GEO_DATA_HEADER_SIZE 4 + +/** Search for a spatial index leaf page record. +@param cur cursor +@param tuple search tuple +@param latch_mode latching mode +@param mtr mini-transaction +@param mode search mode */ +dberr_t rtr_search_leaf(btr_cur_t *cur, const dtuple_t *tuple, + btr_latch_mode latch_mode, mtr_t *mtr, + page_cur_mode_t mode= PAGE_CUR_RTREE_LOCATE) + MY_ATTRIBUTE((nonnull, warn_unused_result)); + +/** Search for inserting a spatial index leaf page record. +@param cur cursor +@param tuple search tuple +@param latch_mode latching mode +@param mtr mini-transaction */ +inline dberr_t rtr_insert_leaf(btr_cur_t *cur, const dtuple_t *tuple, + btr_latch_mode latch_mode, mtr_t *mtr) +{ + return rtr_search_leaf(cur, tuple, latch_mode, mtr, PAGE_CUR_RTREE_INSERT); +} + +/** Search for a spatial index leaf page record. +@param pcur cursor +@param tuple search tuple +@param mode search mode +@param mtr mini-transaction */ +dberr_t rtr_search_leaf(btr_pcur_t *pcur, const dtuple_t *tuple, + page_cur_mode_t mode, mtr_t *mtr) + MY_ATTRIBUTE((nonnull, warn_unused_result)); + +dberr_t rtr_search_to_nth_level(ulint level, const dtuple_t *tuple, + page_cur_mode_t mode, + btr_latch_mode latch_mode, + btr_cur_t *cur, mtr_t *mtr) + MY_ATTRIBUTE((nonnull, warn_unused_result)); + /**********************************************************************//** Builds a Rtree node pointer out of a physical record and a page number. @return own: node pointer */ @@ -295,11 +333,9 @@ rtr_store_parent_path( /**************************************************************//** Initializes and opens a persistent cursor to an index tree. It should be closed with btr_pcur_close. */ -bool -rtr_pcur_open( - dict_index_t* index, /*!< in: index */ +bool rtr_search( const dtuple_t* tuple, /*!< in: tuple on which search done */ - btr_latch_mode latch_mode,/*!< in: BTR_SEARCH_LEAF, ... */ + btr_latch_mode latch_mode,/*!< in: BTR_MODIFY_LEAF, ... */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ mtr_t* mtr) /*!< in: mtr */ MY_ATTRIBUTE((warn_unused_result)); diff --git a/storage/innobase/include/gis0type.h b/storage/innobase/include/gis0type.h index 4fccfdb6c26..d6a4ef67a38 100644 --- a/storage/innobase/include/gis0type.h +++ b/storage/innobase/include/gis0type.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2014, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2018, 2020, MariaDB Corporation. +Copyright (c) 2018, 2023, 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 @@ -105,12 +105,6 @@ typedef struct rtr_info{ matched_rec_t* matches;/*!< struct holding matching leaf records */ mysql_mutex_t rtr_path_mutex; /*!< mutex protect the "path" vector */ - buf_block_t* tree_blocks[RTR_MAX_LEVELS + RTR_LEAF_LATCH_NUM]; - /*!< tracking pages that would be locked - at leaf level, for future free */ - ulint tree_savepoints[RTR_MAX_LEVELS + RTR_LEAF_LATCH_NUM]; - /*!< savepoint used to release latches/blocks - on each level and leaf level */ rtr_mbr_t mbr; /*!< the search MBR */ que_thr_t* thr; /*!< the search thread */ mem_heap_t* heap; /*!< memory heap */ diff --git a/storage/innobase/include/ibuf0ibuf.inl b/storage/innobase/include/ibuf0ibuf.inl index 9f4e937f31d..1e21f74ff2b 100644 --- a/storage/innobase/include/ibuf0ibuf.inl +++ b/storage/innobase/include/ibuf0ibuf.inl @@ -100,9 +100,8 @@ ibuf_should_try( decide */ { return(innodb_change_buffering + && !(index->type & (DICT_CLUSTERED | DICT_IBUF)) && ibuf.max_size != 0 - && !dict_index_is_clust(index) - && !dict_index_is_spatial(index) && index->table->quiesce == QUIESCE_NONE && (ignore_sec_unique || !dict_index_is_unique(index))); } diff --git a/storage/innobase/include/mtr0log.h b/storage/innobase/include/mtr0log.h index 093b706c1de..0dfb50125bd 100644 --- a/storage/innobase/include/mtr0log.h +++ b/storage/innobase/include/mtr0log.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2019, 2022, MariaDB Corporation. +Copyright (c) 2019, 2023, 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 @@ -401,7 +401,9 @@ inline byte *mtr_t::log_write(const page_id_t id, const buf_page_t *bpage, ut_ad(have_offset || offset == 0); ut_ad(offset + len <= srv_page_size); static_assert(MIN_4BYTE >= UNIV_PAGE_SIZE_MAX, "consistency"); - + ut_ad(type == FREE_PAGE || type == OPTION || (type == EXTENDED && !bpage) || + memo_contains_flagged(bpage, + MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX)); size_t max_len; if (!have_len) max_len= 1 + 5 + 5; diff --git a/storage/innobase/include/mtr0mtr.h b/storage/innobase/include/mtr0mtr.h index 41f9b473856..3c4e9da69a2 100644 --- a/storage/innobase/include/mtr0mtr.h +++ b/storage/innobase/include/mtr0mtr.h @@ -37,15 +37,6 @@ Created 11/26/1995 Heikki Tuuri /** Commit a mini-transaction. */ #define mtr_commit(m) (m)->commit() -/** Set and return a savepoint in mtr. -@return savepoint */ -#define mtr_set_savepoint(m) (m)->get_savepoint() - -/** Release the (index tree) s-latch stored in an mtr memo after a -savepoint. */ -#define mtr_release_s_latch_at_savepoint(m, s, l) \ - (m)->release_s_latch_at_savepoint((s), (l)) - /** Change the logging mode of a mini-transaction. @return old mode */ #define mtr_set_log_mode(m, d) (m)->set_log_mode((d)) @@ -60,13 +51,10 @@ savepoint. */ # define mtr_sx_lock_index(i,m) (m)->u_lock(&(i)->lock) #endif -#define mtr_release_block_at_savepoint(m, s, b) \ - (m)->release_block_at_savepoint((s), (b)) - /** Mini-transaction memo stack slot. */ struct mtr_memo_slot_t { - /** pointer to the object, or nullptr if released */ + /** pointer to the object */ void *object; /** type of the stored object */ mtr_memo_type_t type; @@ -125,41 +113,36 @@ struct mtr_t { return m_memo.size(); } - /** Release the (index tree) s-latch stored in an mtr memo after a savepoint. - @param savepoint value returned by get_savepoint() - @param lock index latch to release */ - void release_s_latch_at_savepoint(ulint savepoint, index_lock *lock) + /** Get the block at a savepoint */ + buf_block_t *at_savepoint(ulint savepoint) const { ut_ad(is_active()); - mtr_memo_slot_t &slot= m_memo[savepoint]; - ut_ad(slot.object == lock); - ut_ad(slot.type == MTR_MEMO_S_LOCK); - slot.object= nullptr; - lock->s_unlock(); + const mtr_memo_slot_t &slot= m_memo[savepoint]; + ut_ad(slot.type < MTR_MEMO_S_LOCK); + ut_ad(slot.object); + return static_cast(slot.object); } - /** Release the block in an mtr memo after a savepoint. */ - void release_block_at_savepoint(ulint savepoint, buf_block_t *block) + + /** Try to get a block at a savepoint. + @param savepoint the savepoint right before the block was acquired + @return the block at the savepoint + @retval nullptr if no buffer block was registered at that savepoint */ + buf_block_t *block_at_savepoint(ulint savepoint) const { ut_ad(is_active()); - mtr_memo_slot_t &slot= m_memo[savepoint]; - ut_ad(slot.object == block); - ut_ad(!(slot.type & MTR_MEMO_MODIFY)); - slot.object= nullptr; - block->page.unfix(); - - switch (slot.type) { - case MTR_MEMO_PAGE_S_FIX: - block->page.lock.s_unlock(); - break; - case MTR_MEMO_PAGE_SX_FIX: - case MTR_MEMO_PAGE_X_FIX: - block->page.lock.u_or_x_unlock(slot.type == MTR_MEMO_PAGE_SX_FIX); - break; - default: - break; - } + const mtr_memo_slot_t &slot= m_memo[savepoint]; + return slot.type < MTR_MEMO_S_LOCK + ? static_cast(slot.object) + : nullptr; } + /** Retrieve a page that has already been latched. + @param id page identifier + @param type page latch type + @return block */ + buf_block_t *get_already_latched(const page_id_t id, mtr_memo_type_t type) + const; + /** @return if we are about to make a clean buffer block dirty */ static bool is_block_dirtied(const buf_page_t &b) { @@ -408,28 +391,17 @@ public: @param rw_latch latch to acquire */ void upgrade_buffer_fix(ulint savepoint, rw_lock_type_t rw_latch); - /** Register a page latch on a buffer-fixed block was buffer-fixed. - @param latch latch type */ - void u_lock_register(ulint savepoint) - { - mtr_memo_slot_t &slot= m_memo[savepoint]; - ut_ad(slot.type == MTR_MEMO_BUF_FIX); - slot.type= MTR_MEMO_PAGE_SX_FIX; - } - - /** Register a page latch on a buffer-fixed block was buffer-fixed. - @param latch latch type */ - void s_lock_register(ulint savepoint) + /** Register a page latch on a previously buffer-fixed block. */ + void lock_register(ulint savepoint, mtr_memo_type_t type) { mtr_memo_slot_t &slot= m_memo[savepoint]; ut_ad(slot.type == MTR_MEMO_BUF_FIX); - slot.type= MTR_MEMO_PAGE_S_FIX; + ut_ad(type <= MTR_MEMO_PAGE_SX_FIX); + slot.type= type; } /** Upgrade U locks on a block to X */ void page_lock_upgrade(const buf_block_t &block); - /** Upgrade U lock to X */ - void lock_upgrade(const index_lock &lock); /** Check if we are holding tablespace latch @param space tablespace to search for diff --git a/storage/innobase/include/small_vector.h b/storage/innobase/include/small_vector.h index 76069cfc168..d28a36184b8 100644 --- a/storage/innobase/include/small_vector.h +++ b/storage/innobase/include/small_vector.h @@ -71,6 +71,7 @@ public: using const_iterator= const T *; using reverse_iterator= std::reverse_iterator; using reference= T &; + using const_reference= const T&; iterator begin() { return static_cast(BeginX); } const_iterator begin() const { return static_cast(BeginX); } @@ -81,6 +82,8 @@ public: reverse_iterator rend() { return reverse_iterator(begin()); } reference operator[](size_t i) { assert(i < size()); return begin()[i]; } + const_reference operator[](size_t i) const + { return const_cast(*this)[i]; } void erase(const_iterator S, const_iterator E) { diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc index 730914b2892..ce72b26693c 100644 --- a/storage/innobase/mtr/mtr0mtr.cc +++ b/storage/innobase/mtr/mtr0mtr.cc @@ -40,6 +40,8 @@ Created 11/26/1995 Heikki Tuuri void mtr_memo_slot_t::release() const { + ut_ad(object); + switch (type) { case MTR_MEMO_S_LOCK: static_cast(object)->s_unlock(); @@ -121,11 +123,7 @@ inline void mtr_t::release_resources() void mtr_t::release() { for (auto it= m_memo.rbegin(); it != m_memo.rend(); it++) - { - mtr_memo_slot_t &slot= *it; - if (slot.object) - slot.release(); - } + it->release(); m_memo.clear(); } @@ -191,7 +189,7 @@ void mtr_t::commit() for (const mtr_memo_slot_t &slot : m_memo) { - if (slot.object && slot.type & MTR_MEMO_MODIFY) + if (slot.type & MTR_MEMO_MODIFY) { ut_ad(slot.type == MTR_MEMO_PAGE_X_MODIFY || slot.type == MTR_MEMO_PAGE_SX_MODIFY); @@ -226,8 +224,7 @@ void mtr_t::rollback_to_savepoint(ulint begin, ulint end) while (s-- > begin) { const mtr_memo_slot_t &slot= m_memo[s]; - if (!slot.object) - continue; + ut_ad(slot.object); /* This is intended for releasing latches on indexes or unmodified buffer pool pages. */ ut_ad(slot.type <= MTR_MEMO_SX_LOCK); @@ -289,8 +286,7 @@ void mtr_t::commit_shrink(fil_space_t &space) for (mtr_memo_slot_t &slot : m_memo) { - if (!slot.object) - continue; + ut_ad(slot.object); switch (slot.type) { default: ut_ad("invalid type" == 0); @@ -954,15 +950,6 @@ void mtr_t::page_lock_upgrade(const buf_block_t &block) #endif /* BTR_CUR_HASH_ADAPT */ } -void mtr_t::lock_upgrade(const index_lock &lock) -{ - ut_ad(lock.have_x()); - - for (mtr_memo_slot_t &slot : m_memo) - if (slot.object == &lock && slot.type == MTR_MEMO_SX_LOCK) - slot.type= MTR_MEMO_X_LOCK; -} - /** Latch a buffer pool block. @param block block to be latched @param rw_latch RW_S_LATCH, RW_SX_LATCH, RW_X_LATCH, RW_NO_LATCH */ @@ -1134,7 +1121,8 @@ buf_block_t* mtr_t::memo_contains_page_flagged(const byte *ptr, ulint flags) for (const mtr_memo_slot_t &slot : m_memo) { - if (!slot.object || !(flags & slot.type)) + ut_ad(slot.object); + if (!(flags & slot.type)) continue; buf_page_t *bpage= static_cast(slot.object); @@ -1194,20 +1182,23 @@ void mtr_t::free(const fil_space_t &space, uint32_t offset) buf_block_t *freed= nullptr; const page_id_t id{space.id, offset}; - for (auto it= m_memo.rbegin(); it != m_memo.rend(); it++) + for (auto it= m_memo.end(); it != m_memo.begin(); ) { + it--; + next: mtr_memo_slot_t &slot= *it; buf_block_t *block= static_cast(slot.object); - if (!block); - else if (block == freed) + ut_ad(block); + if (block == freed) { if (slot.type & (MTR_MEMO_PAGE_SX_FIX | MTR_MEMO_PAGE_X_FIX)) slot.type= MTR_MEMO_PAGE_X_FIX; else { ut_ad(slot.type == MTR_MEMO_BUF_FIX); - slot.object= nullptr; block->page.unfix(); + m_memo.erase(it, it + 1); + goto next; } } else if (slot.type & (MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX) && diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc index 45c35bc6995..861095b421e 100644 --- a/storage/innobase/row/row0import.cc +++ b/storage/innobase/row/row0import.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2012, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2015, 2022, MariaDB Corporation. +Copyright (c) 2015, 2023, 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 @@ -1629,6 +1629,9 @@ inline dberr_t IndexPurge::purge_pessimistic_delete() noexcept dberr_t IndexPurge::purge() noexcept { btr_pcur_store_position(&m_pcur, &m_mtr); + m_mtr.commit(); + m_mtr.start(); + m_mtr.set_log_mode(MTR_LOG_NO_REDO); dberr_t err= purge_pessimistic_delete(); m_mtr.start(); diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index 81babd0082e..eea66ae05aa 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2022, MariaDB Corporation. +Copyright (c) 2016, 2023, 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 @@ -314,8 +314,10 @@ row_ins_clust_index_entry_by_modify( } if (mode != BTR_MODIFY_TREE) { - ut_ad((mode & ulint(~BTR_ALREADY_S_LATCHED)) - == BTR_MODIFY_LEAF); + ut_ad(mode == BTR_MODIFY_LEAF + || mode == BTR_MODIFY_LEAF_ALREADY_LATCHED + || mode == BTR_MODIFY_ROOT_AND_LEAF + || mode == BTR_MODIFY_ROOT_AND_LEAF_ALREADY_LATCHED); /* Try optimistic updating of the record, keeping changes within the page */ @@ -1621,8 +1623,7 @@ row_ins_check_foreign_constraint( dtuple_set_n_fields_cmp(entry, foreign->n_fields); pcur.btr_cur.page_cur.index = check_index; - err = btr_pcur_open(entry, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, 0, - &mtr); + err = btr_pcur_open(entry, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); if (UNIV_UNLIKELY(err != DB_SUCCESS)) { goto end_scan; } @@ -2119,7 +2120,7 @@ row_ins_scan_sec_index_for_duplicate( pcur.btr_cur.page_cur.index = index; trx_t* const trx = thr_get_trx(thr); dberr_t err = btr_pcur_open(entry, PAGE_CUR_GE, BTR_SEARCH_LEAF, - &pcur, 0, mtr); + &pcur, mtr); if (err != DB_SUCCESS) { goto end_scan; } @@ -2539,8 +2540,8 @@ row_ins_index_entry_big_rec( index->set_modified(mtr); } - dberr_t error = btr_pcur_open(entry, PAGE_CUR_LE, - BTR_MODIFY_TREE, &pcur, 0, &mtr); + dberr_t error = btr_pcur_open(entry, PAGE_CUR_LE, BTR_MODIFY_TREE, + &pcur, &mtr); if (error != DB_SUCCESS) { return error; } @@ -2598,11 +2599,10 @@ row_ins_clust_index_entry_low( que_thr_t* thr) /*!< in: query thread */ { btr_pcur_t pcur; - btr_cur_t* cursor; dberr_t err = DB_SUCCESS; big_rec_t* big_rec = NULL; mtr_t mtr; - ib_uint64_t auto_inc = 0; + uint64_t auto_inc = 0; mem_heap_t* offsets_heap = NULL; rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs* offsets = offsets_; @@ -2618,7 +2618,7 @@ row_ins_clust_index_entry_low( ut_ad(!n_uniq || n_uniq == dict_index_get_n_unique(index)); ut_ad(!trx->in_rollback); - mtr_start(&mtr); + mtr.start(); if (index->table->is_temporary()) { /* Disable REDO logging as the lifetime of temp-tables is @@ -2658,6 +2658,13 @@ row_ins_clust_index_entry_low( dfield->type.mtype, dfield->type.prtype & DATA_UNSIGNED); + if (auto_inc + && mode != BTR_MODIFY_TREE) { + mode = btr_latch_mode( + BTR_MODIFY_ROOT_AND_LEAF + ^ BTR_MODIFY_LEAF + ^ mode); + } } } } @@ -2667,20 +2674,26 @@ row_ins_clust_index_entry_low( the function will return in both low_match and up_match of the cursor sensible values */ pcur.btr_cur.page_cur.index = index; - err = btr_pcur_open(entry, PAGE_CUR_LE, mode, &pcur, auto_inc, &mtr); + err = btr_pcur_open(entry, PAGE_CUR_LE, mode, &pcur, &mtr); if (err != DB_SUCCESS) { index->table->file_unreadable = true; -commit_exit: +err_exit: mtr.commit(); goto func_exit; } - cursor = btr_pcur_get_btr_cur(&pcur); - cursor->thr = thr; + if (auto_inc) { + buf_block_t* root + = mtr.at_savepoint(mode != BTR_MODIFY_ROOT_AND_LEAF); + ut_ad(index->page == root->page.id().page_no()); + page_set_autoinc(root, auto_inc, &mtr, false); + } + + btr_pcur_get_btr_cur(&pcur)->thr = thr; #ifdef UNIV_DEBUG { - page_t* page = btr_cur_get_page(cursor); + page_t* page = btr_pcur_get_page(&pcur); rec_t* first_rec = page_rec_get_next( page_get_infimum_rec(page)); @@ -2689,7 +2702,7 @@ commit_exit: } #endif /* UNIV_DEBUG */ - block = btr_cur_get_block(cursor); + block = btr_pcur_get_block(&pcur); DBUG_EXECUTE_IF("row_ins_row_level", goto skip_bulk_insert;); @@ -2711,7 +2724,7 @@ commit_exit: if (err != DB_SUCCESS) { trx->error_state = err; - goto commit_exit; + goto err_exit; } if (index->table->n_rec_locks) { @@ -2741,7 +2754,7 @@ skip_bulk_insert: ut_ad(index->is_instant()); ut_ad(!dict_index_is_online_ddl(index)); - const rec_t* rec = btr_cur_get_rec(cursor); + const rec_t* rec = btr_pcur_get_rec(&pcur); if (rec_get_info_bits(rec, page_rec_is_comp(rec)) & REC_INFO_MIN_REC_FLAG) { @@ -2750,16 +2763,17 @@ skip_bulk_insert: goto err_exit; } - ut_ad(!row_ins_must_modify_rec(cursor)); + ut_ad(!row_ins_must_modify_rec(&pcur.btr_cur)); goto do_insert; } - if (rec_is_metadata(btr_cur_get_rec(cursor), *index)) { + if (rec_is_metadata(btr_pcur_get_rec(&pcur), *index)) { goto do_insert; } if (n_uniq - && (cursor->up_match >= n_uniq || cursor->low_match >= n_uniq)) { + && (pcur.btr_cur.up_match >= n_uniq + || pcur.btr_cur.low_match >= n_uniq)) { if (flags == (BTR_CREATE_FLAG | BTR_NO_LOCKING_FLAG @@ -2767,7 +2781,7 @@ skip_bulk_insert: /* Set no locks when applying log in online table rebuild. Only check for duplicates. */ err = row_ins_duplicate_error_in_clust_online( - n_uniq, entry, cursor, + n_uniq, entry, &pcur.btr_cur, &offsets, &offsets_heap); switch (err) { @@ -2778,26 +2792,24 @@ skip_bulk_insert: /* fall through */ case DB_SUCCESS_LOCKED_REC: case DB_DUPLICATE_KEY: - trx->error_info = cursor->index(); + trx->error_info = index; } } else { /* Note that the following may return also DB_LOCK_WAIT */ err = row_ins_duplicate_error_in_clust( - flags, cursor, entry, thr); + flags, &pcur.btr_cur, entry, thr); } if (err != DB_SUCCESS) { -err_exit: - mtr_commit(&mtr); - goto func_exit; + goto err_exit; } } /* Note: Allowing duplicates would qualify for modification of an existing record as the new entry is exactly same as old entry. */ - if (row_ins_must_modify_rec(cursor)) { + if (row_ins_must_modify_rec(&pcur.btr_cur)) { /* There is already an index entry with a long enough common prefix, we must convert the insert into a modify of an existing record */ @@ -2815,10 +2827,13 @@ do_insert: rec_t* insert_rec; if (mode != BTR_MODIFY_TREE) { - ut_ad(mode == BTR_MODIFY_LEAF || - mode == BTR_MODIFY_LEAF_ALREADY_LATCHED); + ut_ad(mode == BTR_MODIFY_LEAF + || mode == BTR_MODIFY_LEAF_ALREADY_LATCHED + || mode == BTR_MODIFY_ROOT_AND_LEAF + || mode + == BTR_MODIFY_ROOT_AND_LEAF_ALREADY_LATCHED); err = btr_cur_optimistic_insert( - flags, cursor, &offsets, &offsets_heap, + flags, &pcur.btr_cur, &offsets, &offsets_heap, entry, &insert_rec, &big_rec, n_ext, thr, &mtr); } else { @@ -2827,17 +2842,15 @@ do_insert: goto err_exit; } - DEBUG_SYNC_C("before_insert_pessimitic_row_ins_clust"); - err = btr_cur_optimistic_insert( - flags, cursor, + flags, &pcur.btr_cur, &offsets, &offsets_heap, entry, &insert_rec, &big_rec, n_ext, thr, &mtr); if (err == DB_FAIL) { err = btr_cur_pessimistic_insert( - flags, cursor, + flags, &pcur.btr_cur, &offsets, &offsets_heap, entry, &insert_rec, &big_rec, n_ext, thr, &mtr); @@ -2949,9 +2962,7 @@ row_ins_sec_index_entry_low( rtr_init_rtr_info(&rtr_info, false, &cursor, index, false); rtr_info_update_btr(&cursor, &rtr_info); - err = btr_cur_search_to_nth_level(0, entry, - PAGE_CUR_RTREE_INSERT, - search_mode, &cursor, &mtr); + err = rtr_insert_leaf(&cursor, entry, search_mode, &mtr); if (err == DB_SUCCESS && search_mode == BTR_MODIFY_LEAF && rtr_info.mbr_adj) { @@ -2967,9 +2978,8 @@ row_ins_sec_index_entry_low( } else { index->set_modified(mtr); } - err = btr_cur_search_to_nth_level( - 0, entry, PAGE_CUR_RTREE_INSERT, - search_mode, &cursor, &mtr); + err = rtr_insert_leaf(&cursor, entry, + search_mode, &mtr); } DBUG_EXECUTE_IF( @@ -2985,8 +2995,8 @@ row_ins_sec_index_entry_low( : BTR_INSERT)); } - err = btr_cur_search_to_nth_level(0, entry, PAGE_CUR_LE, - search_mode, &cursor, &mtr); + err = cursor.search_leaf(entry, PAGE_CUR_LE, search_mode, + &mtr); } if (err != DB_SUCCESS) { @@ -3062,12 +3072,12 @@ row_ins_sec_index_entry_low( prevent any insertion of a duplicate by another transaction. Let us now reposition the cursor and continue the insertion (bypassing the change buffer). */ - err = btr_cur_search_to_nth_level( - 0, entry, PAGE_CUR_LE, + err = cursor.search_leaf( + entry, PAGE_CUR_LE, btr_latch_mode(search_mode & ~(BTR_INSERT | BTR_IGNORE_SEC_UNIQUE)), - &cursor, &mtr); + &mtr); if (err != DB_SUCCESS) { goto func_exit; } diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc index 94d69d88fb5..0743dc2bb50 100644 --- a/storage/innobase/row/row0log.cc +++ b/storage/innobase/row/row0log.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2011, 2018, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2022, MariaDB Corporation. +Copyright (c) 2017, 2023, 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 @@ -1696,8 +1696,8 @@ err_exit: mtr->start(); index->set_modified(*mtr); pcur->btr_cur.page_cur.index = index; - error = btr_pcur_open(entry, PAGE_CUR_LE, - BTR_PURGE_TREE, pcur, 0, mtr); + error = btr_pcur_open(entry, PAGE_CUR_LE, BTR_PURGE_TREE, pcur, + mtr); if (error) { goto err_exit; } @@ -1780,8 +1780,8 @@ row_log_table_apply_delete( mtr_start(&mtr); index->set_modified(mtr); - dberr_t err = btr_pcur_open(old_pk, PAGE_CUR_LE, - BTR_PURGE_TREE, &pcur, 0, &mtr); + dberr_t err = btr_pcur_open(old_pk, PAGE_CUR_LE, BTR_PURGE_TREE, &pcur, + &mtr); if (err != DB_SUCCESS) { goto all_done; } @@ -1917,8 +1917,8 @@ row_log_table_apply_update( mtr.start(); index->set_modified(mtr); - error = btr_pcur_open(old_pk, PAGE_CUR_LE, - BTR_MODIFY_TREE, &pcur, 0, &mtr); + error = btr_pcur_open(old_pk, PAGE_CUR_LE, BTR_MODIFY_TREE, &pcur, + &mtr); if (error != DB_SUCCESS) { func_exit: mtr.commit(); @@ -3084,11 +3084,8 @@ row_log_apply_op_low( record. The operation may already have been performed, depending on when the row in the clustered index was scanned. */ - *error = btr_cur_search_to_nth_level(0, entry, PAGE_CUR_LE, - has_index_lock - ? BTR_MODIFY_TREE - : BTR_MODIFY_LEAF, - &cursor, &mtr); + *error = cursor.search_leaf(entry, PAGE_CUR_LE, has_index_lock + ? BTR_MODIFY_TREE : BTR_MODIFY_LEAF, &mtr); if (UNIV_UNLIKELY(*error != DB_SUCCESS)) { goto func_exit; } @@ -3138,9 +3135,9 @@ row_log_apply_op_low( mtr_commit(&mtr); mtr_start(&mtr); index->set_modified(mtr); - *error = btr_cur_search_to_nth_level( - 0, entry, PAGE_CUR_LE, - BTR_MODIFY_TREE, &cursor, &mtr); + *error = cursor.search_leaf(entry, PAGE_CUR_LE, + BTR_MODIFY_TREE, + &mtr); if (UNIV_UNLIKELY(*error != DB_SUCCESS)) { goto func_exit; } @@ -3242,9 +3239,9 @@ insert_the_rec: mtr_commit(&mtr); mtr_start(&mtr); index->set_modified(mtr); - *error = btr_cur_search_to_nth_level( - 0, entry, PAGE_CUR_LE, - BTR_MODIFY_TREE, &cursor, &mtr); + *error = cursor.search_leaf(entry, PAGE_CUR_LE, + BTR_MODIFY_TREE, + &mtr); if (*error != DB_SUCCESS) { break; } diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index caa2646eee2..535762ee105 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -150,9 +150,8 @@ public: false); rtr_info_update_btr(&ins_cur, &rtr_info); - error = btr_cur_search_to_nth_level( - 0, dtuple, PAGE_CUR_RTREE_INSERT, - BTR_MODIFY_LEAF, &ins_cur, &mtr); + error = rtr_insert_leaf(&ins_cur, dtuple, + BTR_MODIFY_LEAF, &mtr); /* It need to update MBR in parent entry, so change search mode to BTR_MODIFY_TREE */ @@ -164,10 +163,8 @@ public: rtr_info_update_btr(&ins_cur, &rtr_info); mtr.start(); index->set_modified(mtr); - error = btr_cur_search_to_nth_level( - 0, dtuple, - PAGE_CUR_RTREE_INSERT, - BTR_MODIFY_TREE, &ins_cur, &mtr); + error = rtr_insert_leaf(&ins_cur, dtuple, + BTR_MODIFY_TREE, &mtr); } if (error == DB_SUCCESS) { @@ -189,11 +186,8 @@ public: &ins_cur, index, false); rtr_info_update_btr(&ins_cur, &rtr_info); - error = btr_cur_search_to_nth_level( - 0, dtuple, - PAGE_CUR_RTREE_INSERT, - BTR_MODIFY_TREE, - &ins_cur, &mtr); + error = rtr_insert_leaf(&ins_cur, dtuple, + BTR_MODIFY_TREE, &mtr); if (error == DB_SUCCESS) { error = btr_cur_pessimistic_insert( diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc index e216d9faa3b..753b42332fc 100644 --- a/storage/innobase/row/row0purge.cc +++ b/storage/innobase/row/row0purge.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2022, MariaDB Corporation. +Copyright (c) 2017, 2023, 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 @@ -104,7 +104,7 @@ bool row_purge_remove_clust_if_poss_low( /*===============================*/ purge_node_t* node, /*!< in/out: row purge node */ - btr_latch_mode mode) /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */ + btr_latch_mode mode) /*!< in: BTR_MODIFY_LEAF or BTR_PURGE_TREE */ { dict_index_t* index = dict_table_get_first_index(node->table); table_id_t table_id = 0; @@ -342,17 +342,20 @@ row_purge_remove_sec_if_poss_tree( ibool success = TRUE; dberr_t err; mtr_t mtr; - enum row_search_result search_result; log_free_check(); mtr.start(); index->set_modified(mtr); pcur.btr_cur.page_cur.index = index; - search_result = row_search_index_entry(entry, BTR_PURGE_TREE, - &pcur, &mtr); + if (index->is_spatial()) { + if (!rtr_search(entry, BTR_PURGE_TREE, &pcur, &mtr)) { + goto found; + } + goto func_exit; + } - switch (search_result) { + switch (row_search_index_entry(entry, BTR_PURGE_TREE, &pcur, &mtr)) { case ROW_NOT_FOUND: /* Not found. This is a legitimate condition. In a rollback, InnoDB will remove secondary recs that would @@ -381,6 +384,7 @@ row_purge_remove_sec_if_poss_tree( which cannot be purged yet, requires its existence. If some requires, we should do nothing. */ +found: if (row_purge_poss_sec(node, index, entry, &pcur, &mtr, true)) { /* Remove the index record, which should have been @@ -439,8 +443,6 @@ row_purge_remove_sec_if_poss_leaf( { mtr_t mtr; btr_pcur_t pcur; - enum btr_latch_mode mode; - enum row_search_result search_result; bool success = true; log_free_check(); @@ -449,31 +451,27 @@ row_purge_remove_sec_if_poss_leaf( mtr.start(); index->set_modified(mtr); - /* Change buffering is disabled for spatial index and - virtual index. */ - mode = (index->type & (DICT_SPATIAL | DICT_VIRTUAL)) - ? BTR_MODIFY_LEAF : BTR_PURGE_LEAF; pcur.btr_cur.page_cur.index = index; /* Set the purge node for the call to row_purge_poss_sec(). */ pcur.btr_cur.purge_node = node; if (index->is_spatial()) { pcur.btr_cur.thr = NULL; - index->lock.u_lock(SRW_LOCK_CALL); - search_result = row_search_index_entry( - entry, mode, &pcur, &mtr); - index->lock.u_unlock(); - } else { - /* Set the query thread, so that ibuf_insert_low() will be - able to invoke thd_get_trx(). */ - pcur.btr_cur.thr = static_cast( - que_node_get_parent(node)); - search_result = row_search_index_entry( - entry, mode, &pcur, &mtr); + if (!rtr_search(entry, BTR_MODIFY_LEAF, &pcur, &mtr)) { + goto found; + } + goto func_exit; } - switch (search_result) { + /* Set the query thread, so that ibuf_insert_low() will be + able to invoke thd_get_trx(). */ + pcur.btr_cur.thr = static_cast(que_node_get_parent(node)); + + switch (row_search_index_entry(entry, index->has_virtual() + ? BTR_MODIFY_LEAF : BTR_PURGE_LEAF, + &pcur, &mtr)) { case ROW_FOUND: +found: /* Before attempting to purge a record, check if it is safe to do so. */ if (row_purge_poss_sec(node, index, entry, &pcur, &mtr, false)) { diff --git a/storage/innobase/row/row0row.cc b/storage/innobase/row/row0row.cc index b998d27d836..4a00b2a430e 100644 --- a/storage/innobase/row/row0row.cc +++ b/storage/innobase/row/row0row.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2018, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2018, 2022, MariaDB Corporation. +Copyright (c) 2018, 2023, 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 @@ -1216,7 +1216,7 @@ row_search_on_row_ref( & REC_INFO_MIN_REC_FLAG; } else { ut_a(ref->n_fields == index->n_uniq); - if (btr_pcur_open(ref, PAGE_CUR_LE, mode, pcur, 0, mtr) + if (btr_pcur_open(ref, PAGE_CUR_LE, mode, pcur, mtr) != DB_SUCCESS) { return false; } @@ -1278,21 +1278,13 @@ row_search_index_entry( ut_ad(dtuple_check_typed(entry)); - if (pcur->index()->is_spatial()) { - if (rtr_pcur_open(pcur->index(), entry, mode, pcur, mtr)) { - return ROW_NOT_FOUND; - } - } else { - if (btr_pcur_open(entry, PAGE_CUR_LE, mode, pcur, 0, mtr) - != DB_SUCCESS) { - return ROW_NOT_FOUND; - } + if (btr_pcur_open(entry, PAGE_CUR_LE, mode, pcur, mtr) != DB_SUCCESS) { + return ROW_NOT_FOUND; } switch (btr_pcur_get_btr_cur(pcur)->flag) { case BTR_CUR_DELETE_REF: ut_ad(!(~mode & BTR_DELETE)); - ut_ad(!pcur->index()->is_spatial()); return(ROW_NOT_DELETED_REF); case BTR_CUR_DEL_MARK_IBUF: diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 87e3ca43b1c..e44cc466295 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -2,7 +2,7 @@ Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. -Copyright (c) 2015, 2022, MariaDB Corporation. +Copyright (c) 2015, 2023, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -4776,7 +4776,7 @@ wait_table_again: pcur->btr_cur.thr = thr; pcur->old_rec = nullptr; - if (dict_index_is_spatial(index)) { + if (index->is_spatial()) { if (!prebuilt->rtr_info) { prebuilt->rtr_info = rtr_create_rtr_info( set_also_gap_locks, true, @@ -4792,10 +4792,13 @@ wait_table_again: prebuilt->rtr_info->search_tuple = search_tuple; prebuilt->rtr_info->search_mode = mode; } - } - err = btr_pcur_open_with_no_init(search_tuple, mode, - BTR_SEARCH_LEAF, pcur, &mtr); + err = rtr_search_leaf(pcur, search_tuple, mode, &mtr); + } else { + err = btr_pcur_open_with_no_init(search_tuple, mode, + BTR_SEARCH_LEAF, + pcur, &mtr); + } if (err != DB_SUCCESS) { page_corrupted: @@ -5771,8 +5774,7 @@ next_rec_after_check: if (spatial_search) { /* No need to do store restore for R-tree */ - mtr.commit(); - mtr.start(); + mtr.rollback_to_savepoint(0); } else if (mtr_extra_clust_savepoint) { /* We must release any clustered index latches if we are moving to the next non-clustered diff --git a/storage/innobase/row/row0uins.cc b/storage/innobase/row/row0uins.cc index 6567019a33d..50196e78092 100644 --- a/storage/innobase/row/row0uins.cc +++ b/storage/innobase/row/row0uins.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2022, MariaDB Corporation. +Copyright (c) 2017, 2023, 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 @@ -233,7 +233,7 @@ func_exit: if (err == DB_SUCCESS && node->rec_type == TRX_UNDO_INSERT_METADATA) { /* When rolling back the very first instant ADD COLUMN operation, reset the root page to the basic state. */ - err = btr_reset_instant(*index, true, &mtr); + btr_reset_instant(*index, true, &mtr); } btr_pcur_commit_specify_mtr(&node->pcur, &mtr); @@ -268,21 +268,32 @@ row_undo_ins_remove_sec_low( pcur.btr_cur.page_cur.index = index; row_mtr_start(&mtr, index, !modify_leaf); - if (modify_leaf) { - mode = BTR_MODIFY_LEAF_ALREADY_LATCHED; - mtr_s_lock_index(index, &mtr); - } else { - ut_ad(mode == BTR_PURGE_TREE); - mtr_sx_lock_index(index, &mtr); - } - if (index->is_spatial()) { mode = modify_leaf - ? btr_latch_mode(BTR_MODIFY_LEAF_ALREADY_LATCHED + ? btr_latch_mode(BTR_MODIFY_LEAF | BTR_RTREE_DELETE_MARK | BTR_RTREE_UNDO_INS) : btr_latch_mode(BTR_PURGE_TREE | BTR_RTREE_UNDO_INS); btr_pcur_get_btr_cur(&pcur)->thr = thr; + if (rtr_search(entry, mode, &pcur, &mtr)) { + goto func_exit; + } + + if (rec_get_deleted_flag( + btr_pcur_get_rec(&pcur), + dict_table_is_comp(index->table))) { + ib::error() << "Record found in index " << index->name + << " is deleted marked on insert rollback."; + ut_ad(0); + } + goto found; + } else if (modify_leaf) { + mode = BTR_MODIFY_LEAF_ALREADY_LATCHED; + mtr_s_lock_index(index, &mtr); + } else { + ut_ad(mode == BTR_PURGE_TREE); + mode = BTR_PURGE_TREE_ALREADY_LATCHED; + mtr_x_lock_index(index, &mtr); } switch (row_search_index_entry(entry, mode, &pcur, &mtr)) { @@ -295,15 +306,7 @@ row_undo_ins_remove_sec_low( case ROW_NOT_FOUND: break; case ROW_FOUND: - if (dict_index_is_spatial(index) - && rec_get_deleted_flag( - btr_pcur_get_rec(&pcur), - dict_table_is_comp(index->table))) { - ib::error() << "Record found in index " << index->name - << " is deleted marked on insert rollback."; - ut_ad(0); - } - + found: btr_cur_t* btr_cur = btr_pcur_get_btr_cur(&pcur); if (modify_leaf) { @@ -318,6 +321,7 @@ row_undo_ins_remove_sec_low( } } +func_exit: btr_pcur_close(&pcur); mtr_commit(&mtr); diff --git a/storage/innobase/row/row0umod.cc b/storage/innobase/row/row0umod.cc index 2d04dca4003..50e15e03cc9 100644 --- a/storage/innobase/row/row0umod.cc +++ b/storage/innobase/row/row0umod.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2022, MariaDB Corporation. +Copyright (c) 2017, 2023, 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 @@ -133,8 +133,7 @@ row_undo_mod_clust_low( && node->ref == &trx_undo_metadata && btr_cur_get_index(btr_cur)->table->instant && node->update->info_bits == REC_INFO_METADATA_ADD) { - err = btr_reset_instant(*btr_cur_get_index(btr_cur), - false, mtr); + btr_reset_instant(*btr_cur->index(), false, mtr); } } @@ -490,7 +489,6 @@ row_undo_mod_del_mark_or_remove_sec_low( dberr_t err = DB_SUCCESS; mtr_t mtr; mtr_t mtr_vers; - row_search_result search_result; const bool modify_leaf = mode == BTR_MODIFY_LEAF; row_mtr_start(&mtr, index, !modify_leaf); @@ -505,6 +503,11 @@ row_undo_mod_del_mark_or_remove_sec_low( | BTR_RTREE_UNDO_INS) : btr_latch_mode(BTR_PURGE_TREE | BTR_RTREE_UNDO_INS); btr_cur->thr = thr; + if (UNIV_LIKELY(!rtr_search(entry, mode, &pcur, &mtr))) { + goto found; + } else { + goto func_exit; + } } else if (!index->is_committed()) { /* The index->online_status may change if the index is or was being created online, but not committed yet. It @@ -514,7 +517,8 @@ row_undo_mod_del_mark_or_remove_sec_low( mtr_s_lock_index(index, &mtr); } else { ut_ad(mode == BTR_PURGE_TREE); - mtr_sx_lock_index(index, &mtr); + mode = BTR_PURGE_TREE_ALREADY_LATCHED; + mtr_x_lock_index(index, &mtr); } } else { /* For secondary indexes, @@ -523,9 +527,8 @@ row_undo_mod_del_mark_or_remove_sec_low( ut_ad(!dict_index_is_online_ddl(index)); } - search_result = row_search_index_entry(entry, mode, &pcur, &mtr); - - switch (UNIV_EXPECT(search_result, ROW_FOUND)) { + switch (UNIV_EXPECT(row_search_index_entry(entry, mode, &pcur, &mtr), + ROW_FOUND)) { case ROW_NOT_FOUND: /* In crash recovery, the secondary index record may be missing if the UPDATE did not have time to insert @@ -547,6 +550,7 @@ row_undo_mod_del_mark_or_remove_sec_low( ut_error; } +found: /* We should remove the index record if no prior version of the row, which cannot be purged yet, requires its existence. If some requires, we should delete mark the record. */ @@ -665,13 +669,12 @@ row_undo_mod_del_unmark_sec_and_undo_update( trx_t* trx = thr_get_trx(thr); const ulint flags = BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG; - row_search_result search_result; const auto orig_mode = mode; pcur.btr_cur.page_cur.index = index; ut_ad(trx->id != 0); - if (dict_index_is_spatial(index)) { + if (index->is_spatial()) { /* FIXME: Currently we do a 2-pass search for the undo due to avoid undel-mark a wrong rec in rolling back in partial update. Later, we could log some info in @@ -686,9 +689,22 @@ try_again: btr_cur->thr = thr; - search_result = row_search_index_entry(entry, mode, &pcur, &mtr); + if (index->is_spatial()) { + if (!rtr_search(entry, mode, &pcur, &mtr)) { + goto found; + } - switch (search_result) { + if (mode != orig_mode && btr_cur->rtr_info->fd_del) { + mode = orig_mode; + btr_pcur_close(&pcur); + mtr.commit(); + goto try_again; + } + + goto not_found; + } + + switch (row_search_index_entry(entry, mode, &pcur, &mtr)) { mem_heap_t* heap; mem_heap_t* offsets_heap; rec_offs* offsets; @@ -699,17 +715,7 @@ try_again: flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */ ut_error; case ROW_NOT_FOUND: - /* For spatial index, if first search didn't find an - undel-marked rec, try to find a del-marked rec. */ - if (dict_index_is_spatial(index) && btr_cur->rtr_info->fd_del) { - if (mode != orig_mode) { - mode = orig_mode; - btr_pcur_close(&pcur); - mtr_commit(&mtr); - goto try_again; - } - } - +not_found: if (btr_cur->up_match >= dict_index_get_n_unique(index) || btr_cur->low_match >= dict_index_get_n_unique(index)) { ib::warn() << "Record in index " << index->name @@ -767,6 +773,7 @@ try_again: break; case ROW_FOUND: +found: btr_rec_set_deleted(btr_cur_get_block(btr_cur), btr_cur_get_rec(btr_cur), &mtr); heap = mem_heap_create( diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc index a3f940adff5..fe88fce58a2 100644 --- a/storage/innobase/row/row0upd.cc +++ b/storage/innobase/row/row0upd.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2015, 2022, MariaDB Corporation. +Copyright (c) 2015, 2023, 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 @@ -1832,12 +1832,10 @@ row_upd_sec_index_entry( que_thr_t* thr) /*!< in: query thread */ { mtr_t mtr; - const rec_t* rec; btr_pcur_t pcur; mem_heap_t* heap; dtuple_t* entry; dict_index_t* index; - btr_cur_t* btr_cur; dberr_t err = DB_SUCCESS; trx_t* trx = thr_get_trx(thr); btr_latch_mode mode; @@ -1876,10 +1874,6 @@ row_upd_sec_index_entry( case SRV_TMP_SPACE_ID: mtr.set_log_mode(MTR_LOG_NO_REDO); flags = BTR_NO_LOCKING_FLAG; - if (index->is_spatial()) { - mode = btr_latch_mode(BTR_MODIFY_LEAF - | BTR_RTREE_DELETE_MARK); - } break; default: index->set_modified(mtr); @@ -1888,26 +1882,35 @@ row_upd_sec_index_entry( flags = index->table->no_rollback() ? BTR_NO_ROLLBACK : 0; /* We can only buffer delete-mark operations if there are no foreign key constraints referring to the index. */ - mode = index->is_spatial() - ? btr_latch_mode(BTR_MODIFY_LEAF - | BTR_RTREE_DELETE_MARK) - : referenced - ? BTR_MODIFY_LEAF : BTR_DELETE_MARK_LEAF; + if (!referenced) { + mode = BTR_DELETE_MARK_LEAF; + } break; } /* Set the query thread, so that ibuf_insert_low() will be able to invoke thd_get_trx(). */ - btr_pcur_get_btr_cur(&pcur)->thr = thr; + pcur.btr_cur.thr = thr; pcur.btr_cur.page_cur.index = index; - search_result = row_search_index_entry(entry, mode, &pcur, &mtr); + if (index->is_spatial()) { + mode = btr_latch_mode(BTR_MODIFY_LEAF | BTR_RTREE_DELETE_MARK); + if (UNIV_LIKELY(!rtr_search(entry, mode, &pcur, &mtr))) { + goto found; + } - btr_cur = btr_pcur_get_btr_cur(&pcur); + if (pcur.btr_cur.rtr_info->fd_del) { + /* We found the record, but a delete marked */ + goto close; + } - rec = btr_cur_get_rec(btr_cur); + goto not_found; + } + + search_result = row_search_index_entry(entry, mode, &pcur, &mtr); switch (search_result) { + const rec_t* rec; case ROW_NOT_DELETED_REF: /* should only occur for BTR_DELETE */ ut_error; break; @@ -1916,11 +1919,8 @@ row_upd_sec_index_entry( break; case ROW_NOT_FOUND: - if (dict_index_is_spatial(index) && btr_cur->rtr_info->fd_del) { - /* We found the record, but a delete marked */ - break; - } - +not_found: + rec = btr_pcur_get_rec(&pcur); ib::error() << "Record in index " << index->name << " of table " << index->table->name @@ -1934,7 +1934,9 @@ row_upd_sec_index_entry( #endif /* UNIV_DEBUG */ break; case ROW_FOUND: +found: ut_ad(err == DB_SUCCESS); + rec = btr_pcur_get_rec(&pcur); /* Delete mark the old index record; it can already be delete marked if we return after a lock wait in @@ -1943,14 +1945,14 @@ row_upd_sec_index_entry( rec, dict_table_is_comp(index->table))) { err = lock_sec_rec_modify_check_and_lock( flags, - btr_cur_get_block(btr_cur), - btr_cur_get_rec(btr_cur), index, thr, &mtr); + btr_pcur_get_block(&pcur), + btr_pcur_get_rec(&pcur), index, thr, &mtr); if (err != DB_SUCCESS) { break; } - btr_rec_set_deleted(btr_cur_get_block(btr_cur), - btr_cur_get_rec(btr_cur), + btr_rec_set_deleted(btr_pcur_get_block(&pcur), + btr_pcur_get_rec(&pcur), &mtr); #ifdef WITH_WSREP if (!referenced && foreign @@ -2009,6 +2011,7 @@ row_upd_sec_index_entry( } } +close: btr_pcur_close(&pcur); mtr_commit(&mtr); -- cgit v1.2.1 From 986276f01976693624256cff87318412d48b3298 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Fri, 20 Jan 2023 12:55:21 +0100 Subject: mysql-5.7.41 --- mysql-test/suite/perfschema/r/nesting.result | 258 +++++++++++++-------------- 1 file changed, 123 insertions(+), 135 deletions(-) diff --git a/mysql-test/suite/perfschema/r/nesting.result b/mysql-test/suite/perfschema/r/nesting.result index 97418476dc6..a8e8fc8fcb0 100644 --- a/mysql-test/suite/perfschema/r/nesting.result +++ b/mysql-test/suite/perfschema/r/nesting.result @@ -120,141 +120,129 @@ and (end_event_id <= @marker_end) ) all_events order by relative_event_id asc; relative_event_id relative_end_event_id event_name comment nesting_event_type relative_nesting_event_id -0 18 statement/sql/select select "MARKER_BEGIN" as marker NULL NULL -1 6 stage/sql/starting (stage) STATEMENT 0 +0 17 statement/sql/select select "MARKER_BEGIN" as marker NULL NULL +1 5 stage/sql/starting (stage) STATEMENT 0 2 2 wait/io/socket/sql/client_connection recv STAGE 1 3 3 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 1 4 4 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 1 -5 5 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 1 -6 6 wait/io/file/sql/query_log write STAGE 1 -7 7 stage/sql/checking permissions (stage) STATEMENT 0 -8 8 stage/sql/Opening tables (stage) STATEMENT 0 -9 9 stage/sql/init (stage) STATEMENT 0 -10 10 stage/sql/optimizing (stage) STATEMENT 0 -11 11 stage/sql/executing (stage) STATEMENT 0 -12 12 stage/sql/end (stage) STATEMENT 0 -13 13 stage/sql/query end (stage) STATEMENT 0 -14 14 stage/sql/closing tables (stage) STATEMENT 0 -15 16 stage/sql/freeing items (stage) STATEMENT 0 -16 16 wait/io/socket/sql/client_connection send STAGE 15 -17 18 stage/sql/cleaning up (stage) STATEMENT 0 -18 18 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 17 -19 19 wait/synch/mutex/sql/THD::LOCK_thd_query lock STATEMENT 0 -20 20 idle idle NULL NULL -21 39 statement/sql/select select "This is simple statement one" as payload NULL NULL -22 27 stage/sql/starting (stage) STATEMENT 21 -23 23 wait/io/socket/sql/client_connection recv STAGE 22 -24 24 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 22 -25 25 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 22 -26 26 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 22 -27 27 wait/io/file/sql/query_log write STAGE 22 -28 28 stage/sql/checking permissions (stage) STATEMENT 21 -29 29 stage/sql/Opening tables (stage) STATEMENT 21 -30 30 stage/sql/init (stage) STATEMENT 21 -31 31 stage/sql/optimizing (stage) STATEMENT 21 -32 32 stage/sql/executing (stage) STATEMENT 21 -33 33 stage/sql/end (stage) STATEMENT 21 -34 34 stage/sql/query end (stage) STATEMENT 21 -35 35 stage/sql/closing tables (stage) STATEMENT 21 -36 37 stage/sql/freeing items (stage) STATEMENT 21 -37 37 wait/io/socket/sql/client_connection send STAGE 36 -38 39 stage/sql/cleaning up (stage) STATEMENT 21 -39 39 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 38 -40 40 wait/synch/mutex/sql/THD::LOCK_thd_query lock STATEMENT 21 -41 41 idle idle NULL NULL -42 60 statement/sql/select select "This is simple statement two" as payload NULL NULL -43 48 stage/sql/starting (stage) STATEMENT 42 -44 44 wait/io/socket/sql/client_connection recv STAGE 43 -45 45 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 43 -46 46 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 43 -47 47 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 43 -48 48 wait/io/file/sql/query_log write STAGE 43 -49 49 stage/sql/checking permissions (stage) STATEMENT 42 -50 50 stage/sql/Opening tables (stage) STATEMENT 42 -51 51 stage/sql/init (stage) STATEMENT 42 -52 52 stage/sql/optimizing (stage) STATEMENT 42 -53 53 stage/sql/executing (stage) STATEMENT 42 -54 54 stage/sql/end (stage) STATEMENT 42 -55 55 stage/sql/query end (stage) STATEMENT 42 -56 56 stage/sql/closing tables (stage) STATEMENT 42 -57 58 stage/sql/freeing items (stage) STATEMENT 42 -58 58 wait/io/socket/sql/client_connection send STAGE 57 -59 60 stage/sql/cleaning up (stage) STATEMENT 42 -60 60 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 59 -61 61 wait/synch/mutex/sql/THD::LOCK_thd_query lock STATEMENT 42 -62 62 idle idle NULL NULL -63 80 statement/sql/select select "This is the first part of a multi query" as payload; -select "And this is the second part of a multi query" as payload; -select "With a third part to make things complete" as payload NULL NULL -64 70 stage/sql/starting (stage) STATEMENT 63 -65 65 wait/io/socket/sql/client_connection recv STAGE 64 -66 66 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 64 -67 67 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 64 -68 68 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 64 -69 69 wait/io/file/sql/query_log write STAGE 64 -70 70 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 64 -71 71 stage/sql/checking permissions (stage) STATEMENT 63 -72 72 stage/sql/Opening tables (stage) STATEMENT 63 -73 73 stage/sql/init (stage) STATEMENT 63 -74 74 stage/sql/optimizing (stage) STATEMENT 63 -75 75 stage/sql/executing (stage) STATEMENT 63 -76 76 stage/sql/end (stage) STATEMENT 63 -77 77 stage/sql/query end (stage) STATEMENT 63 -78 78 stage/sql/closing tables (stage) STATEMENT 63 -79 80 stage/sql/freeing items (stage) STATEMENT 63 -80 80 wait/io/socket/sql/client_connection send STAGE 79 -81 96 statement/sql/select select "And this is the second part of a multi query" as payload; -select "With a third part to make things complete" as payload NULL NULL -82 86 stage/sql/starting (stage) STATEMENT 81 -83 83 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 82 -84 84 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 82 -85 85 wait/io/file/sql/query_log write STAGE 82 -86 86 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 82 -87 87 stage/sql/checking permissions (stage) STATEMENT 81 -88 88 stage/sql/Opening tables (stage) STATEMENT 81 -89 89 stage/sql/init (stage) STATEMENT 81 -90 90 stage/sql/optimizing (stage) STATEMENT 81 -91 91 stage/sql/executing (stage) STATEMENT 81 -92 92 stage/sql/end (stage) STATEMENT 81 -93 93 stage/sql/query end (stage) STATEMENT 81 -94 94 stage/sql/closing tables (stage) STATEMENT 81 -95 96 stage/sql/freeing items (stage) STATEMENT 81 -96 96 wait/io/socket/sql/client_connection send STAGE 95 -97 113 statement/sql/select select "With a third part to make things complete" as payload NULL NULL -98 101 stage/sql/starting (stage) STATEMENT 97 -99 99 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 98 -100 100 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 98 -101 101 wait/io/file/sql/query_log write STAGE 98 -102 102 stage/sql/checking permissions (stage) STATEMENT 97 -103 103 stage/sql/Opening tables (stage) STATEMENT 97 -104 104 stage/sql/init (stage) STATEMENT 97 -105 105 stage/sql/optimizing (stage) STATEMENT 97 -106 106 stage/sql/executing (stage) STATEMENT 97 -107 107 stage/sql/end (stage) STATEMENT 97 -108 108 stage/sql/query end (stage) STATEMENT 97 -109 109 stage/sql/closing tables (stage) STATEMENT 97 -110 111 stage/sql/freeing items (stage) STATEMENT 97 -111 111 wait/io/socket/sql/client_connection send STAGE 110 -112 113 stage/sql/cleaning up (stage) STATEMENT 97 -113 113 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 112 -114 114 wait/synch/mutex/sql/THD::LOCK_thd_query lock STATEMENT 97 -115 115 idle idle NULL NULL -116 134 statement/sql/select select "MARKER_END" as marker NULL NULL -117 122 stage/sql/starting (stage) STATEMENT 116 -118 118 wait/io/socket/sql/client_connection recv STAGE 117 -119 119 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 117 -120 120 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 117 -121 121 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 117 -122 122 wait/io/file/sql/query_log write STAGE 117 -123 123 stage/sql/checking permissions (stage) STATEMENT 116 -124 124 stage/sql/Opening tables (stage) STATEMENT 116 -125 125 stage/sql/init (stage) STATEMENT 116 -126 126 stage/sql/optimizing (stage) STATEMENT 116 -127 127 stage/sql/executing (stage) STATEMENT 116 -128 128 stage/sql/end (stage) STATEMENT 116 -129 129 stage/sql/query end (stage) STATEMENT 116 -130 130 stage/sql/closing tables (stage) STATEMENT 116 -131 132 stage/sql/freeing items (stage) STATEMENT 116 -132 132 wait/io/socket/sql/client_connection send STAGE 131 -133 134 stage/sql/cleaning up (stage) STATEMENT 116 -134 134 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 133 +5 5 wait/io/file/sql/query_log write STAGE 1 +6 6 stage/sql/checking permissions (stage) STATEMENT 0 +7 7 stage/sql/Opening tables (stage) STATEMENT 0 +8 8 stage/sql/init (stage) STATEMENT 0 +9 9 stage/sql/optimizing (stage) STATEMENT 0 +10 10 stage/sql/executing (stage) STATEMENT 0 +11 11 stage/sql/end (stage) STATEMENT 0 +12 12 stage/sql/query end (stage) STATEMENT 0 +13 13 stage/sql/closing tables (stage) STATEMENT 0 +14 15 stage/sql/freeing items (stage) STATEMENT 0 +15 15 wait/io/socket/sql/client_connection send STAGE 14 +16 17 stage/sql/cleaning up (stage) STATEMENT 0 +17 17 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 16 +18 18 idle idle NULL NULL +19 36 statement/sql/select select "This is simple statement one" as payload NULL NULL +20 24 stage/sql/starting (stage) STATEMENT 19 +21 21 wait/io/socket/sql/client_connection recv STAGE 20 +22 22 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 20 +23 23 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 20 +24 24 wait/io/file/sql/query_log write STAGE 20 +25 25 stage/sql/checking permissions (stage) STATEMENT 19 +26 26 stage/sql/Opening tables (stage) STATEMENT 19 +27 27 stage/sql/init (stage) STATEMENT 19 +28 28 stage/sql/optimizing (stage) STATEMENT 19 +29 29 stage/sql/executing (stage) STATEMENT 19 +30 30 stage/sql/end (stage) STATEMENT 19 +31 31 stage/sql/query end (stage) STATEMENT 19 +32 32 stage/sql/closing tables (stage) STATEMENT 19 +33 34 stage/sql/freeing items (stage) STATEMENT 19 +34 34 wait/io/socket/sql/client_connection send STAGE 33 +35 36 stage/sql/cleaning up (stage) STATEMENT 19 +36 36 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 35 +37 37 idle idle NULL NULL +38 55 statement/sql/select select "This is simple statement two" as payload NULL NULL +39 43 stage/sql/starting (stage) STATEMENT 38 +40 40 wait/io/socket/sql/client_connection recv STAGE 39 +41 41 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 39 +42 42 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 39 +43 43 wait/io/file/sql/query_log write STAGE 39 +44 44 stage/sql/checking permissions (stage) STATEMENT 38 +45 45 stage/sql/Opening tables (stage) STATEMENT 38 +46 46 stage/sql/init (stage) STATEMENT 38 +47 47 stage/sql/optimizing (stage) STATEMENT 38 +48 48 stage/sql/executing (stage) STATEMENT 38 +49 49 stage/sql/end (stage) STATEMENT 38 +50 50 stage/sql/query end (stage) STATEMENT 38 +51 51 stage/sql/closing tables (stage) STATEMENT 38 +52 53 stage/sql/freeing items (stage) STATEMENT 38 +53 53 wait/io/socket/sql/client_connection send STAGE 52 +54 55 stage/sql/cleaning up (stage) STATEMENT 38 +55 55 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 54 +56 56 idle idle NULL NULL +57 73 statement/sql/select select "This is the first part of a multi query" as payload NULL NULL +58 63 stage/sql/starting (stage) STATEMENT 57 +59 59 wait/io/socket/sql/client_connection recv STAGE 58 +60 60 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 58 +61 61 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 58 +62 62 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 58 +63 63 wait/io/file/sql/query_log write STAGE 58 +64 64 stage/sql/checking permissions (stage) STATEMENT 57 +65 65 stage/sql/Opening tables (stage) STATEMENT 57 +66 66 stage/sql/init (stage) STATEMENT 57 +67 67 stage/sql/optimizing (stage) STATEMENT 57 +68 68 stage/sql/executing (stage) STATEMENT 57 +69 69 stage/sql/end (stage) STATEMENT 57 +70 70 stage/sql/query end (stage) STATEMENT 57 +71 71 stage/sql/closing tables (stage) STATEMENT 57 +72 73 stage/sql/freeing items (stage) STATEMENT 57 +73 73 wait/io/socket/sql/client_connection send STAGE 72 +74 89 statement/sql/select select "And this is the second part of a multi query" as payload NULL NULL +75 79 stage/sql/starting (stage) STATEMENT 74 +76 76 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 75 +77 77 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 75 +78 78 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 75 +79 79 wait/io/file/sql/query_log write STAGE 75 +80 80 stage/sql/checking permissions (stage) STATEMENT 74 +81 81 stage/sql/Opening tables (stage) STATEMENT 74 +82 82 stage/sql/init (stage) STATEMENT 74 +83 83 stage/sql/optimizing (stage) STATEMENT 74 +84 84 stage/sql/executing (stage) STATEMENT 74 +85 85 stage/sql/end (stage) STATEMENT 74 +86 86 stage/sql/query end (stage) STATEMENT 74 +87 87 stage/sql/closing tables (stage) STATEMENT 74 +88 89 stage/sql/freeing items (stage) STATEMENT 74 +89 89 wait/io/socket/sql/client_connection send STAGE 88 +90 106 statement/sql/select select "With a third part to make things complete" as payload NULL NULL +91 94 stage/sql/starting (stage) STATEMENT 90 +92 92 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 91 +93 93 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 91 +94 94 wait/io/file/sql/query_log write STAGE 91 +95 95 stage/sql/checking permissions (stage) STATEMENT 90 +96 96 stage/sql/Opening tables (stage) STATEMENT 90 +97 97 stage/sql/init (stage) STATEMENT 90 +98 98 stage/sql/optimizing (stage) STATEMENT 90 +99 99 stage/sql/executing (stage) STATEMENT 90 +100 100 stage/sql/end (stage) STATEMENT 90 +101 101 stage/sql/query end (stage) STATEMENT 90 +102 102 stage/sql/closing tables (stage) STATEMENT 90 +103 104 stage/sql/freeing items (stage) STATEMENT 90 +104 104 wait/io/socket/sql/client_connection send STAGE 103 +105 106 stage/sql/cleaning up (stage) STATEMENT 90 +106 106 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 105 +107 107 idle idle NULL NULL +108 125 statement/sql/select select "MARKER_END" as marker NULL NULL +109 113 stage/sql/starting (stage) STATEMENT 108 +110 110 wait/io/socket/sql/client_connection recv STAGE 109 +111 111 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 109 +112 112 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 109 +113 113 wait/io/file/sql/query_log write STAGE 109 +114 114 stage/sql/checking permissions (stage) STATEMENT 108 +115 115 stage/sql/Opening tables (stage) STATEMENT 108 +116 116 stage/sql/init (stage) STATEMENT 108 +117 117 stage/sql/optimizing (stage) STATEMENT 108 +118 118 stage/sql/executing (stage) STATEMENT 108 +119 119 stage/sql/end (stage) STATEMENT 108 +120 120 stage/sql/query end (stage) STATEMENT 108 +121 121 stage/sql/closing tables (stage) STATEMENT 108 +122 123 stage/sql/freeing items (stage) STATEMENT 108 +123 123 wait/io/socket/sql/client_connection send STAGE 122 +124 125 stage/sql/cleaning up (stage) STATEMENT 108 +125 125 wait/synch/mutex/sql/THD::LOCK_thd_query lock STAGE 124 -- cgit v1.2.1 From 647a7232ff12361a271c33223f1e1bbee6da5675 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Fri, 20 Jan 2023 16:07:43 +0530 Subject: MDEV-30438 innodb.undo_truncate,4k fails when innodb-immediate-scrub-data-uncompressed is enabled - InnoDB fails to clear the freed ranges during truncation of innodb undo log tablespace. During shutdown, InnoDB flushes the freed page ranges and throws the out of bound error. mtr_t::commit_shrink(): clear the freed ranges while doing undo tablespace truncation --- mysql-test/suite/innodb/t/undo_truncate.opt | 1 + storage/innobase/mtr/mtr0mtr.cc | 23 +++-------------------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/mysql-test/suite/innodb/t/undo_truncate.opt b/mysql-test/suite/innodb/t/undo_truncate.opt index f4d78725c6e..1459ec5db74 100644 --- a/mysql-test/suite/innodb/t/undo_truncate.opt +++ b/mysql-test/suite/innodb/t/undo_truncate.opt @@ -1 +1,2 @@ --innodb-buffer-pool-size=24M +--innodb-immediate-scrub-data-uncompressed=ON diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc index 0aa5b9a4c3f..7f4812e158a 100644 --- a/storage/innobase/mtr/mtr0mtr.cc +++ b/storage/innobase/mtr/mtr0mtr.cc @@ -579,26 +579,9 @@ void mtr_t::commit_shrink(fil_space_t &space) /* Durably write the reduced FSP_SIZE before truncating the data file. */ log_write_and_flush(); - if (m_freed_pages) - { - ut_ad(!m_freed_pages->empty()); - ut_ad(m_freed_space == &space); - ut_ad(memo_contains(*m_freed_space)); - ut_ad(is_named_space(m_freed_space)); - m_freed_space->update_last_freed_lsn(m_commit_lsn); - - if (!is_trim_pages()) - for (const auto &range : *m_freed_pages) - m_freed_space->add_free_range(range); - else - m_freed_space->clear_freed_ranges(); - delete m_freed_pages; - m_freed_pages= nullptr; - m_freed_space= nullptr; - /* mtr_t::start() will reset m_trim_pages */ - } - else - ut_ad(!m_freed_space); + ut_ad(!m_freed_pages); + + space.clear_freed_ranges(); m_memo.for_each_block_in_reverse(CIterate{space}); -- cgit v1.2.1 From 1bbf37e0db459f6368b6a48e366e3f2558853f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 23 Jan 2023 13:05:52 +0200 Subject: MDEV-515: Improve test coverage Cover dict_index_t::clear() for TEMPORARY TABLE --- mysql-test/suite/innodb/r/insert_into_empty.result | 4 ++++ mysql-test/suite/innodb/t/insert_into_empty.test | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/mysql-test/suite/innodb/r/insert_into_empty.result b/mysql-test/suite/innodb/r/insert_into_empty.result index eeff9428a49..e7f051d3e56 100644 --- a/mysql-test/suite/innodb/r/insert_into_empty.result +++ b/mysql-test/suite/innodb/r/insert_into_empty.result @@ -45,6 +45,10 @@ SET tx_read_only=1; BEGIN; INSERT INTO t2 VALUES(0); INSERT INTO t VALUES(0); +ROLLBACK; +BEGIN; +INSERT INTO t2 VALUES(0); +INSERT INTO t VALUES(0); COMMIT; INSERT INTO t VALUES(0); DROP TEMPORARY TABLE t,t2; diff --git a/mysql-test/suite/innodb/t/insert_into_empty.test b/mysql-test/suite/innodb/t/insert_into_empty.test index 58b3cdce335..af8ed8be90d 100644 --- a/mysql-test/suite/innodb/t/insert_into_empty.test +++ b/mysql-test/suite/innodb/t/insert_into_empty.test @@ -51,6 +51,10 @@ SET tx_read_only=1; BEGIN; INSERT INTO t2 VALUES(0); INSERT INTO t VALUES(0); +ROLLBACK; +BEGIN; +INSERT INTO t2 VALUES(0); +INSERT INTO t VALUES(0); COMMIT; INSERT INTO t VALUES(0); -- cgit v1.2.1 From e41fb3697c9f10b3db2e35b6a5704a03b1041c7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 23 Jan 2023 14:52:49 +0200 Subject: Revert "MDEV-30400 Assertion height == btr_page_get_level(...) on INSERT" This reverts commit f9cac8d2cbf82d4d616905fb3dfab34a9901179d which was accidentally pushed prematurely. --- mysql-test/suite/innodb/r/innodb_wl6326.result | 405 ++++ mysql-test/suite/innodb/t/innodb_wl6326.opt | 1 + mysql-test/suite/innodb/t/innodb_wl6326.test | 519 +++++ mysql-test/suite/innodb_gis/r/rtree_split.result | 12 - mysql-test/suite/innodb_gis/t/rtree_split.test | 15 - storage/innobase/btr/btr0btr.cc | 498 ++--- storage/innobase/btr/btr0cur.cc | 2268 ++++++++++++++-------- storage/innobase/btr/btr0defragment.cc | 68 +- storage/innobase/btr/btr0pcur.cc | 109 +- storage/innobase/btr/btr0sea.cc | 22 +- storage/innobase/dict/dict0crea.cc | 29 +- storage/innobase/dict/dict0dict.cc | 8 +- storage/innobase/dict/dict0load.cc | 23 +- storage/innobase/dict/dict0stats.cc | 4 +- storage/innobase/fsp/fsp0fsp.cc | 25 +- storage/innobase/gis/gis0sea.cc | 634 ++---- storage/innobase/handler/ha_innodb.cc | 3 +- storage/innobase/handler/handler0alter.cc | 9 +- storage/innobase/ibuf/ibuf0ibuf.cc | 24 +- storage/innobase/include/btr0btr.h | 11 +- storage/innobase/include/btr0cur.h | 89 +- storage/innobase/include/btr0pcur.h | 49 +- storage/innobase/include/btr0pcur.inl | 40 +- storage/innobase/include/btr0types.h | 32 +- storage/innobase/include/gis0rtree.h | 46 +- storage/innobase/include/gis0type.h | 8 +- storage/innobase/include/ibuf0ibuf.inl | 3 +- storage/innobase/include/mtr0log.h | 6 +- storage/innobase/include/mtr0mtr.h | 84 +- storage/innobase/include/small_vector.h | 3 - storage/innobase/mtr/mtr0mtr.cc | 39 +- storage/innobase/row/row0import.cc | 5 +- storage/innobase/row/row0ins.cc | 104 +- storage/innobase/row/row0log.cc | 33 +- storage/innobase/row/row0merge.cc | 18 +- storage/innobase/row/row0purge.cc | 46 +- storage/innobase/row/row0row.cc | 16 +- storage/innobase/row/row0sel.cc | 16 +- storage/innobase/row/row0uins.cc | 44 +- storage/innobase/row/row0umod.cc | 53 +- storage/innobase/row/row0upd.cc | 53 +- 41 files changed, 3158 insertions(+), 2316 deletions(-) create mode 100644 mysql-test/suite/innodb/r/innodb_wl6326.result create mode 100644 mysql-test/suite/innodb/t/innodb_wl6326.opt create mode 100644 mysql-test/suite/innodb/t/innodb_wl6326.test diff --git a/mysql-test/suite/innodb/r/innodb_wl6326.result b/mysql-test/suite/innodb/r/innodb_wl6326.result new file mode 100644 index 00000000000..fcd58aedafe --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_wl6326.result @@ -0,0 +1,405 @@ +SET GLOBAL innodb_adaptive_hash_index = false; +SET GLOBAL innodb_stats_persistent = false; +connect con1,localhost,root,,; +connect con2,localhost,root,,; +connect con3,localhost,root,,; +CREATE TABLE t1 ( +a00 CHAR(255) NOT NULL DEFAULT 'a', +a01 CHAR(255) NOT NULL DEFAULT 'a', +a02 CHAR(255) NOT NULL DEFAULT 'a', +a03 CHAR(255) NOT NULL DEFAULT 'a', +a04 CHAR(255) NOT NULL DEFAULT 'a', +a05 CHAR(255) NOT NULL DEFAULT 'a', +a06 CHAR(255) NOT NULL DEFAULT 'a', +b INT NOT NULL DEFAULT 0 +) ENGINE = InnoDB; +ALTER TABLE t1 ADD PRIMARY KEY( +a00, +a01, +a02, +a03, +a04, +a05, +a06 +); +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; +CLUST_INDEX_SIZE +1 +SET GLOBAL innodb_limit_optimistic_insert_debug = 7; +BEGIN; +INSERT INTO t1 (a00) VALUES ('aa'); +INSERT INTO t1 (a00) VALUES ('ab'); +INSERT INTO t1 (a00) VALUES ('ac'); +INSERT INTO t1 (a00) VALUES ('ad'); +INSERT INTO t1 (a00) VALUES ('ae'); +INSERT INTO t1 (a00) VALUES ('af'); +INSERT INTO t1 (a00) VALUES ('ag'); +INSERT INTO t1 (a00) VALUES ('ah'); +COMMIT; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; +CLUST_INDEX_SIZE +3 +BEGIN; +INSERT INTO t1 (a00) VALUES ('ai'); +INSERT INTO t1 (a00) VALUES ('aj'); +INSERT INTO t1 (a00) VALUES ('ak'); +COMMIT; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; +CLUST_INDEX_SIZE +4 +BEGIN; +INSERT INTO t1 (a00) VALUES ('al'); +INSERT INTO t1 (a00) VALUES ('am'); +INSERT INTO t1 (a00) VALUES ('an'); +INSERT INTO t1 (a00) VALUES ('ao'); +INSERT INTO t1 (a00) VALUES ('ap'); +INSERT INTO t1 (a00) VALUES ('aq'); +INSERT INTO t1 (a00) VALUES ('ar'); +COMMIT; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; +CLUST_INDEX_SIZE +5 +BEGIN; +INSERT INTO t1 (a00) VALUES ('as'); +INSERT INTO t1 (a00) VALUES ('at'); +INSERT INTO t1 (a00) VALUES ('au'); +INSERT INTO t1 (a00) VALUES ('av'); +INSERT INTO t1 (a00) VALUES ('aw'); +INSERT INTO t1 (a00) VALUES ('ax'); +INSERT INTO t1 (a00) VALUES ('ay'); +COMMIT; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; +CLUST_INDEX_SIZE +6 +BEGIN; +INSERT INTO t1 (a00) VALUES ('az'); +INSERT INTO t1 (a00) VALUES ('ba'); +INSERT INTO t1 (a00) VALUES ('bb'); +INSERT INTO t1 (a00) VALUES ('bc'); +INSERT INTO t1 (a00) VALUES ('bd'); +INSERT INTO t1 (a00) VALUES ('be'); +INSERT INTO t1 (a00) VALUES ('bf'); +COMMIT; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; +CLUST_INDEX_SIZE +7 +BEGIN; +INSERT INTO t1 (a00) VALUES ('bg'); +INSERT INTO t1 (a00) VALUES ('bh'); +INSERT INTO t1 (a00) VALUES ('bi'); +INSERT INTO t1 (a00) VALUES ('bj'); +INSERT INTO t1 (a00) VALUES ('bk'); +INSERT INTO t1 (a00) VALUES ('bl'); +INSERT INTO t1 (a00) VALUES ('bm'); +COMMIT; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; +CLUST_INDEX_SIZE +8 +BEGIN; +INSERT INTO t1 (a00) VALUES ('bn'); +INSERT INTO t1 (a00) VALUES ('bo'); +INSERT INTO t1 (a00) VALUES ('bp'); +INSERT INTO t1 (a00) VALUES ('bq'); +INSERT INTO t1 (a00) VALUES ('br'); +INSERT INTO t1 (a00) VALUES ('bs'); +INSERT INTO t1 (a00) VALUES ('bt'); +COMMIT; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; +CLUST_INDEX_SIZE +11 +BEGIN; +INSERT INTO t1 (a00) VALUES ('bu'); +INSERT INTO t1 (a00) VALUES ('bv'); +INSERT INTO t1 (a00) VALUES ('bw'); +INSERT INTO t1 (a00) VALUES ('bx'); +INSERT INTO t1 (a00) VALUES ('by'); +INSERT INTO t1 (a00) VALUES ('bz'); +INSERT INTO t1 (a00) VALUES ('ca'); +INSERT INTO t1 (a00) VALUES ('cb'); +INSERT INTO t1 (a00) VALUES ('cc'); +INSERT INTO t1 (a00) VALUES ('cd'); +INSERT INTO t1 (a00) VALUES ('ce'); +INSERT INTO t1 (a00) VALUES ('cf'); +INSERT INTO t1 (a00) VALUES ('cg'); +INSERT INTO t1 (a00) VALUES ('ch'); +INSERT INTO t1 (a00) VALUES ('ci'); +INSERT INTO t1 (a00) VALUES ('cj'); +INSERT INTO t1 (a00) VALUES ('ck'); +INSERT INTO t1 (a00) VALUES ('cl'); +INSERT INTO t1 (a00) VALUES ('cm'); +INSERT INTO t1 (a00) VALUES ('cn'); +INSERT INTO t1 (a00) VALUES ('co'); +COMMIT; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; +CLUST_INDEX_SIZE +15 +BEGIN; +INSERT INTO t1 (a00) VALUES ('cp'); +INSERT INTO t1 (a00) VALUES ('cq'); +INSERT INTO t1 (a00) VALUES ('cr'); +INSERT INTO t1 (a00) VALUES ('cs'); +INSERT INTO t1 (a00) VALUES ('ct'); +INSERT INTO t1 (a00) VALUES ('cu'); +INSERT INTO t1 (a00) VALUES ('cv'); +INSERT INTO t1 (a00) VALUES ('cw'); +INSERT INTO t1 (a00) VALUES ('cx'); +INSERT INTO t1 (a00) VALUES ('cy'); +INSERT INTO t1 (a00) VALUES ('cz'); +INSERT INTO t1 (a00) VALUES ('da'); +INSERT INTO t1 (a00) VALUES ('db'); +INSERT INTO t1 (a00) VALUES ('dc'); +INSERT INTO t1 (a00) VALUES ('dd'); +INSERT INTO t1 (a00) VALUES ('de'); +INSERT INTO t1 (a00) VALUES ('df'); +INSERT INTO t1 (a00) VALUES ('dg'); +INSERT INTO t1 (a00) VALUES ('dh'); +INSERT INTO t1 (a00) VALUES ('di'); +INSERT INTO t1 (a00) VALUES ('dj'); +INSERT INTO t1 (a00) VALUES ('dk'); +INSERT INTO t1 (a00) VALUES ('dl'); +INSERT INTO t1 (a00) VALUES ('dm'); +INSERT INTO t1 (a00) VALUES ('dn'); +INSERT INTO t1 (a00) VALUES ('do'); +INSERT INTO t1 (a00) VALUES ('dp'); +INSERT INTO t1 (a00) VALUES ('dq'); +INSERT INTO t1 (a00) VALUES ('dr'); +INSERT INTO t1 (a00) VALUES ('ds'); +INSERT INTO t1 (a00) VALUES ('dt'); +INSERT INTO t1 (a00) VALUES ('du'); +INSERT INTO t1 (a00) VALUES ('dv'); +INSERT INTO t1 (a00) VALUES ('dw'); +INSERT INTO t1 (a00) VALUES ('dx'); +INSERT INTO t1 (a00) VALUES ('dy'); +INSERT INTO t1 (a00) VALUES ('dz'); +INSERT INTO t1 (a00) VALUES ('ea'); +INSERT INTO t1 (a00) VALUES ('eb'); +INSERT INTO t1 (a00) VALUES ('ec'); +INSERT INTO t1 (a00) VALUES ('ed'); +INSERT INTO t1 (a00) VALUES ('ee'); +INSERT INTO t1 (a00) VALUES ('ef'); +INSERT INTO t1 (a00) VALUES ('eg'); +INSERT INTO t1 (a00) VALUES ('eh'); +INSERT INTO t1 (a00) VALUES ('ei'); +INSERT INTO t1 (a00) VALUES ('ej'); +INSERT INTO t1 (a00) VALUES ('ek'); +INSERT INTO t1 (a00) VALUES ('el'); +COMMIT; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; +CLUST_INDEX_SIZE +23 +BEGIN; +INSERT INTO t1 (a00) VALUES ('em'); +INSERT INTO t1 (a00) VALUES ('en'); +INSERT INTO t1 (a00) VALUES ('eo'); +INSERT INTO t1 (a00) VALUES ('ep'); +INSERT INTO t1 (a00) VALUES ('eq'); +INSERT INTO t1 (a00) VALUES ('er'); +INSERT INTO t1 (a00) VALUES ('es'); +INSERT INTO t1 (a00) VALUES ('et'); +INSERT INTO t1 (a00) VALUES ('eu'); +INSERT INTO t1 (a00) VALUES ('ev'); +INSERT INTO t1 (a00) VALUES ('ew'); +INSERT INTO t1 (a00) VALUES ('ex'); +INSERT INTO t1 (a00) VALUES ('ey'); +INSERT INTO t1 (a00) VALUES ('ez'); +INSERT INTO t1 (a00) VALUES ('fa'); +INSERT INTO t1 (a00) VALUES ('fb'); +INSERT INTO t1 (a00) VALUES ('fc'); +INSERT INTO t1 (a00) VALUES ('fd'); +INSERT INTO t1 (a00) VALUES ('fe'); +INSERT INTO t1 (a00) VALUES ('ff'); +INSERT INTO t1 (a00) VALUES ('fg'); +INSERT INTO t1 (a00) VALUES ('fh'); +INSERT INTO t1 (a00) VALUES ('fi'); +INSERT INTO t1 (a00) VALUES ('fj'); +INSERT INTO t1 (a00) VALUES ('fk'); +INSERT INTO t1 (a00) VALUES ('fl'); +INSERT INTO t1 (a00) VALUES ('fm'); +INSERT INTO t1 (a00) VALUES ('fn'); +INSERT INTO t1 (a00) VALUES ('fo'); +INSERT INTO t1 (a00) VALUES ('fp'); +INSERT INTO t1 (a00) VALUES ('fq'); +INSERT INTO t1 (a00) VALUES ('fr'); +INSERT INTO t1 (a00) VALUES ('fs'); +INSERT INTO t1 (a00) VALUES ('ft'); +INSERT INTO t1 (a00) VALUES ('fu'); +INSERT INTO t1 (a00) VALUES ('fv'); +INSERT INTO t1 (a00) VALUES ('fw'); +INSERT INTO t1 (a00) VALUES ('fx'); +INSERT INTO t1 (a00) VALUES ('fy'); +INSERT INTO t1 (a00) VALUES ('fz'); +INSERT INTO t1 (a00) VALUES ('ga'); +INSERT INTO t1 (a00) VALUES ('gb'); +INSERT INTO t1 (a00) VALUES ('gc'); +INSERT INTO t1 (a00) VALUES ('gd'); +INSERT INTO t1 (a00) VALUES ('ge'); +INSERT INTO t1 (a00) VALUES ('gf'); +INSERT INTO t1 (a00) VALUES ('gg'); +INSERT INTO t1 (a00) VALUES ('gh'); +COMMIT; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; +CLUST_INDEX_SIZE +29 +SET GLOBAL innodb_limit_optimistic_insert_debug = 0; +# Test start +SET DEBUG_SYNC = 'RESET'; +INSERT INTO t1 (a00) VALUES ('bfa'); +connection con1; +SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue'; +INSERT INTO t1 (a00) VALUES ('bfb'); +connection con2; +SET DEBUG_SYNC = 'now WAIT_FOR reached'; +SELECT a00,a01 FROM t1 WHERE a00 = 'aa'; +a00 a01 +aa a +SELECT a00,a01 FROM t1 WHERE a00 = 'aq'; +a00 a01 +aq a +SELECT a00,a01 FROM t1 WHERE a00 = 'cp'; +a00 a01 +cp a +SELECT a00,a01 FROM t1 WHERE a00 = 'el'; +a00 a01 +el a +SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1'; +SELECT a00,a01 FROM t1 WHERE a00 = 'ar'; +connection con3; +SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2'; +SELECT a00,a01 FROM t1 WHERE a00 = 'cn'; +connection default; +SET DEBUG_SYNC = 'now WAIT_FOR lockwait1 TIMEOUT 1'; +SET DEBUG_SYNC = 'now WAIT_FOR lockwait2 TIMEOUT 1'; +SET DEBUG_SYNC = 'now SIGNAL continue'; +connection con1; +connection con2; +a00 a01 +ar a +connection con3; +a00 a01 +cn a +connection default; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; +CLUST_INDEX_SIZE +30 +SET DEBUG_SYNC = 'RESET'; +INSERT INTO t1 (a00) VALUES ('coa'); +connection con1; +SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue'; +INSERT INTO t1 (a00) VALUES ('cob'); +connection con2; +SET DEBUG_SYNC = 'now WAIT_FOR reached'; +SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1'; +SELECT a00,a01 FROM t1 WHERE a00 = 'aa'; +connection con3; +SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2'; +SELECT a00,a01 FROM t1 WHERE a00 = 'el'; +connection default; +SET DEBUG_SYNC = 'now WAIT_FOR lockwait1 TIMEOUT 1'; +SET DEBUG_SYNC = 'now WAIT_FOR lockwait2 TIMEOUT 1'; +SET DEBUG_SYNC = 'now SIGNAL continue'; +connection con1; +connection con2; +a00 a01 +aa a +connection con3; +a00 a01 +el a +connection default; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; +CLUST_INDEX_SIZE +31 +SET DEBUG_SYNC = 'RESET'; +INSERT INTO t1 (a00) VALUES ('gba'); +connection con1; +SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue'; +INSERT INTO t1 (a00) VALUES ('gbb'); +connection con2; +SET DEBUG_SYNC = 'now WAIT_FOR reached'; +SELECT a00,a01 FROM t1 WHERE a00 = 'aa'; +a00 a01 +aa a +SELECT a00,a01 FROM t1 WHERE a00 = 'ek'; +a00 a01 +ek a +SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1'; +SELECT a00,a01 FROM t1 WHERE a00 = 'el'; +connection con3; +SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2'; +SELECT a00,a01 FROM t1 WHERE a00 = 'gb'; +connection default; +SET DEBUG_SYNC = 'now WAIT_FOR lockwait1 TIMEOUT 1'; +SET DEBUG_SYNC = 'now WAIT_FOR lockwait2 TIMEOUT 1'; +SET DEBUG_SYNC = 'now SIGNAL continue'; +connection con1; +connection con2; +a00 a01 +el a +connection con3; +a00 a01 +gb a +connection default; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; +CLUST_INDEX_SIZE +32 +SET DEBUG_SYNC = 'RESET'; +connection default; +disconnect con1; +disconnect con2; +disconnect con3; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/innodb_wl6326.opt b/mysql-test/suite/innodb/t/innodb_wl6326.opt new file mode 100644 index 00000000000..99bf0e5a28b --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_wl6326.opt @@ -0,0 +1 @@ +--innodb-sys-tablestats diff --git a/mysql-test/suite/innodb/t/innodb_wl6326.test b/mysql-test/suite/innodb/t/innodb_wl6326.test new file mode 100644 index 00000000000..1cf98cd1c7b --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_wl6326.test @@ -0,0 +1,519 @@ +# +# WL#6326: InnoDB: fix index->lock contention +# + +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc +--source include/have_innodb_16k.inc + +--disable_query_log +SET @old_innodb_limit_optimistic_insert_debug = @@innodb_limit_optimistic_insert_debug; +SET @old_innodb_adaptive_hash_index = @@innodb_adaptive_hash_index; +SET @old_innodb_stats_persistent = @@innodb_stats_persistent; +--enable_query_log + +# Save the initial number of concurrent sessions +--source include/count_sessions.inc + +SET GLOBAL innodb_adaptive_hash_index = false; +SET GLOBAL innodb_stats_persistent = false; + +--connect (con1,localhost,root,,) +--connect (con2,localhost,root,,) +--connect (con3,localhost,root,,) + +CREATE TABLE t1 ( + a00 CHAR(255) NOT NULL DEFAULT 'a', + a01 CHAR(255) NOT NULL DEFAULT 'a', + a02 CHAR(255) NOT NULL DEFAULT 'a', + a03 CHAR(255) NOT NULL DEFAULT 'a', + a04 CHAR(255) NOT NULL DEFAULT 'a', + a05 CHAR(255) NOT NULL DEFAULT 'a', + a06 CHAR(255) NOT NULL DEFAULT 'a', + b INT NOT NULL DEFAULT 0 +) ENGINE = InnoDB; + +ALTER TABLE t1 ADD PRIMARY KEY( + a00, + a01, + a02, + a03, + a04, + a05, + a06 +); + +# +# Prepare primary key index tree to be used for this test. +# + +# Only root (1) +ANALYZE TABLE t1; +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; + +# Make the first records sparse artificially, +# not to cause modify_tree by single node_ptr insert operation. +# * (7 - 2) records should be larger than a half of the page size +# * (7 + 2) records should be fit to the page +# (above t1 definition is already adjusted) +SET GLOBAL innodb_limit_optimistic_insert_debug = 7; + +BEGIN; +INSERT INTO t1 (a00) VALUES ('aa'); +INSERT INTO t1 (a00) VALUES ('ab'); +INSERT INTO t1 (a00) VALUES ('ac'); +INSERT INTO t1 (a00) VALUES ('ad'); +INSERT INTO t1 (a00) VALUES ('ae'); +INSERT INTO t1 (a00) VALUES ('af'); +INSERT INTO t1 (a00) VALUES ('ag'); +INSERT INTO t1 (a00) VALUES ('ah'); +COMMIT; +# Raise root (1-2) +# (aa,ad) +# (aa,ab,ac)(ad,ae,af,ag,ah) +ANALYZE TABLE t1; +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; + +BEGIN; +INSERT INTO t1 (a00) VALUES ('ai'); +INSERT INTO t1 (a00) VALUES ('aj'); +INSERT INTO t1 (a00) VALUES ('ak'); +COMMIT; +# Split leaf (1-3) +# (aa,ad,ak) +# (aa,ab,ac)(ad,ae,af,ag,ah,ai,aj)(ak) +ANALYZE TABLE t1; +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; + +BEGIN; +INSERT INTO t1 (a00) VALUES ('al'); +INSERT INTO t1 (a00) VALUES ('am'); +INSERT INTO t1 (a00) VALUES ('an'); +INSERT INTO t1 (a00) VALUES ('ao'); +INSERT INTO t1 (a00) VALUES ('ap'); +INSERT INTO t1 (a00) VALUES ('aq'); +INSERT INTO t1 (a00) VALUES ('ar'); +COMMIT; +# Split leaf (1-4) +# (aa,ad,ak,ar) +# (aa,ab,ac)(ad,ae,af,ag,ah,ai,aj)(ak,al,am,an,ao,ap,aq)(ar) +ANALYZE TABLE t1; +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; + +BEGIN; +INSERT INTO t1 (a00) VALUES ('as'); +INSERT INTO t1 (a00) VALUES ('at'); +INSERT INTO t1 (a00) VALUES ('au'); +INSERT INTO t1 (a00) VALUES ('av'); +INSERT INTO t1 (a00) VALUES ('aw'); +INSERT INTO t1 (a00) VALUES ('ax'); +INSERT INTO t1 (a00) VALUES ('ay'); +COMMIT; +# Split leaf (1-5) +# (aa,ad,ak,ar,ay) +# (aa,ab,ac)(ad,ae,af,ag,ah,ai,aj)(ak,al,am,an,ao,ap,aq)(ar,as,at,au,av,aw,ax)(ay) +ANALYZE TABLE t1; +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; + +BEGIN; +INSERT INTO t1 (a00) VALUES ('az'); +INSERT INTO t1 (a00) VALUES ('ba'); +INSERT INTO t1 (a00) VALUES ('bb'); +INSERT INTO t1 (a00) VALUES ('bc'); +INSERT INTO t1 (a00) VALUES ('bd'); +INSERT INTO t1 (a00) VALUES ('be'); +INSERT INTO t1 (a00) VALUES ('bf'); +COMMIT; +# Split leaf (1-6) +# (aa,ad,ak,ar,ay,bf) +# (aa,ab,ac)(ad..)(ak..)(ar,as,at,au,av,aw,ax)(ay,az,ba,bb,bc,bd,be)(bf) +ANALYZE TABLE t1; +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; + +BEGIN; +INSERT INTO t1 (a00) VALUES ('bg'); +INSERT INTO t1 (a00) VALUES ('bh'); +INSERT INTO t1 (a00) VALUES ('bi'); +INSERT INTO t1 (a00) VALUES ('bj'); +INSERT INTO t1 (a00) VALUES ('bk'); +INSERT INTO t1 (a00) VALUES ('bl'); +INSERT INTO t1 (a00) VALUES ('bm'); +COMMIT; +# Split leaf (1-7) +# (aa,ad,ak,ar,ay,bf,bm) +# (aa,ab,ac)(ad..)(ak..)(ar..)(ay,az,ba,bb,bc,bd,be)(bf,bg,bh,bi,bj,bk,bl)(bm) +ANALYZE TABLE t1; +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; + +BEGIN; +INSERT INTO t1 (a00) VALUES ('bn'); +INSERT INTO t1 (a00) VALUES ('bo'); +INSERT INTO t1 (a00) VALUES ('bp'); +INSERT INTO t1 (a00) VALUES ('bq'); +INSERT INTO t1 (a00) VALUES ('br'); +INSERT INTO t1 (a00) VALUES ('bs'); +INSERT INTO t1 (a00) VALUES ('bt'); +COMMIT; +# Raise root (1-2-8) +# (aa,ar) +# (aa,ad,ak) (ar,ay,bf,bm,bt) +# (aa,ab,ac)(ad..)(ak..)(ar..)(ay..)(bf..)(bm..)(bt) +ANALYZE TABLE t1; +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; + +BEGIN; +INSERT INTO t1 (a00) VALUES ('bu'); +INSERT INTO t1 (a00) VALUES ('bv'); +INSERT INTO t1 (a00) VALUES ('bw'); +INSERT INTO t1 (a00) VALUES ('bx'); +INSERT INTO t1 (a00) VALUES ('by'); +INSERT INTO t1 (a00) VALUES ('bz'); +INSERT INTO t1 (a00) VALUES ('ca'); + +INSERT INTO t1 (a00) VALUES ('cb'); +INSERT INTO t1 (a00) VALUES ('cc'); +INSERT INTO t1 (a00) VALUES ('cd'); +INSERT INTO t1 (a00) VALUES ('ce'); +INSERT INTO t1 (a00) VALUES ('cf'); +INSERT INTO t1 (a00) VALUES ('cg'); +INSERT INTO t1 (a00) VALUES ('ch'); + +INSERT INTO t1 (a00) VALUES ('ci'); +INSERT INTO t1 (a00) VALUES ('cj'); +INSERT INTO t1 (a00) VALUES ('ck'); +INSERT INTO t1 (a00) VALUES ('cl'); +INSERT INTO t1 (a00) VALUES ('cm'); +INSERT INTO t1 (a00) VALUES ('cn'); +INSERT INTO t1 (a00) VALUES ('co'); +COMMIT; +# Split also at level 1 (1-3-11) +# (aa,ar,co) +# (aa,ad,ak) (ar,ay,bf,bm,bt,ca,ch) (co) +# (aa,ab,ac)(ad..)(ak..)(ar..)(ay..)(bf..)(bm..)(bt..)(ca..)(ch..)(co) +ANALYZE TABLE t1; +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; + +BEGIN; +INSERT INTO t1 (a00) VALUES ('cp'); +INSERT INTO t1 (a00) VALUES ('cq'); +INSERT INTO t1 (a00) VALUES ('cr'); +INSERT INTO t1 (a00) VALUES ('cs'); +INSERT INTO t1 (a00) VALUES ('ct'); +INSERT INTO t1 (a00) VALUES ('cu'); +INSERT INTO t1 (a00) VALUES ('cv'); + +INSERT INTO t1 (a00) VALUES ('cw'); +INSERT INTO t1 (a00) VALUES ('cx'); +INSERT INTO t1 (a00) VALUES ('cy'); +INSERT INTO t1 (a00) VALUES ('cz'); +INSERT INTO t1 (a00) VALUES ('da'); +INSERT INTO t1 (a00) VALUES ('db'); +INSERT INTO t1 (a00) VALUES ('dc'); + +INSERT INTO t1 (a00) VALUES ('dd'); +INSERT INTO t1 (a00) VALUES ('de'); +INSERT INTO t1 (a00) VALUES ('df'); +INSERT INTO t1 (a00) VALUES ('dg'); +INSERT INTO t1 (a00) VALUES ('dh'); +INSERT INTO t1 (a00) VALUES ('di'); +INSERT INTO t1 (a00) VALUES ('dj'); + +INSERT INTO t1 (a00) VALUES ('dk'); +INSERT INTO t1 (a00) VALUES ('dl'); +INSERT INTO t1 (a00) VALUES ('dm'); +INSERT INTO t1 (a00) VALUES ('dn'); +INSERT INTO t1 (a00) VALUES ('do'); +INSERT INTO t1 (a00) VALUES ('dp'); +INSERT INTO t1 (a00) VALUES ('dq'); + +INSERT INTO t1 (a00) VALUES ('dr'); +INSERT INTO t1 (a00) VALUES ('ds'); +INSERT INTO t1 (a00) VALUES ('dt'); +INSERT INTO t1 (a00) VALUES ('du'); +INSERT INTO t1 (a00) VALUES ('dv'); +INSERT INTO t1 (a00) VALUES ('dw'); +INSERT INTO t1 (a00) VALUES ('dx'); + +INSERT INTO t1 (a00) VALUES ('dy'); +INSERT INTO t1 (a00) VALUES ('dz'); +INSERT INTO t1 (a00) VALUES ('ea'); +INSERT INTO t1 (a00) VALUES ('eb'); +INSERT INTO t1 (a00) VALUES ('ec'); +INSERT INTO t1 (a00) VALUES ('ed'); +INSERT INTO t1 (a00) VALUES ('ee'); + +INSERT INTO t1 (a00) VALUES ('ef'); +INSERT INTO t1 (a00) VALUES ('eg'); +INSERT INTO t1 (a00) VALUES ('eh'); +INSERT INTO t1 (a00) VALUES ('ei'); +INSERT INTO t1 (a00) VALUES ('ej'); +INSERT INTO t1 (a00) VALUES ('ek'); +INSERT INTO t1 (a00) VALUES ('el'); +COMMIT; +# Split also at level 1 (1-4-18) +# (aa,ar,co,el) +# (aa,ad,ak) (ar,ay,bf,bm,bt,ca,ch) (co,cv,dc,dj,dq,dx,ee) (el) +# (aa,ab,ac)(ad..)(ak..)(ar..)(ay..)(bf..)(bm..)(bt..)(ca..)(ch..)(co..)(cv..)(dc..)(dj..)(dq..)(dx..)(ee..)(el) +ANALYZE TABLE t1; +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; + +BEGIN; +INSERT INTO t1 (a00) VALUES ('em'); +INSERT INTO t1 (a00) VALUES ('en'); +INSERT INTO t1 (a00) VALUES ('eo'); +INSERT INTO t1 (a00) VALUES ('ep'); +INSERT INTO t1 (a00) VALUES ('eq'); +INSERT INTO t1 (a00) VALUES ('er'); +INSERT INTO t1 (a00) VALUES ('es'); + +INSERT INTO t1 (a00) VALUES ('et'); +INSERT INTO t1 (a00) VALUES ('eu'); +INSERT INTO t1 (a00) VALUES ('ev'); +INSERT INTO t1 (a00) VALUES ('ew'); +INSERT INTO t1 (a00) VALUES ('ex'); +INSERT INTO t1 (a00) VALUES ('ey'); +INSERT INTO t1 (a00) VALUES ('ez'); + +INSERT INTO t1 (a00) VALUES ('fa'); +INSERT INTO t1 (a00) VALUES ('fb'); +INSERT INTO t1 (a00) VALUES ('fc'); +INSERT INTO t1 (a00) VALUES ('fd'); +INSERT INTO t1 (a00) VALUES ('fe'); +INSERT INTO t1 (a00) VALUES ('ff'); +INSERT INTO t1 (a00) VALUES ('fg'); + +INSERT INTO t1 (a00) VALUES ('fh'); +INSERT INTO t1 (a00) VALUES ('fi'); +INSERT INTO t1 (a00) VALUES ('fj'); +INSERT INTO t1 (a00) VALUES ('fk'); +INSERT INTO t1 (a00) VALUES ('fl'); +INSERT INTO t1 (a00) VALUES ('fm'); +INSERT INTO t1 (a00) VALUES ('fn'); + +INSERT INTO t1 (a00) VALUES ('fo'); +INSERT INTO t1 (a00) VALUES ('fp'); +INSERT INTO t1 (a00) VALUES ('fq'); +INSERT INTO t1 (a00) VALUES ('fr'); +INSERT INTO t1 (a00) VALUES ('fs'); +INSERT INTO t1 (a00) VALUES ('ft'); +INSERT INTO t1 (a00) VALUES ('fu'); + +INSERT INTO t1 (a00) VALUES ('fv'); +INSERT INTO t1 (a00) VALUES ('fw'); +INSERT INTO t1 (a00) VALUES ('fx'); +INSERT INTO t1 (a00) VALUES ('fy'); +INSERT INTO t1 (a00) VALUES ('fz'); +INSERT INTO t1 (a00) VALUES ('ga'); +INSERT INTO t1 (a00) VALUES ('gb'); + +INSERT INTO t1 (a00) VALUES ('gc'); +INSERT INTO t1 (a00) VALUES ('gd'); +INSERT INTO t1 (a00) VALUES ('ge'); +INSERT INTO t1 (a00) VALUES ('gf'); +INSERT INTO t1 (a00) VALUES ('gg'); +INSERT INTO t1 (a00) VALUES ('gh'); +COMMIT; + +# Current tree form (1-4-24) +# (aa,ar,co,el) +# (aa,ad,ak) (ar,ay,bf,bm,bt,ca,ch) (co,cv,dc,dj,dq,dx,ee) (el..,gb) +# (aa,ab,ac)(ad..)(ak..)(ar..)(ay..)(bf..)(bm..)(bt..)(ca..)(ch..)(co..)(cv..)(dc..)(dj..)(dq..)(dx..)(ee..)(el..)..(gb..) +ANALYZE TABLE t1; +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; + +# Insert the rest of records normally +SET GLOBAL innodb_limit_optimistic_insert_debug = 0; + +--echo # Test start + +# (1) Insert records to leaf page (bf..) and cause modify_page. +# - root page is not X latched +# - latched from level 1 page (ar,ay,bf,bm,bt,ca,ch) + +SET DEBUG_SYNC = 'RESET'; + +# Filling leaf page (bf..) +INSERT INTO t1 (a00) VALUES ('bfa'); + +--connection con1 +SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue'; +# Cause modify_tree +--send +INSERT INTO t1 (a00) VALUES ('bfb'); + +--connection con2 +SET DEBUG_SYNC = 'now WAIT_FOR reached'; +# Not blocked searches +SELECT a00,a01 FROM t1 WHERE a00 = 'aa'; +SELECT a00,a01 FROM t1 WHERE a00 = 'aq'; +# "where a00 = 'co'" is blocked because searching from smaller ('co','a','a',..). +SELECT a00,a01 FROM t1 WHERE a00 = 'cp'; +SELECT a00,a01 FROM t1 WHERE a00 = 'el'; + +SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1'; +# Blocked +--send +SELECT a00,a01 FROM t1 WHERE a00 = 'ar'; + +--connection con3 +SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2'; +# Blocked +--send +SELECT a00,a01 FROM t1 WHERE a00 = 'cn'; + +--connection default +# FIXME: These occasionally time out! +--disable_warnings +SET DEBUG_SYNC = 'now WAIT_FOR lockwait1 TIMEOUT 1'; +SET DEBUG_SYNC = 'now WAIT_FOR lockwait2 TIMEOUT 1'; +--enable_warnings +SET DEBUG_SYNC = 'now SIGNAL continue'; + +--connection con1 +--reap + +--connection con2 +--reap + +--connection con3 +--reap + +--connection default + +ANALYZE TABLE t1; +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; + + + +# (2) Insert records to leaf page (co..) and cause modify_page +# - root page is X latched, because node_ptr for 'co' +# is 1st record for (co,cv,dc,dj,dq,dx,ee) +# +# * ordinary pessimitic insert might be done by pessistic update +# and we should consider possibility node_ptr to be deleted. + +SET DEBUG_SYNC = 'RESET'; + +# Filling leaf page (co..) +INSERT INTO t1 (a00) VALUES ('coa'); + +--connection con1 +SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue'; +# Cause modify_tree +--send +INSERT INTO t1 (a00) VALUES ('cob'); + +--connection con2 +SET DEBUG_SYNC = 'now WAIT_FOR reached'; +# All searches are blocked because root page is X latched + +SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1'; +# Blocked +--send +SELECT a00,a01 FROM t1 WHERE a00 = 'aa'; + +--connection con3 +SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2'; +# Blocked +--send +SELECT a00,a01 FROM t1 WHERE a00 = 'el'; + +--connection default +# FIXME: These occasionally time out! +--disable_warnings +SET DEBUG_SYNC = 'now WAIT_FOR lockwait1 TIMEOUT 1'; +SET DEBUG_SYNC = 'now WAIT_FOR lockwait2 TIMEOUT 1'; +--enable_warnings +SET DEBUG_SYNC = 'now SIGNAL continue'; + +--connection con1 +--reap + +--connection con2 +--reap + +--connection con3 +--reap + +--connection default + +ANALYZE TABLE t1; +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; + + + +# (3) Insert records to rightmost leaf page (gb..) and cause modify_page +# - root page is not X latched, because node_ptr for 'gb' is the last record +# of the level 1 though it is last record in the page. +# - lathed from level 1 page (el..,gb) + +SET DEBUG_SYNC = 'RESET'; + +# Filling leaf page (gb..) +INSERT INTO t1 (a00) VALUES ('gba'); + +--connection con1 +SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue'; +# Cause modify_tree +--send +INSERT INTO t1 (a00) VALUES ('gbb'); + +--connection con2 +SET DEBUG_SYNC = 'now WAIT_FOR reached'; +# Not blocked searches +SELECT a00,a01 FROM t1 WHERE a00 = 'aa'; +SELECT a00,a01 FROM t1 WHERE a00 = 'ek'; + +SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1'; +# Blocked +--send +SELECT a00,a01 FROM t1 WHERE a00 = 'el'; + +--connection con3 +SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2'; +# Blocked +--send +SELECT a00,a01 FROM t1 WHERE a00 = 'gb'; + +--connection default +# FIXME: These occasionally time out! +--disable_warnings +SET DEBUG_SYNC = 'now WAIT_FOR lockwait1 TIMEOUT 1'; +SET DEBUG_SYNC = 'now WAIT_FOR lockwait2 TIMEOUT 1'; +--enable_warnings +SET DEBUG_SYNC = 'now SIGNAL continue'; + +--connection con1 +--reap + +--connection con2 +--reap + +--connection con3 +--reap + +--connection default +ANALYZE TABLE t1; +SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1'; + + + +# Cleanup +SET DEBUG_SYNC = 'RESET'; + +--connection default +--disconnect con1 +--disconnect con2 +--disconnect con3 + +DROP TABLE t1; + +--disable_query_log +SET GLOBAL innodb_limit_optimistic_insert_debug = @old_innodb_limit_optimistic_insert_debug; +SET GLOBAL innodb_adaptive_hash_index = @old_innodb_adaptive_hash_index; +SET GLOBAL innodb_stats_persistent = @old_innodb_stats_persistent; +--enable_query_log + +# Wait till all disconnects are completed. +--source include/wait_until_count_sessions.inc diff --git a/mysql-test/suite/innodb_gis/r/rtree_split.result b/mysql-test/suite/innodb_gis/r/rtree_split.result index 10262f0220b..8e475776ce0 100644 --- a/mysql-test/suite/innodb_gis/r/rtree_split.result +++ b/mysql-test/suite/innodb_gis/r/rtree_split.result @@ -61,15 +61,3 @@ select count(*) from t1 where MBRWithin(t1.c2, @g1); count(*) 57344 drop table t1; -# -# MDEV-30400 Assertion height == btr_page_get_level ... on INSERT -# -CREATE TABLE t1 (c POINT NOT NULL,SPATIAL (c)) ENGINE=InnoDB; -SET @save_limit=@@GLOBAL.innodb_limit_optimistic_insert_debug; -SET GLOBAL innodb_limit_optimistic_insert_debug=2; -BEGIN; -INSERT INTO t1 SELECT POINTFROMTEXT ('POINT(0 0)') FROM seq_1_to_366; -ROLLBACK; -SET GLOBAL innodb_limit_optimistic_insert_debug=@save_limit; -DROP TABLE t1; -# End of 10.6 tests diff --git a/mysql-test/suite/innodb_gis/t/rtree_split.test b/mysql-test/suite/innodb_gis/t/rtree_split.test index de7fc676e0e..6f285187508 100644 --- a/mysql-test/suite/innodb_gis/t/rtree_split.test +++ b/mysql-test/suite/innodb_gis/t/rtree_split.test @@ -73,18 +73,3 @@ select count(*) from t1 where MBRWithin(t1.c2, @g1); # Clean up. drop table t1; - ---echo # ---echo # MDEV-30400 Assertion height == btr_page_get_level ... on INSERT ---echo # - -CREATE TABLE t1 (c POINT NOT NULL,SPATIAL (c)) ENGINE=InnoDB; -SET @save_limit=@@GLOBAL.innodb_limit_optimistic_insert_debug; -SET GLOBAL innodb_limit_optimistic_insert_debug=2; -BEGIN; -INSERT INTO t1 SELECT POINTFROMTEXT ('POINT(0 0)') FROM seq_1_to_366; -ROLLBACK; -SET GLOBAL innodb_limit_optimistic_insert_debug=@save_limit; -DROP TABLE t1; - ---echo # End of 10.6 tests diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index ef44ed5d9d6..0bb16dba374 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -2,7 +2,7 @@ Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2014, 2023, MariaDB Corporation. +Copyright (c) 2014, 2022, 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 @@ -460,53 +460,6 @@ btr_page_create( } } -buf_block_t * -mtr_t::get_already_latched(const page_id_t id, mtr_memo_type_t type) const -{ - ut_ad(is_active()); - ut_ad(type == MTR_MEMO_PAGE_X_FIX || type == MTR_MEMO_PAGE_SX_FIX || - type == MTR_MEMO_PAGE_S_FIX); - for (ulint i= 0; i < m_memo.size(); i++) - { - const mtr_memo_slot_t &slot= m_memo[i]; - const auto slot_type= mtr_memo_type_t(slot.type & ~MTR_MEMO_MODIFY); - if (slot_type == MTR_MEMO_PAGE_X_FIX || slot_type == type) - { - buf_block_t *block= static_cast(slot.object); - if (block->page.id() == id) - return block; - } - } - return nullptr; -} - -/** Fetch an index root page that was already latched in the -mini-transaction. */ -static buf_block_t *btr_get_latched_root(const dict_index_t &index, mtr_t *mtr) -{ - return mtr->get_already_latched(page_id_t{index.table->space_id, index.page}, - MTR_MEMO_PAGE_SX_FIX); -} - -/** Fet an index page that should have been already latched in the -mini-transaction. */ -static buf_block_t * -btr_block_reget(mtr_t *mtr, const dict_index_t &index, - const page_id_t id, rw_lock_type_t rw_latch, - dberr_t *err) -{ - if (buf_block_t *block= - mtr->get_already_latched(id, mtr_memo_type_t(rw_latch))) - { - *err= DB_SUCCESS; - return block; - } - - /* MDEV-29385 FIXME: Acquire the page latch upfront. */ - ut_ad(mtr->memo_contains_flagged(&index.lock, MTR_MEMO_X_LOCK)); - return btr_block_get(index, id.page_no(), rw_latch, true, mtr, err); -} - /**************************************************************//** Allocates a new file page to be used in an ibuf tree. Takes the page from the free list of the tree, which must contain pages! @@ -519,16 +472,18 @@ btr_page_alloc_for_ibuf( mtr_t* mtr, /*!< in: mtr */ dberr_t* err) /*!< out: error code */ { - buf_block_t *root= btr_get_latched_root(*index, mtr); + buf_block_t *root= btr_root_block_get(index, RW_SX_LATCH, mtr, err); if (UNIV_UNLIKELY(!root)) return root; + buf_block_t *new_block= - buf_page_get_gen(page_id_t(IBUF_SPACE_ID, + buf_page_get_gen(page_id_t(index->table->space_id, mach_read_from_4(PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST + FLST_FIRST + FIL_ADDR_PAGE + root->page.frame)), - 0, RW_X_LATCH, nullptr, BUF_GET, mtr, err); + index->table->space->zip_size(), RW_X_LATCH, nullptr, + BUF_GET, mtr, err); if (new_block) *err= flst_remove(root, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, new_block, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST_NODE, mtr); @@ -568,11 +523,11 @@ btr_page_alloc_low( #ifdef BTR_CUR_HASH_ADAPT ut_ad(!root->index || !root->index->freed()); #endif - mtr->rollback_to_savepoint(savepoint); + mtr->release_block_at_savepoint(savepoint, root); } else { - mtr->lock_register(savepoint, MTR_MEMO_PAGE_SX_FIX); + mtr->u_lock_register(savepoint); root->page.lock.u_lock(); #ifdef BTR_CUR_HASH_ADAPT btr_search_drop_page_hash_index(root, true); @@ -624,12 +579,15 @@ btr_page_free_for_ibuf( mtr_t* mtr) /*!< in: mtr */ { ut_ad(mtr->memo_contains_flagged(block, MTR_MEMO_PAGE_X_FIX)); - buf_block_t *root= btr_get_latched_root(*index, mtr); - dberr_t err= - flst_add_first(root, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, + + dberr_t err; + if (buf_block_t *root= btr_root_block_get(index, RW_SX_LATCH, mtr, &err)) + { + err= flst_add_first(root, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, block, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST_NODE, mtr); - ut_d(if (err == DB_SUCCESS) - flst_validate(root, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, mtr)); + ut_d(if (err == DB_SUCCESS) + flst_validate(root, PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, mtr)); + } return err; } @@ -679,11 +637,11 @@ dberr_t btr_page_free(dict_index_t* index, buf_block_t* block, mtr_t* mtr, #ifdef BTR_CUR_HASH_ADAPT ut_ad(!root->index || !root->index->freed()); #endif - mtr->rollback_to_savepoint(savepoint); + mtr->release_block_at_savepoint(savepoint, root); } else { - mtr->lock_register(savepoint, MTR_MEMO_PAGE_SX_FIX); + mtr->u_lock_register(savepoint); root->page.lock.u_lock(); #ifdef BTR_CUR_HASH_ADAPT btr_search_drop_page_hash_index(root, true); @@ -754,27 +712,35 @@ btr_node_ptr_get_child( mtr, err); } -MY_ATTRIBUTE((nonnull(2,3,4), warn_unused_result)) +MY_ATTRIBUTE((nonnull(2,3,5), warn_unused_result)) /************************************************************//** Returns the upper level node pointer to a page. It is assumed that mtr holds an sx-latch on the tree. @return rec_get_offsets() of the node pointer record */ static rec_offs* -btr_page_get_father_node_ptr_for_validate( +btr_page_get_father_node_ptr_func( +/*==============================*/ rec_offs* offsets,/*!< in: work area for the return value */ mem_heap_t* heap, /*!< in: memory heap to use */ btr_cur_t* cursor, /*!< in: cursor pointing to user record, out: cursor on node pointer record, its page x-latched */ + btr_latch_mode latch_mode,/*!< in: BTR_CONT_MODIFY_TREE + or BTR_CONT_SEARCH_TREE */ mtr_t* mtr) /*!< in: mtr */ { + ut_ad(latch_mode == BTR_CONT_MODIFY_TREE + || latch_mode == BTR_CONT_SEARCH_TREE); + const uint32_t page_no = btr_cur_get_block(cursor)->page.id().page_no(); dict_index_t* index = btr_cur_get_index(cursor); ut_ad(!dict_index_is_spatial(index)); - ut_ad(mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK - | MTR_MEMO_SX_LOCK)); + ut_ad(srv_read_only_mode + || mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK + | MTR_MEMO_SX_LOCK)); + ut_ad(dict_index_get_page(index) != page_no); const auto level = btr_page_get_level(btr_cur_get_page(cursor)); @@ -786,16 +752,12 @@ btr_page_get_father_node_ptr_for_validate( dict_index_build_node_ptr(index, user_rec, 0, heap, level), - RW_S_LATCH, + PAGE_CUR_LE, latch_mode, cursor, mtr) != DB_SUCCESS) { return nullptr; } const rec_t* node_ptr = btr_cur_get_rec(cursor); -#if 0 /* MDEV-29835 FIXME */ - ut_ad(!btr_cur_get_block(cursor)->page.lock.not_recursive() - || mtr->memo_contains(index->lock, MTR_MEMO_X_LOCK)); -#endif offsets = rec_get_offsets(node_ptr, index, offsets, 0, ULINT_UNDEFINED, &heap); @@ -807,64 +769,13 @@ btr_page_get_father_node_ptr_for_validate( return(offsets); } -MY_ATTRIBUTE((nonnull(2,3,4), warn_unused_result)) -/************************************************************//** -Returns the upper level node pointer to a page. It is assumed that -it has already been latched. -@return rec_get_offsets() of the node pointer record */ -static -rec_offs* -btr_page_get_parent( - rec_offs* offsets,/*!< in: work area for the return value */ - mem_heap_t* heap, /*!< in: memory heap to use */ - btr_cur_t* cursor, /*!< in: cursor pointing to user record, - out: cursor on node pointer record, - its page x-latched */ - mtr_t* mtr) /*!< in: mtr */ -{ - const uint32_t page_no= cursor->block()->page.id().page_no(); - const dict_index_t *index= cursor->index(); - ut_ad(!index->is_spatial()); - ut_ad(index->page != page_no); - - uint32_t p= index->page; - const dtuple_t *tuple= - dict_index_build_node_ptr(index, btr_cur_get_rec(cursor), 0, heap, - btr_page_get_level(btr_cur_get_page(cursor))); - - ulint i; - for (i= 0; i < mtr->get_savepoint(); i++) - if (buf_block_t *block= mtr->block_at_savepoint(i)) - if (block->page.id().page_no() == p) - { - ut_ad(block->page.lock.have_u_or_x() || - (!block->page.lock.have_s() && index->lock.have_x())); - ulint up_match= 0, low_match= 0; - cursor->page_cur.block= block; - if (page_cur_search_with_match(tuple, PAGE_CUR_LE, &up_match, - &low_match, &cursor->page_cur, - nullptr)) - return nullptr; - offsets= rec_get_offsets(cursor->page_cur.rec, index, offsets, 0, - ULINT_UNDEFINED, &heap); - p= btr_node_ptr_get_child_page_no(cursor->page_cur.rec, offsets); - if (p != page_no) - { - i= 0; // MDEV-29835 FIXME: require all pages to be latched in order! - continue; - } - ut_ad(block->page.lock.have_u_or_x()); - if (block->page.lock.have_u_not_x()) - { - ut_ad(block->page.id().page_no() == index->page); - block->page.lock.u_x_upgrade(); - mtr->page_lock_upgrade(*block); - } - return offsets; - } +#define btr_page_get_father_node_ptr(of,heap,cur,mtr) \ + btr_page_get_father_node_ptr_func( \ + of,heap,cur,BTR_CONT_MODIFY_TREE,mtr) - return nullptr; -} +#define btr_page_get_father_node_ptr_for_validate(of,heap,cur,mtr) \ + btr_page_get_father_node_ptr_func( \ + of,heap,cur,BTR_CONT_SEARCH_TREE,mtr) /************************************************************//** Returns the upper level node pointer to a page. It is assumed that mtr holds @@ -885,7 +796,7 @@ btr_page_get_father_block( if (UNIV_UNLIKELY(!rec)) return nullptr; cursor->page_cur.rec= rec; - return btr_page_get_parent(offsets, heap, cursor, mtr); + return btr_page_get_father_node_ptr(offsets, heap, cursor, mtr); } /** Seek to the parent page of a B-tree page. @@ -900,7 +811,7 @@ bool btr_page_get_father(mtr_t* mtr, btr_cur_t* cursor) return false; cursor->page_cur.rec= rec; mem_heap_t *heap= mem_heap_create(100); - const bool got= btr_page_get_parent(nullptr, heap, cursor, mtr); + const bool got= btr_page_get_father_node_ptr(nullptr, heap, cursor, mtr); mem_heap_free(heap); return got; } @@ -1807,43 +1718,48 @@ void btr_set_instant(buf_block_t* root, const dict_index_t& index, mtr_t* mtr) /** Reset the table to the canonical format on ROLLBACK of instant ALTER TABLE. @param[in] index clustered index with instant ALTER TABLE @param[in] all whether to reset FIL_PAGE_TYPE as well -@param[in,out] mtr mini-transaction */ +@param[in,out] mtr mini-transaction +@return error code */ ATTRIBUTE_COLD -void btr_reset_instant(const dict_index_t &index, bool all, mtr_t *mtr) +dberr_t btr_reset_instant(const dict_index_t &index, bool all, mtr_t *mtr) { ut_ad(!index.table->is_temporary()); ut_ad(index.is_primary()); - buf_block_t *root= btr_get_latched_root(index, mtr); - byte *page_type= root->page.frame + FIL_PAGE_TYPE; - if (all) - { - ut_ad(mach_read_from_2(page_type) == FIL_PAGE_TYPE_INSTANT || - mach_read_from_2(page_type) == FIL_PAGE_INDEX); - mtr->write<2,mtr_t::MAYBE_NOP>(*root, page_type, FIL_PAGE_INDEX); - byte *instant= PAGE_INSTANT + PAGE_HEADER + root->page.frame; - mtr->write<2,mtr_t::MAYBE_NOP>(*root, instant, - page_ptr_get_direction(instant + 1)); - } - else - ut_ad(mach_read_from_2(page_type) == FIL_PAGE_TYPE_INSTANT); - static const byte supremuminfimum[8 + 8] = "supremuminfimum"; - uint16_t infimum, supremum; - if (page_is_comp(root->page.frame)) - { - infimum= PAGE_NEW_INFIMUM; - supremum= PAGE_NEW_SUPREMUM; - } - else + dberr_t err; + if (buf_block_t *root= btr_root_block_get(&index, RW_SX_LATCH, mtr, &err)) { - infimum= PAGE_OLD_INFIMUM; - supremum= PAGE_OLD_SUPREMUM; + byte *page_type= root->page.frame + FIL_PAGE_TYPE; + if (all) + { + ut_ad(mach_read_from_2(page_type) == FIL_PAGE_TYPE_INSTANT || + mach_read_from_2(page_type) == FIL_PAGE_INDEX); + mtr->write<2,mtr_t::MAYBE_NOP>(*root, page_type, FIL_PAGE_INDEX); + byte *instant= PAGE_INSTANT + PAGE_HEADER + root->page.frame; + mtr->write<2,mtr_t::MAYBE_NOP>(*root, instant, + page_ptr_get_direction(instant + 1)); + } + else + ut_ad(mach_read_from_2(page_type) == FIL_PAGE_TYPE_INSTANT); + static const byte supremuminfimum[8 + 8] = "supremuminfimum"; + uint16_t infimum, supremum; + if (page_is_comp(root->page.frame)) + { + infimum= PAGE_NEW_INFIMUM; + supremum= PAGE_NEW_SUPREMUM; + } + else + { + infimum= PAGE_OLD_INFIMUM; + supremum= PAGE_OLD_SUPREMUM; + } + ut_ad(!memcmp(&root->page.frame[infimum], supremuminfimum + 8, 8) == + !memcmp(&root->page.frame[supremum], supremuminfimum, 8)); + mtr->memcpy(*root, &root->page.frame[infimum], + supremuminfimum + 8, 8); + mtr->memcpy(*root, &root->page.frame[supremum], + supremuminfimum, 8); } - ut_ad(!memcmp(&root->page.frame[infimum], supremuminfimum + 8, 8) == - !memcmp(&root->page.frame[supremum], supremuminfimum, 8)); - mtr->memcpy(*root, &root->page.frame[infimum], - supremuminfimum + 8, 8); - mtr->memcpy(*root, &root->page.frame[supremum], - supremuminfimum, 8); + return err; } /*************************************************************//** @@ -1940,6 +1856,11 @@ btr_root_raise_and_insert( } /* Copy the records from root to the new page one by one. */ + dberr_t e; + if (!err) { + err = &e; + } + if (0 #ifdef UNIV_ZIP_COPY || new_page_zip @@ -2083,15 +2004,21 @@ btr_root_raise_and_insert( page_cursor->block = new_block; page_cursor->index = index; - ut_ad(dtuple_check_typed(tuple)); - /* Reposition the cursor to the child node */ - ulint low_match = 0, up_match = 0; + if (tuple) { + ut_ad(dtuple_check_typed(tuple)); + /* Reposition the cursor to the child node */ + ulint low_match = 0, up_match = 0; - if (page_cur_search_with_match(tuple, PAGE_CUR_LE, - &up_match, &low_match, - page_cursor, nullptr)) { - *err = DB_CORRUPTION; - return nullptr; + if (page_cur_search_with_match(tuple, PAGE_CUR_LE, + &up_match, &low_match, + page_cursor, nullptr)) { + if (err) { + *err = DB_CORRUPTION; + } + return nullptr; + } + } else { + page_cursor->rec = page_get_infimum_rec(new_block->page.frame); } /* Split the child and insert tuple */ @@ -2310,7 +2237,6 @@ func_exit: return(rec); } -#ifdef UNIV_DEBUG /*************************************************************//** Returns TRUE if the insert fits on the appropriate half-page with the chosen split_rec. @@ -2408,7 +2334,6 @@ got_rec: return(false); } -#endif /*******************************************************//** Inserts a data tuple to a tree on a non-leaf level. It is assumed @@ -2431,34 +2356,25 @@ btr_insert_on_non_leaf_level( rtr_info_t rtr_info; ut_ad(level > 0); - - flags |= BTR_NO_LOCKING_FLAG | BTR_KEEP_SYS_FLAG - | BTR_NO_UNDO_LOG_FLAG; - cursor.page_cur.index = index; - - dberr_t err; + auto mode = PAGE_CUR_LE; if (index->is_spatial()) { + mode = PAGE_CUR_RTREE_INSERT; /* For spatial index, initialize structures to track its parents etc. */ rtr_init_rtr_info(&rtr_info, false, &cursor, index, false); rtr_info_update_btr(&cursor, &rtr_info); - err = rtr_search_to_nth_level(level, tuple, - PAGE_CUR_RTREE_INSERT, - BTR_CONT_MODIFY_TREE, - &cursor, mtr); - } else { - err = btr_cur_search_to_nth_level(level, tuple, RW_X_LATCH, - &cursor, mtr); } + flags |= BTR_NO_LOCKING_FLAG | BTR_KEEP_SYS_FLAG + | BTR_NO_UNDO_LOG_FLAG; + cursor.page_cur.index = index; + + dberr_t err = btr_cur_search_to_nth_level(level, tuple, mode, + BTR_CONT_MODIFY_TREE, + &cursor, mtr); ut_ad(cursor.flag == BTR_CUR_BINARY); -#if 0 /* MDEV-29835 FIXME */ - ut_ad(!btr_cur_get_block(&cursor)->page.lock.not_recursive() - || index->is_spatial() - || mtr->memo_contains(index->lock, MTR_MEMO_X_LOCK)); -#endif if (UNIV_LIKELY(err == DB_SUCCESS)) { err = btr_cur_optimistic_insert(flags, @@ -2554,7 +2470,6 @@ btr_attach_half_pages( /* Get the level of the split pages */ const ulint level = btr_page_get_level(block->page.frame); ut_ad(level == btr_page_get_level(new_block->page.frame)); - page_id_t id{block->page.id()}; /* Get the previous and next pages of page */ const uint32_t prev_page_no = btr_page_get_prev(block->page.frame); @@ -2562,32 +2477,12 @@ btr_attach_half_pages( /* for consistency, both blocks should be locked, before change */ if (prev_page_no != FIL_NULL && direction == FSP_DOWN) { - id.set_page_no(prev_page_no); - prev_block = mtr->get_already_latched(id, MTR_MEMO_PAGE_X_FIX); -#if 1 /* MDEV-29835 FIXME: acquire page latches upfront */ - if (!prev_block) { -# if 0 /* MDEV-29835 FIXME */ - ut_ad(mtr->memo_contains(index->lock, - MTR_MEMO_X_LOCK)); -# endif - prev_block = btr_block_get(*index, prev_page_no, - RW_X_LATCH, !level, mtr); - } -#endif + prev_block = btr_block_get(*index, prev_page_no, RW_X_LATCH, + !level, mtr); } if (next_page_no != FIL_NULL && direction != FSP_DOWN) { - id.set_page_no(next_page_no); - next_block = mtr->get_already_latched(id, MTR_MEMO_PAGE_X_FIX); -#if 1 /* MDEV-29835 FIXME: acquire page latches upfront */ - if (!next_block) { -# if 0 /* MDEV-29835 FIXME */ - ut_ad(mtr->memo_contains(index->lock, - MTR_MEMO_X_LOCK)); -# endif - next_block = btr_block_get(*index, next_page_no, - RW_X_LATCH, !level, mtr); - } -#endif + next_block = btr_block_get(*index, next_page_no, RW_X_LATCH, + !level, mtr); } /* Build the node pointer (= node key and page address) for the upper @@ -3123,7 +3018,6 @@ insert_empty: return nullptr; } -#ifdef UNIV_DEBUG /* If the split is made on the leaf level and the insert will fit on the appropriate half-page, we may release the tree x-latch. We can then move the records after releasing the tree latch, @@ -3131,21 +3025,21 @@ insert_empty: const bool insert_will_fit = !new_page_zip && btr_page_insert_fits(cursor, split_rec, offsets, tuple, n_ext, heap); -#endif if (!split_rec && !insert_left) { UT_DELETE_ARRAY(buf); buf = NULL; } -#if 0 // FIXME: this used to be a no-op, and may cause trouble if enabled - if (insert_will_fit + if (!srv_read_only_mode + && insert_will_fit && page_is_leaf(page) && !dict_index_is_online_ddl(cursor->index())) { +#if 0 // FIXME: this used to be a no-op, and may cause trouble if enabled mtr->release(cursor->index()->lock); +#endif /* NOTE: We cannot release root block latch here, because it has segment header and already modified in most of cases.*/ } -#endif /* 5. Move then the records to the new page */ if (direction == FSP_DOWN) { @@ -3377,58 +3271,52 @@ func_exit: dberr_t btr_level_list_remove(const buf_block_t& block, const dict_index_t& index, mtr_t* mtr) { - ut_ad(mtr->memo_contains_flagged(&block, MTR_MEMO_PAGE_X_FIX)); - ut_ad(block.zip_size() == index.table->space->zip_size()); - ut_ad(index.table->space->id == block.page.id().space()); - /* Get the previous and next page numbers of page */ - const uint32_t prev_page_no= btr_page_get_prev(block.page.frame); - const uint32_t next_page_no= btr_page_get_next(block.page.frame); - page_id_t id{block.page.id()}; - buf_block_t *prev= nullptr, *next; - dberr_t err; + ut_ad(mtr->memo_contains_flagged(&block, MTR_MEMO_PAGE_X_FIX)); + ut_ad(block.zip_size() == index.table->space->zip_size()); + ut_ad(index.table->space->id == block.page.id().space()); + /* Get the previous and next page numbers of page */ - /* Update page links of the level */ - if (prev_page_no != FIL_NULL) - { - id.set_page_no(prev_page_no); - prev= mtr->get_already_latched(id, MTR_MEMO_PAGE_X_FIX); -#if 1 /* MDEV-29835 FIXME: acquire page latches upfront */ - if (!prev) - { -# if 0 /* MDEV-29835 FIXME */ - ut_ad(mtr->memo_contains(index.lock, MTR_MEMO_X_LOCK)); -# endif - prev= btr_block_get(index, id.page_no(), RW_X_LATCH, - page_is_leaf(block.page.frame), mtr, &err); - if (UNIV_UNLIKELY(!prev)) - return err; - } -#endif - } + const page_t* page = block.page.frame; + const uint32_t prev_page_no = btr_page_get_prev(page); + const uint32_t next_page_no = btr_page_get_next(page); - if (next_page_no != FIL_NULL) - { - id.set_page_no(next_page_no); - next= mtr->get_already_latched(id, MTR_MEMO_PAGE_X_FIX); -#if 1 /* MDEV-29835 FIXME: acquire page latches upfront */ - if (!next) - { -# if 0 /* MDEV-29835 FIXME */ - ut_ad(mtr->memo_contains(index.lock, MTR_MEMO_X_LOCK)); -# endif - next= btr_block_get(index, id.page_no(), RW_X_LATCH, - page_is_leaf(block.page.frame), mtr, &err); - if (UNIV_UNLIKELY(!next)) - return err; - } -#endif - btr_page_set_prev(next, prev_page_no, mtr); - } + /* Update page links of the level */ + dberr_t err; - if (prev) - btr_page_set_next(prev, next_page_no, mtr); + if (prev_page_no != FIL_NULL) { + buf_block_t* prev_block = btr_block_get( + index, prev_page_no, RW_X_LATCH, page_is_leaf(page), + mtr, &err); + if (UNIV_UNLIKELY(!prev_block)) { + return err; + } + if (UNIV_UNLIKELY(memcmp_aligned<4>(prev_block->page.frame + + FIL_PAGE_NEXT, + page + FIL_PAGE_OFFSET, + 4))) { + return DB_CORRUPTION; + } + btr_page_set_next(prev_block, next_page_no, mtr); + } - return DB_SUCCESS; + if (next_page_no != FIL_NULL) { + buf_block_t* next_block = btr_block_get( + index, next_page_no, RW_X_LATCH, page_is_leaf(page), + mtr, &err); + + if (UNIV_UNLIKELY(!next_block)) { + return err; + } + if (UNIV_UNLIKELY(memcmp_aligned<4>(next_block->page.frame + + FIL_PAGE_PREV, + page + FIL_PAGE_OFFSET, + 4))) { + return DB_CORRUPTION; + } + btr_page_set_prev(next_block, prev_page_no, mtr); + } + + return DB_SUCCESS; } /*************************************************************//** @@ -4278,30 +4166,23 @@ btr_discard_page( const uint32_t left_page_no = btr_page_get_prev(block->page.frame); const uint32_t right_page_no = btr_page_get_next(block->page.frame); - page_id_t merge_page_id{block->page.id()}; ut_d(bool parent_is_different = false); - dberr_t err; if (left_page_no != FIL_NULL) { - merge_page_id.set_page_no(left_page_no); - merge_block = btr_block_reget(mtr, *index, merge_page_id, - RW_X_LATCH, &err); + dberr_t err; + merge_block = btr_block_get(*index, left_page_no, RW_X_LATCH, + true, mtr, &err); if (UNIV_UNLIKELY(!merge_block)) { return err; } -#if 0 /* MDEV-29385 FIXME: Acquire the page latch upfront. */ - ut_ad(!memcmp_aligned<4>(merge_block->page.frame - + FIL_PAGE_NEXT, - block->page.frame + FIL_PAGE_OFFSET, - 4)); -#else + if (UNIV_UNLIKELY(memcmp_aligned<4>(merge_block->page.frame + FIL_PAGE_NEXT, block->page.frame + FIL_PAGE_OFFSET, 4))) { return DB_CORRUPTION; } -#endif + ut_d(parent_is_different = (page_rec_get_next( page_get_infimum_rec( @@ -4309,25 +4190,19 @@ btr_discard_page( &parent_cursor))) == btr_cur_get_rec(&parent_cursor))); } else if (right_page_no != FIL_NULL) { - merge_page_id.set_page_no(right_page_no); - merge_block = btr_block_reget(mtr, *index, merge_page_id, - RW_X_LATCH, &err); + dberr_t err; + merge_block = btr_block_get(*index, right_page_no, RW_X_LATCH, + true, mtr, &err); if (UNIV_UNLIKELY(!merge_block)) { return err; } -#if 0 /* MDEV-29385 FIXME: Acquire the page latch upfront. */ - ut_ad(!memcmp_aligned<4>(merge_block->page.frame - + FIL_PAGE_PREV, - block->page.frame + FIL_PAGE_OFFSET, - 4)); -#else if (UNIV_UNLIKELY(memcmp_aligned<4>(merge_block->page.frame + FIL_PAGE_PREV, block->page.frame + FIL_PAGE_OFFSET, 4))) { return DB_CORRUPTION; } -#endif + ut_d(parent_is_different = page_rec_is_supremum( page_rec_get_next(btr_cur_get_rec(&parent_cursor)))); if (page_is_leaf(merge_block->page.frame)) { @@ -4369,10 +4244,13 @@ btr_discard_page( } #ifdef UNIV_ZIP_DEBUG - if (page_zip_des_t* merge_page_zip - = buf_block_get_page_zip(merge_block)); - ut_a(page_zip_validate(merge_page_zip, - merge_block->page.frame, index)); + { + page_zip_des_t* merge_page_zip + = buf_block_get_page_zip(merge_block); + ut_a(!merge_page_zip + || page_zip_validate(merge_page_zip, + merge_block->page.frame, index)); + } #endif /* UNIV_ZIP_DEBUG */ if (index->has_locking()) { @@ -4391,7 +4269,7 @@ btr_discard_page( } /* Free the file page */ - err = btr_page_free(index, block, mtr); + dberr_t err = btr_page_free(index, block, mtr); if (err == DB_SUCCESS) { /* btr_check_node_ptr() needs parent block latched. @@ -4584,8 +4462,6 @@ btr_check_node_ptr( offsets = btr_page_get_father_block(NULL, heap, mtr, &cursor); } - ut_ad(offsets); - if (page_is_leaf(page)) { goto func_exit; @@ -4917,16 +4793,19 @@ btr_validate_level( page_zip_des_t* page_zip; #endif /* UNIV_ZIP_DEBUG */ ulint savepoint = 0; + ulint savepoint2 = 0; uint32_t parent_page_no = FIL_NULL; uint32_t parent_right_page_no = FIL_NULL; bool rightmost_child = false; mtr.start(); - if (lockout) { - mtr_x_lock_index(index, &mtr); - } else { - mtr_sx_lock_index(index, &mtr); + if (!srv_read_only_mode) { + if (lockout) { + mtr_x_lock_index(index, &mtr); + } else { + mtr_sx_lock_index(index, &mtr); + } } dberr_t err; @@ -4974,6 +4853,7 @@ corrupted: offsets = rec_get_offsets(node_ptr, index, offsets, 0, ULINT_UNDEFINED, &heap); + savepoint2 = mtr_set_savepoint(&mtr); block = btr_node_ptr_get_child(node_ptr, index, offsets, &mtr, &err); if (!block) { @@ -4994,8 +4874,10 @@ corrupted: /* To obey latch order of tree blocks, we should release the right_block once to obtain lock of the uncle block. */ - mtr.release_last_page(); + mtr_release_block_at_savepoint( + &mtr, savepoint2, block); + savepoint2 = mtr_set_savepoint(&mtr); block = btr_block_get(*index, left_page_no, RW_SX_LATCH, false, &mtr, &err); @@ -5023,10 +4905,12 @@ func_exit: mem_heap_empty(heap); offsets = offsets2 = NULL; - if (lockout) { - mtr_x_lock_index(index, &mtr); - } else { - mtr_sx_lock_index(index, &mtr); + if (!srv_read_only_mode) { + if (lockout) { + mtr_x_lock_index(index, &mtr); + } else { + mtr_sx_lock_index(index, &mtr); + } } page = block->page.frame; @@ -5071,7 +4955,7 @@ func_exit: if (right_page_no != FIL_NULL) { const rec_t* right_rec; - savepoint = mtr.get_savepoint(); + savepoint = mtr_set_savepoint(&mtr); right_block = btr_block_get(*index, right_page_no, RW_SX_LATCH, !level, &mtr, &err); @@ -5266,10 +5150,8 @@ broken_links: /* To obey latch order of tree blocks, we should release the right_block once to obtain lock of the uncle block. */ - ut_ad(right_block - == mtr.at_savepoint(savepoint)); - mtr.rollback_to_savepoint(savepoint, - savepoint + 1); + mtr_release_block_at_savepoint( + &mtr, savepoint, right_block); if (parent_right_page_no != FIL_NULL) { btr_block_get(*index, diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index b3bfb74bb8b..ac06d9b1568 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -3,7 +3,7 @@ Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2015, 2023, MariaDB Corporation. +Copyright (c) 2015, 2022, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -103,14 +103,14 @@ throughput clearly from about 100000. */ #define BTR_CUR_FINE_HISTORY_LENGTH 100000 #ifdef BTR_CUR_HASH_ADAPT -/** Number of searches down the B-tree in btr_cur_t::search_leaf(). */ +/** Number of searches down the B-tree in btr_cur_search_to_nth_level(). */ ib_counter_t btr_cur_n_non_sea; /** Old value of btr_cur_n_non_sea. Copied by srv_refresh_innodb_monitor_stats(). Referenced by srv_printf_innodb_monitor(). */ ulint btr_cur_n_non_sea_old; /** Number of successful adaptive hash index lookups in -btr_cur_t::search_leaf(). */ +btr_cur_search_to_nth_level(). */ ib_counter_t btr_cur_n_sea; /** Old value of btr_cur_n_sea. Copied by srv_refresh_innodb_monitor_stats(). Referenced by @@ -188,106 +188,164 @@ btr_rec_free_externally_stored_fields( /*==================== B-TREE SEARCH =========================*/ /** Latches the leaf page or pages requested. -@param[in] block_savepoint leaf page where the search converged +@param[in] block leaf page where the search converged @param[in] latch_mode BTR_SEARCH_LEAF, ... @param[in] cursor cursor -@param[in] mtr mini-transaction */ +@param[in] mtr mini-transaction +@param[out] latch_leaves latched blocks and savepoints */ void btr_cur_latch_leaves( - ulint block_savepoint, + buf_block_t* block, btr_latch_mode latch_mode, btr_cur_t* cursor, - mtr_t* mtr) + mtr_t* mtr, + btr_latch_leaves_t* latch_leaves) { compile_time_assert(int(MTR_MEMO_PAGE_S_FIX) == int(RW_S_LATCH)); compile_time_assert(int(MTR_MEMO_PAGE_X_FIX) == int(RW_X_LATCH)); compile_time_assert(int(MTR_MEMO_PAGE_SX_FIX) == int(RW_SX_LATCH)); - - buf_block_t* block = mtr->at_savepoint(block_savepoint); - ut_ad(block->page.id().space() == cursor->index()->table->space->id); ut_ad(block->page.in_file()); - ut_ad(mtr->memo_contains_flagged(&cursor->index()->lock, - MTR_MEMO_S_LOCK - | MTR_MEMO_X_LOCK - | MTR_MEMO_SX_LOCK)); + ut_ad(srv_read_only_mode + || mtr->memo_contains_flagged(&cursor->index()->lock, + MTR_MEMO_S_LOCK + | MTR_MEMO_X_LOCK + | MTR_MEMO_SX_LOCK)); + auto rtr_info = cursor->rtr_info; + if (UNIV_LIKELY_NULL(rtr_info) && !cursor->index()->is_spatial()) { + rtr_info = nullptr; + } + const rw_lock_type_t mode = rw_lock_type_t( latch_mode & (RW_X_LATCH | RW_S_LATCH)); static_assert(ulint{RW_S_LATCH} == ulint{BTR_SEARCH_LEAF}, ""); static_assert(ulint{RW_X_LATCH} == ulint{BTR_MODIFY_LEAF}, ""); + static_assert(BTR_SEARCH_LEAF & BTR_SEARCH_TREE, ""); switch (latch_mode) { + default: + break; uint32_t left_page_no; uint32_t right_page_no; - default: - ut_ad(latch_mode == BTR_CONT_MODIFY_TREE); - ut_ad(cursor->index()->is_spatial()); - break; + ulint save; case BTR_SEARCH_LEAF: - s_latch_block: - block->page.lock.s_lock(); -#ifdef BTR_CUR_HASH_ADAPT - btr_search_drop_page_hash_index(block, true); -#endif - mtr->lock_register(block_savepoint, MTR_MEMO_PAGE_S_FIX); - break; + case BTR_MODIFY_LEAF: + case BTR_SEARCH_TREE: + if (UNIV_LIKELY_NULL(rtr_info)) { + rtr_info->tree_savepoints[RTR_MAX_LEVELS] + = mtr->get_savepoint(); + } +latch_block: + if (latch_leaves) { + latch_leaves->savepoints[1] = mtr->get_savepoint(); + latch_leaves->blocks[1] = block; + } + block->page.fix(); + mtr->page_lock(block, mode); + if (UNIV_LIKELY_NULL(rtr_info)) { + rtr_info->tree_blocks[RTR_MAX_LEVELS] = block; + } + return; case BTR_MODIFY_TREE: /* It is exclusive for other operations which calls btr_page_set_prev() */ ut_ad(mtr->memo_contains_flagged(&cursor->index()->lock, MTR_MEMO_X_LOCK | MTR_MEMO_SX_LOCK)); + save = mtr->get_savepoint(); /* x-latch also siblings from left to right */ left_page_no = btr_page_get_prev(block->page.frame); if (left_page_no != FIL_NULL) { - btr_block_get(*cursor->index(), left_page_no, RW_X_LATCH, - true, mtr); + buf_block_t *b = btr_block_get( + *cursor->index(), left_page_no, RW_X_LATCH, + true, mtr); + + if (latch_leaves) { + latch_leaves->savepoints[0] = save; + latch_leaves->blocks[0] = b; + } + + if (UNIV_LIKELY_NULL(rtr_info)) { + rtr_info->tree_savepoints[RTR_MAX_LEVELS] + = save; + rtr_info->tree_blocks[RTR_MAX_LEVELS] = b; + } + + save = mtr->get_savepoint(); + } + + if (latch_leaves) { + latch_leaves->savepoints[1] = mtr->get_savepoint(); + latch_leaves->blocks[1] = block; } - mtr->x_latch_at_savepoint(block_savepoint, block); + block->page.fix(); + block->page.lock.x_lock(); + + mtr->memo_push(block, MTR_MEMO_PAGE_X_FIX); #ifdef BTR_CUR_HASH_ADAPT - btr_search_drop_page_hash_index(block, true); + ut_ad(!btr_search_check_marked_free_index(block)); #endif + if (UNIV_LIKELY_NULL(rtr_info)) { + rtr_info->tree_savepoints[RTR_MAX_LEVELS + 1] = save; + rtr_info->tree_blocks[RTR_MAX_LEVELS + 1] = block; + } + right_page_no = btr_page_get_next(block->page.frame); if (right_page_no != FIL_NULL) { - btr_block_get(*cursor->index(), right_page_no, - RW_X_LATCH, true, mtr); + save = mtr->get_savepoint(); + + buf_block_t* b = btr_block_get( + *cursor->index(), right_page_no, RW_X_LATCH, + true, mtr); + if (latch_leaves) { + latch_leaves->savepoints[2] = save; + latch_leaves->blocks[2] = b; + } + + if (UNIV_LIKELY_NULL(rtr_info)) { + rtr_info->tree_savepoints[RTR_MAX_LEVELS + 2] + = save; + rtr_info->tree_blocks[RTR_MAX_LEVELS + 2] = b; + } } - break; + + return; case BTR_SEARCH_PREV: case BTR_MODIFY_PREV: - static_assert(BTR_MODIFY_PREV & BTR_MODIFY_LEAF, ""); + ut_ad(!rtr_info); static_assert(BTR_SEARCH_PREV & BTR_SEARCH_LEAF, ""); - ut_ad(cursor->index()->is_ibuf() - || mtr->memo_contains_flagged(&cursor->index()->lock, - MTR_MEMO_X_LOCK - | MTR_MEMO_SX_LOCK)); + static_assert(BTR_MODIFY_PREV & BTR_MODIFY_LEAF, ""); + static_assert((BTR_SEARCH_PREV ^ BTR_MODIFY_PREV) + == (RW_S_LATCH ^ RW_X_LATCH), ""); + /* Because we are holding index->lock, no page splits or merges may run concurrently, and we may read FIL_PAGE_PREV from a buffer-fixed, unlatched page. */ left_page_no = btr_page_get_prev(block->page.frame); if (left_page_no != FIL_NULL) { + save = mtr->get_savepoint(); cursor->left_block = btr_block_get( *cursor->index(), left_page_no, mode, true, mtr); + if (latch_leaves) { + latch_leaves->savepoints[0] = save; + latch_leaves->blocks[0] = cursor->left_block; + } } - if (latch_mode == BTR_SEARCH_PREV) { - goto s_latch_block; - } - - /* fall through */ - case BTR_MODIFY_LEAF: - mtr->x_latch_at_savepoint(block_savepoint, block); -#ifdef BTR_CUR_HASH_ADAPT - btr_search_drop_page_hash_index(block, true); -#endif + goto latch_block; + case BTR_CONT_MODIFY_TREE: + ut_ad(cursor->index()->is_spatial()); + return; } + + MY_ASSERT_UNREACHABLE(); } /** Load the instant ALTER TABLE metadata from the clustered index @@ -671,6 +729,98 @@ bool btr_cur_instant_root_init(dict_index_t* index, const page_t* page) return index->n_core_null_bytes > 128; } +/** Optimistically latches the leaf page or pages requested. +@param[in] block guessed buffer block +@param[in] modify_clock modify clock value +@param[in,out] latch_mode BTR_SEARCH_LEAF, ... +@param[in,out] cursor cursor +@param[in] mtr mini-transaction +@return true if success */ +TRANSACTIONAL_TARGET +bool +btr_cur_optimistic_latch_leaves( + buf_block_t* block, + ib_uint64_t modify_clock, + btr_latch_mode* latch_mode, + btr_cur_t* cursor, + mtr_t* mtr) +{ + ut_ad(block->page.buf_fix_count()); + ut_ad(block->page.in_file()); + ut_ad(block->page.frame); + + switch (*latch_mode) { + default: + MY_ASSERT_UNREACHABLE(); + return(false); + case BTR_SEARCH_LEAF: + case BTR_MODIFY_LEAF: + return(buf_page_optimistic_get(*latch_mode, block, + modify_clock, mtr)); + case BTR_SEARCH_PREV: /* btr_pcur_move_backward_from_page() */ + case BTR_MODIFY_PREV: /* Ditto, or ibuf_insert() */ + uint32_t curr_page_no, left_page_no; + { + transactional_shared_lock_guard g{ + block->page.lock}; + if (block->modify_clock != modify_clock) { + return false; + } + curr_page_no = block->page.id().page_no(); + left_page_no = btr_page_get_prev(block->page.frame); + } + + static_assert(BTR_SEARCH_PREV & BTR_SEARCH_LEAF, ""); + static_assert(BTR_MODIFY_PREV & BTR_MODIFY_LEAF, ""); + static_assert((BTR_SEARCH_PREV ^ BTR_MODIFY_PREV) + == (RW_S_LATCH ^ RW_X_LATCH), ""); + + const rw_lock_type_t mode = rw_lock_type_t( + *latch_mode & (RW_X_LATCH | RW_S_LATCH)); + + if (left_page_no != FIL_NULL) { + cursor->left_block = buf_page_get_gen( + page_id_t(cursor->index()->table->space_id, + left_page_no), + cursor->index()->table->space->zip_size(), + mode, nullptr, BUF_GET_POSSIBLY_FREED, mtr); + + if (cursor->left_block + && btr_page_get_next( + cursor->left_block->page.frame) + != curr_page_no) { +release_left_block: + mtr->release_last_page(); + return false; + } + } else { + cursor->left_block = nullptr; + } + + if (buf_page_optimistic_get(mode, block, modify_clock, mtr)) { + if (btr_page_get_prev(block->page.frame) + == left_page_no) { + /* block was already buffer-fixed while + entering the function and + buf_page_optimistic_get() buffer-fixes + it again. */ + ut_ad(2 <= block->page.buf_fix_count()); + *latch_mode = btr_latch_mode(mode); + return(true); + } + + mtr->release_last_page(); + } + + ut_ad(block->page.buf_fix_count()); + if (cursor->left_block) { + goto release_left_block; + } + } + + return false; +} + /** Gets intention in btr_intention_t from latch_mode, and cleares the intention at the latch_mode. @@ -698,6 +848,38 @@ btr_intention_t btr_cur_get_and_clear_intention(btr_latch_mode *latch_mode) return(intention); } +/** +Gets the desired latch type for the root leaf (root page is root leaf) +at the latch mode. +@param latch_mode in: BTR_SEARCH_LEAF, ... +@return latch type */ +static +rw_lock_type_t +btr_cur_latch_for_root_leaf( + ulint latch_mode) +{ + switch (latch_mode) { + case BTR_SEARCH_LEAF: + case BTR_SEARCH_TREE: + case BTR_SEARCH_PREV: + return(RW_S_LATCH); + case BTR_MODIFY_LEAF: + case BTR_MODIFY_TREE: + case BTR_MODIFY_PREV: + return(RW_X_LATCH); + case BTR_CONT_MODIFY_TREE: + case BTR_CONT_SEARCH_TREE: + /* A root page should be latched already, + and don't need to be latched here. + fall through (RW_NO_LATCH) */ + case BTR_NO_LATCHES: + return(RW_NO_LATCH); + } + + MY_ASSERT_UNREACHABLE(); + return(RW_NO_LATCH); /* avoid compiler warnings */ +} + /** @return whether the distance between two records is at most the specified value */ static bool @@ -1015,841 +1197,1223 @@ static ulint btr_node_ptr_max_size(const dict_index_t* index) return rec_max_size; } -/** @return a B-tree search mode suitable for non-leaf pages -@param mode leaf page search mode */ -static inline page_cur_mode_t btr_cur_nonleaf_mode(page_cur_mode_t mode) -{ - if (mode > PAGE_CUR_GE) - { - ut_ad(mode == PAGE_CUR_L || mode == PAGE_CUR_LE); - return mode; - } - if (mode == PAGE_CUR_GE) - return PAGE_CUR_L; - ut_ad(mode == PAGE_CUR_G); - return PAGE_CUR_LE; -} +/********************************************************************//** +Searches an index tree and positions a tree cursor on a given level. +NOTE: n_fields_cmp in tuple must be set so that it cannot be compared +to node pointer page number fields on the upper levels of the tree! +Note that if mode is PAGE_CUR_LE, which is used in inserts, then +cursor->up_match and cursor->low_match both will have sensible values. +If mode is PAGE_CUR_GE, then up_match will a have a sensible value. -dberr_t btr_cur_t::search_leaf(const dtuple_t *tuple, page_cur_mode_t mode, - btr_latch_mode latch_mode, mtr_t *mtr) +If mode is PAGE_CUR_LE , cursor is left at the place where an insert of the +search tuple should be performed in the B-tree. InnoDB does an insert +immediately after the cursor. Thus, the cursor may end up on a user record, +or on a page infimum record. +@param level the tree level of search +@param tuple data tuple; NOTE: n_fields_cmp in tuple must be set so that + it cannot get compared to the node ptr page number field! +@param mode PAGE_CUR_L, ...; NOTE that if the search is made using a + unique prefix of a record, mode should be PAGE_CUR_LE, not + PAGE_CUR_GE, as the latter may end up on the previous page of + the record! Inserts should always be made using PAGE_CUR_LE + to search the position! +@param latch_mode BTR_SEARCH_LEAF, ..., ORed with at most one of BTR_INSERT, + BTR_DELETE_MARK, or BTR_DELETE; + cursor->left_block is used to store a pointer to the left + neighbor page +@param cursor tree cursor; the cursor page is s- or x-latched, but see also + above! +@param mtr mini-transaction +@param autoinc PAGE_ROOT_AUTO_INC to be written (0 if none) +@return DB_SUCCESS on success or error code otherwise */ +TRANSACTIONAL_TARGET +dberr_t btr_cur_search_to_nth_level(ulint level, + const dtuple_t *tuple, + page_cur_mode_t mode, + btr_latch_mode latch_mode, + btr_cur_t *cursor, mtr_t *mtr, + ib_uint64_t autoinc) { - ut_ad(index()->is_btree() || index()->is_ibuf()); - ut_ad(!index()->is_ibuf() || ibuf_inside(mtr)); + page_t* page = NULL; /* remove warning */ + buf_block_t* block; + buf_block_t* guess; + ulint height; + ulint up_match; + ulint up_bytes; + ulint low_match; + ulint low_bytes; + ulint rw_latch; + page_cur_mode_t page_mode; + page_cur_mode_t search_mode = PAGE_CUR_UNSUPP; + ulint buf_mode; + ulint node_ptr_max_size = srv_page_size / 2; + page_cur_t* page_cursor; + btr_op_t btr_op; + ulint root_height = 0; /* remove warning */ + + btr_intention_t lock_intention; + buf_block_t* tree_blocks[BTR_MAX_LEVELS]; + ulint tree_savepoints[BTR_MAX_LEVELS]; + ulint n_blocks = 0; + ulint n_releases = 0; + bool detected_same_key_root = false; + + ulint leftmost_from_level = 0; + buf_block_t** prev_tree_blocks = NULL; + ulint* prev_tree_savepoints = NULL; + ulint prev_n_blocks = 0; + ulint prev_n_releases = 0; + bool need_path = true; + bool rtree_parent_modified = false; + bool mbr_adj = false; + bool found = false; + dict_index_t * const index = cursor->index(); + + DBUG_ENTER("btr_cur_search_to_nth_level"); - buf_block_t *guess; - btr_op_t btr_op; - btr_intention_t lock_intention; - bool detected_same_key_root= false; +#ifdef BTR_CUR_ADAPT + btr_search_t* info; +#endif /* BTR_CUR_ADAPT */ + mem_heap_t* heap = NULL; + rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; + rec_offs* offsets = offsets_; + rec_offs offsets2_[REC_OFFS_NORMAL_SIZE]; + rec_offs* offsets2 = offsets2_; + rec_offs_init(offsets_); + rec_offs_init(offsets2_); + /* Currently, PAGE_CUR_LE is the only search mode used for searches + ending to upper levels */ + + ut_ad(level == 0 || mode == PAGE_CUR_LE + || RTREE_SEARCH_MODE(mode)); + ut_ad(dict_index_check_search_tuple(index, tuple)); + ut_ad(!dict_index_is_ibuf(index) || ibuf_inside(mtr)); + ut_ad(dtuple_check_typed(tuple)); + ut_ad(!(index->type & DICT_FTS)); + ut_ad(index->page != FIL_NULL); - mem_heap_t* heap = NULL; - rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; - rec_offs* offsets = offsets_; - rec_offs offsets2_[REC_OFFS_NORMAL_SIZE]; - rec_offs* offsets2 = offsets2_; - rec_offs_init(offsets_); - rec_offs_init(offsets2_); - - ut_ad(dict_index_check_search_tuple(index(), tuple)); - ut_ad(dtuple_check_typed(tuple)); - ut_ad(index()->page != FIL_NULL); - - MEM_UNDEFINED(&up_match, sizeof up_match); - MEM_UNDEFINED(&up_bytes, sizeof up_bytes); - MEM_UNDEFINED(&low_match, sizeof low_match); - MEM_UNDEFINED(&low_bytes, sizeof low_bytes); - ut_d(up_match= ULINT_UNDEFINED); - ut_d(low_match= ULINT_UNDEFINED); - - ut_ad(!(latch_mode & BTR_ALREADY_S_LATCHED) || - mtr->memo_contains_flagged(&index()->lock, - MTR_MEMO_S_LOCK | MTR_MEMO_SX_LOCK | - MTR_MEMO_X_LOCK)); - - /* These flags are mutually exclusive, they are lumped together - with the latch mode for historical reasons. It's possible for - none of the flags to be set. */ - switch (UNIV_EXPECT(latch_mode & BTR_DELETE, 0)) { - default: - btr_op= BTR_NO_OP; - break; - case BTR_INSERT: - btr_op= (latch_mode & BTR_IGNORE_SEC_UNIQUE) - ? BTR_INSERT_IGNORE_UNIQUE_OP - : BTR_INSERT_OP; - break; - case BTR_DELETE: - btr_op= BTR_DELETE_OP; - ut_a(purge_node); - break; - case BTR_DELETE_MARK: - btr_op= BTR_DELMARK_OP; - break; - } + MEM_UNDEFINED(&cursor->up_match, sizeof cursor->up_match); + MEM_UNDEFINED(&cursor->up_bytes, sizeof cursor->up_bytes); + MEM_UNDEFINED(&cursor->low_match, sizeof cursor->low_match); + MEM_UNDEFINED(&cursor->low_bytes, sizeof cursor->low_bytes); +#ifdef UNIV_DEBUG + cursor->up_match = ULINT_UNDEFINED; + cursor->low_match = ULINT_UNDEFINED; +#endif /* UNIV_DEBUG */ - /* Operations on the insert buffer tree cannot be buffered. */ - ut_ad(btr_op == BTR_NO_OP || !index()->is_ibuf()); - /* Operations on the clustered index cannot be buffered. */ - ut_ad(btr_op == BTR_NO_OP || !index()->is_clust()); - /* Operations on the temporary table(indexes) cannot be buffered. */ - ut_ad(btr_op == BTR_NO_OP || !index()->table->is_temporary()); + const bool latch_by_caller = latch_mode & BTR_ALREADY_S_LATCHED; - const bool latch_by_caller= latch_mode & BTR_ALREADY_S_LATCHED; - lock_intention= btr_cur_get_and_clear_intention(&latch_mode); - latch_mode= BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); + ut_ad(!latch_by_caller + || srv_read_only_mode + || mtr->memo_contains_flagged(&index->lock, MTR_MEMO_S_LOCK + | MTR_MEMO_SX_LOCK)); - ut_ad(!latch_by_caller - || latch_mode == BTR_SEARCH_LEAF - || latch_mode == BTR_MODIFY_LEAF - || latch_mode == BTR_MODIFY_TREE - || latch_mode == BTR_MODIFY_ROOT_AND_LEAF); + /* These flags are mutually exclusive, they are lumped together + with the latch mode for historical reasons. It's possible for + none of the flags to be set. */ + switch (UNIV_EXPECT(latch_mode & BTR_DELETE, 0)) { + default: + btr_op = BTR_NO_OP; + break; + case BTR_INSERT: + btr_op = (latch_mode & BTR_IGNORE_SEC_UNIQUE) + ? BTR_INSERT_IGNORE_UNIQUE_OP + : BTR_INSERT_OP; + break; + case BTR_DELETE: + btr_op = BTR_DELETE_OP; + ut_a(cursor->purge_node); + break; + case BTR_DELETE_MARK: + btr_op = BTR_DELMARK_OP; + break; + } + + /* Operations on the insert buffer tree cannot be buffered. */ + ut_ad(btr_op == BTR_NO_OP || !dict_index_is_ibuf(index)); + /* Operations on the clustered index cannot be buffered. */ + ut_ad(btr_op == BTR_NO_OP || !dict_index_is_clust(index)); + /* Operations on the temporary table(indexes) cannot be buffered. */ + ut_ad(btr_op == BTR_NO_OP || !index->table->is_temporary()); + /* Operation on the spatial index cannot be buffered. */ + ut_ad(btr_op == BTR_NO_OP || !dict_index_is_spatial(index)); + + lock_intention = btr_cur_get_and_clear_intention(&latch_mode); + + /* Turn the flags unrelated to the latch mode off. */ + latch_mode = BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); + + ut_ad(!latch_by_caller + || latch_mode == BTR_SEARCH_LEAF + || latch_mode == BTR_SEARCH_TREE + || latch_mode == BTR_MODIFY_LEAF); + + ut_ad(autoinc == 0 || dict_index_is_clust(index)); + ut_ad(autoinc == 0 + || latch_mode == BTR_MODIFY_TREE + || latch_mode == BTR_MODIFY_LEAF); + ut_ad(autoinc == 0 || level == 0); + + cursor->flag = BTR_CUR_BINARY; - flag= BTR_CUR_BINARY; #ifndef BTR_CUR_ADAPT - guess= nullptr; + guess = NULL; #else - btr_search_t *info= btr_search_get_info(index()); - guess= info->root_guess; - -# ifdef BTR_CUR_HASH_ADAPT -# ifdef UNIV_SEARCH_PERF_STAT - info->n_searches++; -# endif - /* We do a dirty read of btr_search_enabled below, - and btr_search_guess_on_hash() will have to check it again. */ - if (!btr_search_enabled); - else if (btr_search_guess_on_hash(index(), info, tuple, mode, - latch_mode, this, mtr)) - { - /* Search using the hash index succeeded */ - ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_GE); - ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); - ut_ad(low_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); - ++btr_cur_n_sea; + info = btr_search_get_info(index); + guess = info->root_guess; - return DB_SUCCESS; - } - else - ++btr_cur_n_non_sea; +#ifdef BTR_CUR_HASH_ADAPT + +# ifdef UNIV_SEARCH_PERF_STAT + info->n_searches++; # endif -#endif + /* We do a dirty read of btr_search_enabled below, + and btr_search_guess_on_hash() will have to check it again. */ + if (!btr_search_enabled) { + } else if (autoinc == 0 + && latch_mode <= BTR_MODIFY_LEAF +# ifdef PAGE_CUR_LE_OR_EXTENDS + && mode != PAGE_CUR_LE_OR_EXTENDS +# endif /* PAGE_CUR_LE_OR_EXTENDS */ + && info->last_hash_succ + && !(tuple->info_bits & REC_INFO_MIN_REC_FLAG) + && !index->is_spatial() && !index->table->is_temporary() + && btr_search_guess_on_hash(index, info, tuple, mode, + latch_mode, cursor, mtr)) { + + /* Search using the hash index succeeded */ + + ut_ad(cursor->up_match != ULINT_UNDEFINED + || mode != PAGE_CUR_GE); + ut_ad(cursor->up_match != ULINT_UNDEFINED + || mode != PAGE_CUR_LE); + ut_ad(cursor->low_match != ULINT_UNDEFINED + || mode != PAGE_CUR_LE); + ++btr_cur_n_sea; + + DBUG_RETURN(DB_SUCCESS); + } else { + ++btr_cur_n_non_sea; + } +# endif /* BTR_CUR_HASH_ADAPT */ +#endif /* BTR_CUR_ADAPT */ - /* If the hash search did not succeed, do binary search down the - tree */ + /* If the hash search did not succeed, do binary search down the + tree */ - /* Store the position of the tree latch we push to mtr so that we - know how to release it when we have latched leaf node(s) */ + /* Store the position of the tree latch we push to mtr so that we + know how to release it when we have latched leaf node(s) */ - const ulint savepoint = mtr->get_savepoint(); + ulint savepoint = mtr_set_savepoint(mtr); - ulint node_ptr_max_size= 0; - rw_lock_type_t rw_latch= RW_S_LATCH; + rw_lock_type_t upper_rw_latch; - switch (latch_mode) { - case BTR_MODIFY_TREE: - rw_latch= RW_X_LATCH; - node_ptr_max_size= btr_node_ptr_max_size(index()); - if (latch_by_caller) - { - ut_ad(mtr->memo_contains_flagged(&index()->lock, MTR_MEMO_X_LOCK)); - break; - } - if (lock_intention == BTR_INTENTION_DELETE && buf_pool.n_pend_reads && - trx_sys.history_size_approx() > BTR_CUR_FINE_HISTORY_LENGTH) - /* Most delete-intended operations are due to the purge of history. - Prioritize them when the history list is growing huge. */ - mtr_x_lock_index(index(), mtr); - else - mtr_sx_lock_index(index(), mtr); - break; -#ifdef UNIV_DEBUG - case BTR_CONT_MODIFY_TREE: - ut_ad("invalid mode" == 0); - break; + switch (latch_mode) { + case BTR_MODIFY_TREE: + /* Most of delete-intended operations are purging. + Free blocks and read IO bandwidth should be prior + for them, when the history list is glowing huge. */ + if (lock_intention == BTR_INTENTION_DELETE + && buf_pool.n_pend_reads + && trx_sys.history_size_approx() + > BTR_CUR_FINE_HISTORY_LENGTH) { +x_latch_index: + mtr_x_lock_index(index, mtr); + } else if (index->is_spatial() + && lock_intention <= BTR_INTENTION_BOTH) { + /* X lock the if there is possibility of + pessimistic delete on spatial index. As we could + lock upward for the tree */ + goto x_latch_index; + } else { + mtr_sx_lock_index(index, mtr); + } + upper_rw_latch = RW_X_LATCH; + break; + case BTR_CONT_MODIFY_TREE: + ut_ad(srv_read_only_mode + || mtr->memo_contains_flagged(&index->lock, + MTR_MEMO_X_LOCK + | MTR_MEMO_SX_LOCK)); + if (index->is_spatial()) { + /* If we are about to locate parent page for split + and/or merge operation for R-Tree index, X latch + the parent */ + upper_rw_latch = RW_X_LATCH; + break; + } + /* fall through */ + case BTR_CONT_SEARCH_TREE: + /* Do nothing */ + ut_ad(srv_read_only_mode + || mtr->memo_contains_flagged(&index->lock, + MTR_MEMO_X_LOCK + | MTR_MEMO_SX_LOCK)); + upper_rw_latch = RW_NO_LATCH; + break; + default: + if (!srv_read_only_mode) { + if (!latch_by_caller) { + ut_ad(latch_mode != BTR_SEARCH_TREE); + mtr_s_lock_index(index, mtr); + } + upper_rw_latch = RW_S_LATCH; + } else { + upper_rw_latch = RW_NO_LATCH; + } + } + const rw_lock_type_t root_leaf_rw_latch = btr_cur_latch_for_root_leaf( + latch_mode); + + page_cursor = btr_cur_get_page_cur(cursor); + page_cursor->index = index; + + const ulint zip_size = index->table->space->zip_size(); + + /* Start with the root page. */ + page_id_t page_id(index->table->space_id, index->page); + + if (root_leaf_rw_latch == RW_X_LATCH) { + node_ptr_max_size = btr_node_ptr_max_size(index); + } + + up_match = 0; + up_bytes = 0; + low_match = 0; + low_bytes = 0; + + height = ULINT_UNDEFINED; + + /* We use these modified search modes on non-leaf levels of the + B-tree. These let us end up in the right B-tree leaf. In that leaf + we use the original search mode. */ + + switch (mode) { + case PAGE_CUR_GE: + page_mode = PAGE_CUR_L; + break; + case PAGE_CUR_G: + page_mode = PAGE_CUR_LE; + break; + default: +#ifdef PAGE_CUR_LE_OR_EXTENDS + ut_ad(mode == PAGE_CUR_L || mode == PAGE_CUR_LE + || RTREE_SEARCH_MODE(mode) + || mode == PAGE_CUR_LE_OR_EXTENDS); +#else /* PAGE_CUR_LE_OR_EXTENDS */ + ut_ad(mode == PAGE_CUR_L || mode == PAGE_CUR_LE + || RTREE_SEARCH_MODE(mode)); +#endif /* PAGE_CUR_LE_OR_EXTENDS */ + page_mode = mode; + break; + } + + /* Loop and search until we arrive at the desired level */ + btr_latch_leaves_t latch_leaves = {{NULL, NULL, NULL}, {0, 0, 0}}; + +search_loop: + buf_mode = BUF_GET; + rw_latch = RW_NO_LATCH; + rtree_parent_modified = false; + + if (height != 0) { + /* We are about to fetch the root or a non-leaf page. */ + if ((latch_mode != BTR_MODIFY_TREE || height == level) + && !prev_tree_blocks) { + /* If doesn't have SX or X latch of index, + each pages should be latched before reading. */ + if (height == ULINT_UNDEFINED + && upper_rw_latch == RW_S_LATCH + && autoinc) { + /* needs sx-latch of root page + for writing PAGE_ROOT_AUTO_INC */ + rw_latch = RW_SX_LATCH; + } else { + rw_latch = upper_rw_latch; + } + } + } else if (latch_mode <= BTR_MODIFY_LEAF) { + rw_latch = latch_mode; + + if (btr_op != BTR_NO_OP + && ibuf_should_try(index, btr_op != BTR_INSERT_OP)) { + + /* Try to buffer the operation if the leaf + page is not in the buffer pool. */ + + buf_mode = btr_op == BTR_DELETE_OP + ? BUF_GET_IF_IN_POOL_OR_WATCH + : BUF_GET_IF_IN_POOL; + } + } + +retry_page_get: + ut_ad(n_blocks < BTR_MAX_LEVELS); + tree_savepoints[n_blocks] = mtr_set_savepoint(mtr); + dberr_t err; + block = buf_page_get_gen(page_id, zip_size, rw_latch, guess, + buf_mode, mtr, &err, + height == 0 && !index->is_clust()); + if (!block) { + switch (err) { + case DB_SUCCESS: + /* change buffering */ + break; + case DB_DECRYPTION_FAILED: + btr_decryption_failed(*index); + /* fall through */ + default: + goto func_exit; + } + + /* This must be a search to perform an insert/delete + mark/ delete; try using the insert/delete buffer */ + + ut_ad(height == 0); + ut_ad(cursor->thr); + + switch (btr_op) { + default: + MY_ASSERT_UNREACHABLE(); + break; + case BTR_INSERT_OP: + case BTR_INSERT_IGNORE_UNIQUE_OP: + ut_ad(buf_mode == BUF_GET_IF_IN_POOL); + ut_ad(!dict_index_is_spatial(index)); + + if (ibuf_insert(IBUF_OP_INSERT, tuple, index, + page_id, zip_size, cursor->thr)) { + + cursor->flag = BTR_CUR_INSERT_TO_IBUF; + + goto func_exit; + } + break; + + case BTR_DELMARK_OP: + ut_ad(buf_mode == BUF_GET_IF_IN_POOL); + ut_ad(!dict_index_is_spatial(index)); + + if (ibuf_insert(IBUF_OP_DELETE_MARK, tuple, + index, page_id, zip_size, + cursor->thr)) { + + cursor->flag = BTR_CUR_DEL_MARK_IBUF; + + goto func_exit; + } + + break; + + case BTR_DELETE_OP: + ut_ad(buf_mode == BUF_GET_IF_IN_POOL_OR_WATCH); + ut_ad(!dict_index_is_spatial(index)); + auto& chain = buf_pool.page_hash.cell_get( + page_id.fold()); + + if (!row_purge_poss_sec(cursor->purge_node, + index, tuple)) { + + /* The record cannot be purged yet. */ + cursor->flag = BTR_CUR_DELETE_REF; + } else if (ibuf_insert(IBUF_OP_DELETE, tuple, + index, page_id, zip_size, + cursor->thr)) { + + /* The purge was buffered. */ + cursor->flag = BTR_CUR_DELETE_IBUF; + } else { + /* The purge could not be buffered. */ + buf_pool.watch_unset(page_id, chain); + break; + } + + buf_pool.watch_unset(page_id, chain); + goto func_exit; + } + + /* Insert to the insert/delete buffer did not succeed, we + must read the page from disk. */ + + buf_mode = BUF_GET; + + goto retry_page_get; + } + + tree_blocks[n_blocks] = block; + + if (height && prev_tree_blocks) { + /* also latch left sibling */ + ut_ad(rw_latch == RW_NO_LATCH); + + rw_latch = upper_rw_latch; + + /* Because we are holding index->lock, no page splits + or merges may run concurrently, and we may read + FIL_PAGE_PREV from a buffer-fixed, unlatched page. */ + uint32_t left_page_no = btr_page_get_prev(block->page.frame); + + if (left_page_no != FIL_NULL) { + ut_ad(prev_n_blocks < leftmost_from_level); + + prev_tree_savepoints[prev_n_blocks] + = mtr_set_savepoint(mtr); + buf_block_t* get_block = buf_page_get_gen( + page_id_t(page_id.space(), left_page_no), + zip_size, rw_latch, NULL, buf_mode, + mtr, &err); + if (!get_block) { + if (err == DB_DECRYPTION_FAILED) { + btr_decryption_failed(*index); + } + goto func_exit; + } + + prev_tree_blocks[prev_n_blocks++] = get_block; + /* BTR_MODIFY_TREE doesn't update prev/next_page_no, + without their parent page's lock. So, not needed to + retry here, because we have the parent page's lock. */ + } + + mtr->s_lock_register(tree_savepoints[n_blocks]); + block->page.lock.s_lock(); + } + + page = buf_block_get_frame(block); + + if (height == ULINT_UNDEFINED + && page_is_leaf(page) + && rw_latch != RW_NO_LATCH + && rw_latch != root_leaf_rw_latch) { + /* The root page is also a leaf page (root_leaf). + We should reacquire the page, because the root page + is latched differently from leaf pages. */ + ut_ad(root_leaf_rw_latch != RW_NO_LATCH); + ut_ad(rw_latch == RW_S_LATCH || rw_latch == RW_SX_LATCH); + ut_ad(rw_latch == RW_S_LATCH || autoinc); + ut_ad(!autoinc || root_leaf_rw_latch == RW_X_LATCH); + + ut_ad(n_blocks == 0); + mtr_release_block_at_savepoint( + mtr, tree_savepoints[n_blocks], + tree_blocks[n_blocks]); + + upper_rw_latch = root_leaf_rw_latch; + goto search_loop; + } + +#ifdef UNIV_ZIP_DEBUG + if (rw_latch != RW_NO_LATCH) { + const page_zip_des_t* page_zip + = buf_block_get_page_zip(block); + ut_a(!page_zip || page_zip_validate(page_zip, page, index)); + } +#endif /* UNIV_ZIP_DEBUG */ + + ut_ad(fil_page_index_page_check(page)); + ut_ad(index->id == btr_page_get_index_id(page)); + + if (height == ULINT_UNDEFINED) { + /* We are in the root node */ + + height = btr_page_get_level(page); + root_height = height; + cursor->tree_height = root_height + 1; + + if (dict_index_is_spatial(index)) { + ut_ad(cursor->rtr_info); + + /* If SSN in memory is not initialized, fetch + it from root page */ + if (!rtr_get_current_ssn_id(index)) { + /* FIXME: do this in dict_load_table_one() */ + index->set_ssn(page_get_ssn_id(page) + 1); + } + + /* Save the MBR */ + cursor->rtr_info->thr = cursor->thr; + rtr_get_mbr_from_tuple(tuple, &cursor->rtr_info->mbr); + } + +#ifdef BTR_CUR_ADAPT + info->root_guess = block; #endif - case BTR_MODIFY_ROOT_AND_LEAF: - rw_latch= RW_SX_LATCH; - /* fall through */ - default: - if (!latch_by_caller) - mtr_s_lock_index(index(), mtr); - } + } - const ulint zip_size= index()->table->space->zip_size(); + if (height == 0) { + if (rw_latch == RW_NO_LATCH) { + btr_cur_latch_leaves(block, latch_mode, cursor, mtr, + &latch_leaves); + } - /* Start with the root page. */ - page_id_t page_id(index()->table->space_id, index()->page); + switch (latch_mode) { + case BTR_MODIFY_TREE: + case BTR_CONT_MODIFY_TREE: + case BTR_CONT_SEARCH_TREE: + break; + default: + if (!latch_by_caller + && !srv_read_only_mode) { + /* Release the tree s-latch */ + mtr_release_s_latch_at_savepoint( + mtr, savepoint, + &index->lock); + } - const page_cur_mode_t page_mode= btr_cur_nonleaf_mode(mode); - ulint height= ULINT_UNDEFINED; - up_match= 0; - up_bytes= 0; - low_match= 0; - low_bytes= 0; - ulint buf_mode= BUF_GET; - search_loop: - dberr_t err; - auto block_savepoint= mtr->get_savepoint(); - buf_block_t *block= - buf_page_get_gen(page_id, zip_size, rw_latch, guess, buf_mode, mtr, - &err, height == 0 && !index()->is_clust()); - if (!block) - { - switch (err) { - case DB_DECRYPTION_FAILED: - btr_decryption_failed(*index()); - /* fall through */ - default: - func_exit: - if (UNIV_LIKELY_NULL(heap)) - mem_heap_free(heap); - return err; - case DB_SUCCESS: - /* This must be a search to perform an insert, delete mark, or delete; - try using the change buffer */ - ut_ad(height == 0); - ut_ad(thr); - break; - } + /* release upper blocks */ + if (prev_tree_blocks) { + ut_ad(!autoinc); + for (; + prev_n_releases < prev_n_blocks; + prev_n_releases++) { + mtr_release_block_at_savepoint( + mtr, + prev_tree_savepoints[ + prev_n_releases], + prev_tree_blocks[ + prev_n_releases]); + } + } - switch (btr_op) { - default: - MY_ASSERT_UNREACHABLE(); - break; - case BTR_INSERT_OP: - case BTR_INSERT_IGNORE_UNIQUE_OP: - ut_ad(buf_mode == BUF_GET_IF_IN_POOL); + for (; n_releases < n_blocks; n_releases++) { + if (n_releases == 0 + && (autoinc)) { + /* keep the root page latch */ + ut_ad(mtr->memo_contains_flagged( + tree_blocks[n_releases], + MTR_MEMO_PAGE_SX_FIX + | MTR_MEMO_PAGE_X_FIX)); + continue; + } - if (ibuf_insert(IBUF_OP_INSERT, tuple, index(), page_id, zip_size, thr)) - { - flag= BTR_CUR_INSERT_TO_IBUF; - goto func_exit; - } - break; + mtr_release_block_at_savepoint( + mtr, tree_savepoints[n_releases], + tree_blocks[n_releases]); + } + } - case BTR_DELMARK_OP: - ut_ad(buf_mode == BUF_GET_IF_IN_POOL); + page_mode = mode; + } - if (ibuf_insert(IBUF_OP_DELETE_MARK, tuple, - index(), page_id, zip_size, thr)) - { - flag = BTR_CUR_DEL_MARK_IBUF; - goto func_exit; - } + if (dict_index_is_spatial(index)) { + /* Remember the page search mode */ + search_mode = page_mode; - break; + /* Some adjustment on search mode, when the + page search mode is PAGE_CUR_RTREE_LOCATE + or PAGE_CUR_RTREE_INSERT, as we are searching + with MBRs. When it is not the target level, we + should search all sub-trees that "CONTAIN" the + search range/MBR. When it is at the target + level, the search becomes PAGE_CUR_LE */ + if (page_mode == PAGE_CUR_RTREE_LOCATE + && level == height) { + if (level == 0) { + page_mode = PAGE_CUR_LE; + } else { + page_mode = PAGE_CUR_RTREE_GET_FATHER; + } + } - case BTR_DELETE_OP: - ut_ad(buf_mode == BUF_GET_IF_IN_POOL_OR_WATCH); - auto& chain = buf_pool.page_hash.cell_get(page_id.fold()); - - if (!row_purge_poss_sec(purge_node, index(), tuple)) - /* The record cannot be purged yet. */ - flag= BTR_CUR_DELETE_REF; - else if (ibuf_insert(IBUF_OP_DELETE, tuple, index(), - page_id, zip_size, thr)) - /* The purge was buffered. */ - flag= BTR_CUR_DELETE_IBUF; - else - { - /* The purge could not be buffered. */ - buf_pool.watch_unset(page_id, chain); - break; - } + if (page_mode == PAGE_CUR_RTREE_INSERT) { + page_mode = (level == height) + ? PAGE_CUR_LE + : PAGE_CUR_RTREE_INSERT; - buf_pool.watch_unset(page_id, chain); - goto func_exit; - } + ut_ad(!page_is_leaf(page) || page_mode == PAGE_CUR_LE); + } - /* Change buffering did not succeed, we must read the page. */ - buf_mode= BUF_GET; - goto search_loop; - } + /* "need_path" indicates if we need to tracking the parent + pages, if it is not spatial comparison, then no need to + track it */ + if (page_mode < PAGE_CUR_CONTAIN) { + need_path = false; + } - if (!!page_is_comp(block->page.frame) != index()->table->not_redundant() || - btr_page_get_index_id(block->page.frame) != index()->id || - fil_page_get_type(block->page.frame) == FIL_PAGE_RTREE || - !fil_page_index_page_check(block->page.frame)) - { - corrupted: - ut_ad("corrupted" == 0); // FIXME: remove this - err= DB_CORRUPTION; - goto func_exit; - } + up_match = 0; + low_match = 0; + + if (latch_mode == BTR_MODIFY_TREE + || latch_mode == BTR_CONT_MODIFY_TREE + || latch_mode == BTR_CONT_SEARCH_TREE) { + /* Tree are locked, no need for Page Lock to protect + the "path" */ + cursor->rtr_info->need_page_lock = false; + } + } + + page_cursor->block = block; + + if (dict_index_is_spatial(index) && page_mode >= PAGE_CUR_CONTAIN) { + ut_ad(need_path); + found = rtr_cur_search_with_match( + block, index, tuple, page_mode, page_cursor, + cursor->rtr_info); + + /* Need to use BTR_MODIFY_TREE to do the MBR adjustment */ + if (search_mode == PAGE_CUR_RTREE_INSERT + && cursor->rtr_info->mbr_adj) { + static_assert(BTR_MODIFY_TREE + == (8 | BTR_MODIFY_LEAF), ""); + + if (!(latch_mode & 8)) { + /* Parent MBR needs updated, should retry + with BTR_MODIFY_TREE */ + goto func_exit; + } + + rtree_parent_modified = true; + cursor->rtr_info->mbr_adj = false; + mbr_adj = true; + } + + if (found && page_mode == PAGE_CUR_RTREE_GET_FATHER) { + cursor->low_match = + DICT_INDEX_SPATIAL_NODEPTR_SIZE + 1; + } +#ifdef BTR_CUR_HASH_ADAPT + } else if (height == 0 && btr_search_enabled + && !(tuple->info_bits & REC_INFO_MIN_REC_FLAG) + && index->is_btree()) { + /* The adaptive hash index is only used when searching + for leaf pages (height==0), but not in r-trees. + We only need the byte prefix comparison for the purpose + of updating the adaptive hash index. */ + if (page_cur_search_with_match_bytes( + tuple, page_mode, &up_match, &up_bytes, + &low_match, &low_bytes, page_cursor)) { + err = DB_CORRUPTION; + goto func_exit; + } +#endif /* BTR_CUR_HASH_ADAPT */ + } else { + /* Search for complete index fields. */ + up_bytes = low_bytes = 0; + if (page_cur_search_with_match( + tuple, page_mode, &up_match, + &low_match, page_cursor, + need_path ? cursor->rtr_info : nullptr)) { + err = DB_CORRUPTION; + goto func_exit; + } + } + + /* If this is the desired level, leave the loop */ + + ut_ad(height == btr_page_get_level(page_cur_get_page(page_cursor))); + + /* Add Predicate lock if it is serializable isolation + and only if it is in the search case */ + if (dict_index_is_spatial(index) + && cursor->rtr_info->need_prdt_lock + && mode != PAGE_CUR_RTREE_INSERT + && mode != PAGE_CUR_RTREE_LOCATE + && mode >= PAGE_CUR_CONTAIN) { + lock_prdt_t prdt; + + { + trx_t* trx = thr_get_trx(cursor->thr); + TMLockTrxGuard g{TMLockTrxArgs(*trx)}; + lock_init_prdt_from_mbr( + &prdt, &cursor->rtr_info->mbr, mode, + trx->lock.lock_heap); + } + + if (rw_latch == RW_NO_LATCH && height != 0) { + block->page.lock.s_lock(); + } + + lock_prdt_lock(block, &prdt, index, LOCK_S, + LOCK_PREDICATE, cursor->thr); + + if (rw_latch == RW_NO_LATCH && height != 0) { + block->page.lock.s_unlock(); + } + } + + if (level != height) { + + const rec_t* node_ptr; + ut_ad(height > 0); + + height--; + guess = NULL; - page_cur.block= block; - ut_ad(block == mtr->at_savepoint(block_savepoint)); - const page_t *page= buf_block_get_frame(block); -#ifdef UNIV_ZIP_DEBUG - if (rw_latch != RW_NO_LATCH) - { - const page_zip_des_t *page_zip= buf_block_get_page_zip(block); - ut_a(!page_zip || page_zip_validate(page_zip, page, index())); - } -#endif /* UNIV_ZIP_DEBUG */ - const uint32_t page_level= btr_page_get_level(page); + node_ptr = page_cur_get_rec(page_cursor); - if (height == ULINT_UNDEFINED) - { - /* We are in the B-tree index root page. */ -#ifdef BTR_CUR_ADAPT - info->root_guess= block; -#endif - height= page_level; - tree_height= height + 1; + offsets = rec_get_offsets(node_ptr, index, offsets, 0, + ULINT_UNDEFINED, &heap); - if (!height) - { - /* The root page is also a leaf page. - We may have to reacquire the page latch in a different mode. */ - switch (rw_latch) { - case RW_S_LATCH: - if ((latch_mode & ~12) != RW_S_LATCH) - { - rw_latch= rw_lock_type_t(latch_mode & ~12); - ut_ad(rw_latch == RW_X_LATCH || rw_latch == RW_SX_LATCH); - goto relatch; - } - if (latch_mode != BTR_MODIFY_PREV) - { - if (!latch_by_caller) - /* Release the tree s-latch */ - mtr->rollback_to_savepoint(savepoint, savepoint + 1); - goto reached_latched_leaf; - } - /* fall through */ - case RW_SX_LATCH: - ut_ad(rw_latch == RW_S_LATCH || - latch_mode == BTR_MODIFY_ROOT_AND_LEAF); - rw_latch= RW_X_LATCH; - relatch: - mtr->rollback_to_savepoint(block_savepoint); - height= ULINT_UNDEFINED; - goto search_loop; - case RW_X_LATCH: - if (latch_mode == BTR_MODIFY_TREE) - goto reached_index_root_and_leaf; - goto reached_root_and_leaf; - case RW_NO_LATCH: - ut_ad(mtr->memo_contains_flagged(&index()->lock, MTR_MEMO_X_LOCK)); - } - goto reached_leaf; - } - } - else if (UNIV_UNLIKELY(height != page_level)) - goto corrupted; - else - switch (latch_mode) { - case BTR_MODIFY_TREE: - break; - case BTR_MODIFY_ROOT_AND_LEAF: - ut_ad((mtr->at_savepoint(block_savepoint - 1)->page.id().page_no() == - index()->page) == (tree_height <= height + 2)); - if (tree_height <= height + 2) - /* Retain the root page latch. */ - break; - /* fall through */ - default: - /* Release the parent page latch. */ - ut_ad(block_savepoint > savepoint); - mtr->rollback_to_savepoint(block_savepoint - 1, block_savepoint); - block_savepoint--; - } + /* If the rec is the first or last in the page for + pessimistic delete intention, it might cause node_ptr insert + for the upper level. We should change the intention and retry. + */ + if (latch_mode == BTR_MODIFY_TREE + && btr_cur_need_opposite_intention( + page, lock_intention, node_ptr)) { - if (!height) - { - reached_leaf: - /* We reached the leaf level. */ - ut_ad(block == mtr->at_savepoint(block_savepoint)); +need_opposite_intention: + ut_ad(upper_rw_latch == RW_X_LATCH); - if (latch_mode == BTR_MODIFY_ROOT_AND_LEAF) - { - reached_root_and_leaf: - if (!latch_by_caller) - mtr->rollback_to_savepoint(savepoint, savepoint + 1); - reached_index_root_and_leaf: - ut_ad(rw_latch == RW_X_LATCH); -#ifdef BTR_CUR_HASH_ADAPT - btr_search_drop_page_hash_index(block, true); -#endif - if (page_cur_search_with_match(tuple, mode, &up_match, &low_match, - &page_cur, nullptr)) - goto corrupted; - ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_GE); - ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); - ut_ad(low_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); - goto func_exit; - } + if (n_releases > 0) { + /* release root block */ + mtr_release_block_at_savepoint( + mtr, tree_savepoints[0], + tree_blocks[0]); + } - if (rw_latch == RW_NO_LATCH) - btr_cur_latch_leaves(block_savepoint, latch_mode, this, mtr); + /* release all blocks */ + for (; n_releases <= n_blocks; n_releases++) { + mtr_release_block_at_savepoint( + mtr, tree_savepoints[n_releases], + tree_blocks[n_releases]); + } - if (latch_mode != BTR_MODIFY_TREE) - { - if (!latch_by_caller) - { - /* Release the tree s-latch */ - block_savepoint--; - mtr->rollback_to_savepoint(savepoint, savepoint + 1); - } - /* release upper blocks */ - if (savepoint < block_savepoint) - mtr->rollback_to_savepoint(savepoint, block_savepoint); - } - else - ut_ad(rw_latch == RW_NO_LATCH); + lock_intention = BTR_INTENTION_BOTH; - reached_latched_leaf: -#ifdef BTR_CUR_HASH_ADAPT - if (btr_search_enabled && !(tuple->info_bits & REC_INFO_MIN_REC_FLAG)) - { - if (page_cur_search_with_match_bytes(tuple, mode, - &up_match, &up_bytes, - &low_match, &low_bytes, &page_cur)) - goto corrupted; - } - else -#endif /* BTR_CUR_HASH_ADAPT */ - if (page_cur_search_with_match(tuple, mode, &up_match, &low_match, - &page_cur, nullptr)) - goto corrupted; + page_id.set_page_no(index->page); + up_match = 0; + low_match = 0; + height = ULINT_UNDEFINED; - ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_GE); - ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); - ut_ad(low_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); + n_blocks = 0; + n_releases = 0; -#ifdef BTR_CUR_HASH_ADAPT - /* We do a dirty read of btr_search_enabled here. We will - properly check btr_search_enabled again in - btr_search_build_page_hash_index() before building a page hash - index, while holding search latch. */ - if (!btr_search_enabled); - else if (tuple->info_bits & REC_INFO_MIN_REC_FLAG) - /* This may be a search tuple for btr_pcur_t::restore_position(). */ - ut_ad(tuple->is_metadata() || - (tuple->is_metadata(tuple->info_bits ^ REC_STATUS_INSTANT))); - else if (index()->table->is_temporary()); - else if (!rec_is_metadata(page_cur.rec, *index())) - btr_search_info_update(index(), this); -#endif /* BTR_CUR_HASH_ADAPT */ + goto search_loop; + } - goto func_exit; - } + if (dict_index_is_spatial(index)) { + if (page_rec_is_supremum(node_ptr)) { + cursor->low_match = 0; + cursor->up_match = 0; + goto func_exit; + } - guess= nullptr; - if (page_cur_search_with_match(tuple, page_mode, &up_match, &low_match, - &page_cur, nullptr)) - goto corrupted; - offsets= rec_get_offsets(page_cur.rec, index(), offsets, 0, ULINT_UNDEFINED, - &heap); + /* If we are doing insertion or record locating, + remember the tree nodes we visited */ + if (page_mode == PAGE_CUR_RTREE_INSERT + || (search_mode == PAGE_CUR_RTREE_LOCATE + && (latch_mode != BTR_MODIFY_LEAF))) { + bool add_latch = false; + + if (latch_mode == BTR_MODIFY_TREE + && rw_latch == RW_NO_LATCH) { + ut_ad(mtr->memo_contains_flagged( + &index->lock, MTR_MEMO_X_LOCK + | MTR_MEMO_SX_LOCK)); + block->page.lock.s_lock(); + add_latch = true; + } - ut_ad(block == mtr->at_savepoint(block_savepoint)); + /* Store the parent cursor location */ +#ifdef UNIV_DEBUG + ulint num_stored = rtr_store_parent_path( + block, cursor, latch_mode, + height + 1, mtr); +#else + rtr_store_parent_path( + block, cursor, latch_mode, + height + 1, mtr); +#endif - switch (latch_mode) { - default: - break; - case BTR_MODIFY_TREE: - if (btr_cur_need_opposite_intention(page, lock_intention, page_cur.rec)) - { - /* If the rec is the first or last in the page for pessimistic - delete intention, it might cause node_ptr insert for the upper - level. We should change the intention and retry. */ - need_opposite_intention: - return pessimistic_search_leaf(tuple, mode, mtr); - } + if (page_mode == PAGE_CUR_RTREE_INSERT) { + btr_pcur_t* r_cursor = + rtr_get_parent_cursor( + cursor, height + 1, + true); + /* If it is insertion, there should + be only one parent for each level + traverse */ +#ifdef UNIV_DEBUG + ut_ad(num_stored == 1); +#endif - if (detected_same_key_root || lock_intention != BTR_INTENTION_BOTH || - index()->is_unique() || - (up_match <= rec_offs_n_fields(offsets) && - low_match <= rec_offs_n_fields(offsets))) - break; + node_ptr = btr_pcur_get_rec(r_cursor); - /* If the first or the last record of the page or the same key - value to the first record or last record, then another page might - be chosen when BTR_CONT_MODIFY_TREE. So, the parent page should - not released to avoiding deadlock with blocking the another search - with the same key value. */ - const rec_t *first= page_rec_get_next_const(page_get_infimum_rec(page)); - ulint matched_fields; + } - if (UNIV_UNLIKELY(!first)) - goto corrupted; - if (page_cur.rec == first || page_rec_is_last(page_cur.rec, page)) - { - same_key_root: - detected_same_key_root= true; - break; - } + if (add_latch) { + block->page.lock.s_unlock(); + } - matched_fields= 0; - offsets2= rec_get_offsets(first, index(), offsets2, 0, ULINT_UNDEFINED, - &heap); - cmp_rec_rec(page_cur.rec, first, offsets, offsets2, index(), false, - &matched_fields); - if (matched_fields >= rec_offs_n_fields(offsets) - 1) - goto same_key_root; - if (const rec_t* last= - page_rec_get_prev_const(page_get_supremum_rec(page))) - { - matched_fields= 0; - offsets2= rec_get_offsets(last, index(), offsets2, 0, ULINT_UNDEFINED, - &heap); - cmp_rec_rec(page_cur.rec, last, offsets, offsets2, index(), false, - &matched_fields); - if (matched_fields >= rec_offs_n_fields(offsets) - 1) - goto same_key_root; - } - else - goto corrupted; + ut_ad(!page_rec_is_supremum(node_ptr)); + } - /* Release the non-root parent page unless it may need to be modified. */ - if (tree_height > height + 1 && - !btr_cur_will_modify_tree(index(), page, lock_intention, - page_cur.rec, node_ptr_max_size, - zip_size, mtr)) - { - mtr->rollback_to_savepoint(block_savepoint - 1, block_savepoint); - block_savepoint--; - } - } + ut_ad(page_mode == search_mode + || (page_mode == PAGE_CUR_WITHIN + && search_mode == PAGE_CUR_RTREE_LOCATE)); - /* Go to the child node */ - page_id.set_page_no(btr_node_ptr_get_child_page_no(page_cur.rec, offsets)); + page_mode = search_mode; + } - if (!--height) - { - /* We are about to access the leaf level. */ - rw_latch= RW_NO_LATCH; + /* If the first or the last record of the page + or the same key value to the first record or last record, + the another page might be chosen when BTR_CONT_MODIFY_TREE. + So, the parent page should not released to avoiding deadlock + with blocking the another search with the same key value. */ + if (!detected_same_key_root + && lock_intention == BTR_INTENTION_BOTH + && !dict_index_is_unique(index) + && latch_mode == BTR_MODIFY_TREE + && (up_match >= rec_offs_n_fields(offsets) - 1 + || low_match >= rec_offs_n_fields(offsets) - 1)) { + const rec_t* first_rec = page_rec_get_next_const( + page_get_infimum_rec(page)); + ulint matched_fields; - switch (latch_mode) { - case BTR_MODIFY_ROOT_AND_LEAF: - rw_latch= RW_X_LATCH; - break; - default: - break; - case BTR_MODIFY_PREV: - /* This is almost exclusively for ibuf_insert(), but also for - btr_pcur_move_to_prev(); the latter is not exercised by mtr */ - case BTR_SEARCH_PREV: - if (page_has_prev(page) && page_rec_is_first(page_cur.rec, page)) - { - ut_ad(block_savepoint + 1 == mtr->get_savepoint()); - /* Latch the previous page if the node pointer is the leftmost - of the current page. */ - buf_block_t *left= btr_block_get(*index(), btr_page_get_prev(page), - RW_NO_LATCH, false, mtr, &err); - if (!left) - goto func_exit; - static_assert(mtr_memo_type_t(BTR_MODIFY_PREV & ~4) == - MTR_MEMO_PAGE_X_FIX, ""); - static_assert(mtr_memo_type_t(BTR_SEARCH_PREV & ~4) == - MTR_MEMO_PAGE_S_FIX, ""); - mtr->lock_register(block_savepoint + 1, - mtr_memo_type_t(latch_mode & ~4)); - /* Because we are violating the latching order here, we will - have to temporarily release the right page latch if the left - page latch cannot be acquired without waiting. Concurrent page - splits or merges are impossible because we are holding a latch - on the parent of these sibling pages. */ - if (latch_mode == BTR_MODIFY_PREV) - { - if (!left->page.lock.x_lock_try()) - { - block->page.lock.x_unlock(); - left->page.lock.x_lock(); - } - } - else if (!left->page.lock.s_lock_try()) - { - block->page.lock.s_unlock(); - left->page.lock.s_lock(); - } -#ifdef BTR_CUR_HASH_ADAPT - btr_search_drop_page_hash_index(left, true); -#endif - } - break; - case BTR_MODIFY_LEAF: - case BTR_SEARCH_LEAF: - if (index()->is_ibuf()) - break; - rw_latch= rw_lock_type_t(latch_mode); - if (btr_op != BTR_NO_OP && - ibuf_should_try(index(), btr_op != BTR_INSERT_OP)) - /* Try to buffer the operation if the leaf page - is not in the buffer pool. */ - buf_mode= btr_op == BTR_DELETE_OP - ? BUF_GET_IF_IN_POOL_OR_WATCH - : BUF_GET_IF_IN_POOL; - break; - case BTR_MODIFY_TREE: - if (index()->is_ibuf()) - break; - if (lock_intention == BTR_INTENTION_INSERT && page_has_next(page) && - page_rec_is_last(page_cur.rec, page)) - { - /* btr_insert_into_right_sibling() might cause deleting node_ptr - at upper level */ - mtr->rollback_to_savepoint(block_savepoint); - goto need_opposite_intention; - } - } - } + ut_ad(upper_rw_latch == RW_X_LATCH); - goto search_loop; -} + if (UNIV_UNLIKELY(!first_rec)) { + corrupted: + err = DB_CORRUPTION; + goto func_exit; + } + if (node_ptr == first_rec + || page_rec_is_last(node_ptr, page)) { + detected_same_key_root = true; + } else { + matched_fields = 0; + + offsets2 = rec_get_offsets( + first_rec, index, offsets2, + 0, ULINT_UNDEFINED, &heap); + cmp_rec_rec(node_ptr, first_rec, + offsets, offsets2, index, false, + &matched_fields); + + if (matched_fields + >= rec_offs_n_fields(offsets) - 1) { + detected_same_key_root = true; + } else if (const rec_t* last_rec + = page_rec_get_prev_const( + page_get_supremum_rec( + page))) { + matched_fields = 0; + + offsets2 = rec_get_offsets( + last_rec, index, offsets2, + 0, ULINT_UNDEFINED, &heap); + cmp_rec_rec( + node_ptr, last_rec, + offsets, offsets2, index, + false, &matched_fields); + if (matched_fields + >= rec_offs_n_fields(offsets) - 1) { + detected_same_key_root = true; + } + } else { + goto corrupted; + } + } + } -ATTRIBUTE_COLD -dberr_t btr_cur_t::pessimistic_search_leaf(const dtuple_t *tuple, - page_cur_mode_t mode, mtr_t *mtr) -{ - ut_ad(index()->is_btree() || index()->is_ibuf()); - ut_ad(!index()->is_ibuf() || ibuf_inside(mtr)); + /* If the page might cause modify_tree, + we should not release the parent page's lock. */ + if (!detected_same_key_root + && latch_mode == BTR_MODIFY_TREE + && !btr_cur_will_modify_tree( + index, page, lock_intention, node_ptr, + node_ptr_max_size, zip_size, mtr) + && !rtree_parent_modified) { + ut_ad(upper_rw_latch == RW_X_LATCH); + ut_ad(n_releases <= n_blocks); + + /* we can release upper blocks */ + for (; n_releases < n_blocks; n_releases++) { + if (n_releases == 0) { + /* we should not release root page + to pin to same block. */ + continue; + } - rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; - rec_offs* offsets = offsets_; - rec_offs_init(offsets_); + /* release unused blocks to unpin */ + mtr_release_block_at_savepoint( + mtr, tree_savepoints[n_releases], + tree_blocks[n_releases]); + } + } - ut_ad(flag == BTR_CUR_BINARY); - ut_ad(dict_index_check_search_tuple(index(), tuple)); - ut_ad(dtuple_check_typed(tuple)); - buf_block_t *block= mtr->at_savepoint(1); - ut_ad(block->page.id().page_no() == index()->page); - block->page.fix(); - mtr->rollback_to_savepoint(1); - ut_ad(mtr->memo_contains_flagged(&index()->lock, - MTR_MEMO_SX_LOCK | MTR_MEMO_X_LOCK)); - - const page_cur_mode_t page_mode{btr_cur_nonleaf_mode(mode)}; - - mtr->page_lock(block, RW_X_LATCH); - - up_match= 0; - up_bytes= 0; - low_match= 0; - low_bytes= 0; - ulint height= btr_page_get_level(block->page.frame); - tree_height= height + 1; - mem_heap_t *heap= nullptr; + if (height == level + && latch_mode == BTR_MODIFY_TREE) { + ut_ad(upper_rw_latch == RW_X_LATCH); + /* we should sx-latch root page, if released already. + It contains seg_header. */ + if (n_releases > 0) { + mtr->sx_latch_at_savepoint( + tree_savepoints[0], + tree_blocks[0]); + } - search_loop: - dberr_t err; - page_cur.block= block; + /* x-latch the branch blocks not released yet. */ + for (ulint i = n_releases; i <= n_blocks; i++) { + mtr->x_latch_at_savepoint( + tree_savepoints[i], + tree_blocks[i]); + } + } - if (UNIV_UNLIKELY(!height)) - { - if (page_cur_search_with_match(tuple, mode, &up_match, &low_match, - &page_cur, nullptr)) - corrupted: - err= DB_CORRUPTION; - else - { - ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_GE); - ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); - ut_ad(low_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); + /* We should consider prev_page of parent page, if the node_ptr + is the leftmost of the page. because BTR_SEARCH_PREV and + BTR_MODIFY_PREV latches prev_page of the leaf page. */ + if ((latch_mode == BTR_SEARCH_PREV + || latch_mode == BTR_MODIFY_PREV) + && !prev_tree_blocks) { + /* block should be latched for consistent + btr_page_get_prev() */ + ut_ad(mtr->memo_contains_flagged( + block, MTR_MEMO_PAGE_S_FIX + | MTR_MEMO_PAGE_X_FIX)); -#ifdef BTR_CUR_HASH_ADAPT - /* We do a dirty read of btr_search_enabled here. We will - properly check btr_search_enabled again in - btr_search_build_page_hash_index() before building a page hash - index, while holding search latch. */ - if (!btr_search_enabled); - else if (tuple->info_bits & REC_INFO_MIN_REC_FLAG) - /* This may be a search tuple for btr_pcur_t::restore_position(). */ - ut_ad(tuple->is_metadata() || - (tuple->is_metadata(tuple->info_bits ^ REC_STATUS_INSTANT))); - else if (index()->table->is_temporary()); - else if (!rec_is_metadata(page_cur.rec, *index())) - btr_search_info_update(index(), this); -#endif /* BTR_CUR_HASH_ADAPT */ - err= DB_SUCCESS; - } + if (page_has_prev(page) + && page_rec_is_first(node_ptr, page)) { - func_exit: - if (UNIV_LIKELY_NULL(heap)) - mem_heap_free(heap); - return err; - } + if (leftmost_from_level == 0) { + leftmost_from_level = height + 1; + } + } else { + leftmost_from_level = 0; + } + + if (height == 0 && leftmost_from_level > 0) { + /* should retry to get also prev_page + from level==leftmost_from_level. */ + prev_tree_blocks = static_cast( + ut_malloc_nokey(sizeof(buf_block_t*) + * leftmost_from_level)); + + prev_tree_savepoints = static_cast( + ut_malloc_nokey(sizeof(ulint) + * leftmost_from_level)); + + /* back to the level (leftmost_from_level+1) */ + ulint idx = n_blocks + - (leftmost_from_level - 1); + + page_id.set_page_no( + tree_blocks[idx]->page.id().page_no()); + + for (ulint i = n_blocks + - (leftmost_from_level - 1); + i <= n_blocks; i++) { + mtr_release_block_at_savepoint( + mtr, tree_savepoints[i], + tree_blocks[i]); + } - if (page_cur_search_with_match(tuple, page_mode, &up_match, &low_match, - &page_cur, nullptr)) - goto corrupted; + n_blocks -= (leftmost_from_level - 1); + height = leftmost_from_level; + ut_ad(n_releases == 0); + + /* replay up_match, low_match */ + up_match = 0; + low_match = 0; + rtr_info_t* rtr_info = need_path + ? cursor->rtr_info : NULL; + + for (ulint i = 0; i < n_blocks; i++) { + page_cursor->block = tree_blocks[i]; + if (page_cur_search_with_match( + tuple, + page_mode, &up_match, + &low_match, page_cursor, + rtr_info)) { + err = DB_CORRUPTION; + goto func_exit; + } + } - page_id_t page_id{block->page.id()}; + goto search_loop; + } + } - offsets= rec_get_offsets(page_cur.rec, index(), offsets, 0, ULINT_UNDEFINED, - &heap); - /* Go to the child node */ - page_id.set_page_no(btr_node_ptr_get_child_page_no(page_cur.rec, offsets)); + /* Go to the child node */ + page_id.set_page_no( + btr_node_ptr_get_child_page_no(node_ptr, offsets)); - const auto block_savepoint= mtr->get_savepoint(); - block= - buf_page_get_gen(page_id, block->zip_size(), RW_NO_LATCH, nullptr, BUF_GET, - mtr, &err, !--height && !index()->is_clust()); + n_blocks++; - if (!block) - { - if (err == DB_DECRYPTION_FAILED) - btr_decryption_failed(*index()); - goto func_exit; - } + if (UNIV_UNLIKELY(height == 0 && dict_index_is_ibuf(index))) { + /* We're doing a search on an ibuf tree and we're one + level above the leaf page. */ - if (!!page_is_comp(block->page.frame) != index()->table->not_redundant() || - btr_page_get_index_id(block->page.frame) != index()->id || - fil_page_get_type(block->page.frame) == FIL_PAGE_RTREE || - !fil_page_index_page_check(block->page.frame)) - goto corrupted; + ut_ad(level == 0); - if (height != btr_page_get_level(block->page.frame)) - goto corrupted; + buf_mode = BUF_GET; + rw_latch = RW_NO_LATCH; + goto retry_page_get; + } - if (page_has_prev(block->page.frame) && - !btr_block_get(*index(), btr_page_get_prev(block->page.frame), - RW_X_LATCH, false, mtr, &err)) - goto func_exit; - mtr->x_latch_at_savepoint(block_savepoint, block); -#ifdef BTR_CUR_HASH_ADAPT - btr_search_drop_page_hash_index(block, true); + if (dict_index_is_spatial(index) + && page_mode >= PAGE_CUR_CONTAIN + && page_mode != PAGE_CUR_RTREE_INSERT) { + ut_ad(need_path); + rtr_node_path_t* path = + cursor->rtr_info->path; + + if (!path->empty() && found) { + ut_ad(path->back().page_no + == page_id.page_no()); + path->pop_back(); +#ifdef UNIV_DEBUG + if (page_mode == PAGE_CUR_RTREE_LOCATE + && (latch_mode != BTR_MODIFY_LEAF)) { + btr_pcur_t* cur + = cursor->rtr_info->parent_path->back( + ).cursor; + rec_t* my_node_ptr + = btr_pcur_get_rec(cur); + + offsets = rec_get_offsets( + my_node_ptr, index, offsets, + 0, ULINT_UNDEFINED, &heap); + + ulint my_page_no + = btr_node_ptr_get_child_page_no( + my_node_ptr, offsets); + + ut_ad(page_id.page_no() == my_page_no); + } #endif -#ifdef UNIV_ZIP_DEBUG - const page_zip_des_t *page_zip= buf_block_get_page_zip(block); - ut_a(!page_zip || page_zip_validate(page_zip, page, index())); -#endif /* UNIV_ZIP_DEBUG */ - if (page_has_next(block->page.frame) && - !btr_block_get(*index(), btr_page_get_next(block->page.frame), - RW_X_LATCH, false, mtr, &err)) - goto func_exit; - goto search_loop; -} + } + } -/********************************************************************//** -Searches an index tree and positions a tree cursor on a given non-leaf level. -NOTE: n_fields_cmp in tuple must be set so that it cannot be compared -to node pointer page number fields on the upper levels of the tree! -cursor->up_match and cursor->low_match both will have sensible values. -Cursor is left at the place where an insert of the -search tuple should be performed in the B-tree. InnoDB does an insert -immediately after the cursor. Thus, the cursor may end up on a user record, -or on a page infimum record. -@param level the tree level of search -@param tuple data tuple; NOTE: n_fields_cmp in tuple must be set so that - it cannot get compared to the node ptr page number field! -@param latch RW_S_LATCH or RW_X_LATCH -@param cursor tree cursor; the cursor page is s- or x-latched, but see also - above! -@param mtr mini-transaction -@return DB_SUCCESS on success or error code otherwise */ -TRANSACTIONAL_TARGET -dberr_t btr_cur_search_to_nth_level(ulint level, - const dtuple_t *tuple, - rw_lock_type_t rw_latch, - btr_cur_t *cursor, mtr_t *mtr) -{ - dict_index_t *const index= cursor->index(); + goto search_loop; + } else if (!dict_index_is_spatial(index) + && latch_mode == BTR_MODIFY_TREE + && lock_intention == BTR_INTENTION_INSERT + && page_has_next(page) + && page_rec_is_last(page_cur_get_rec(page_cursor), page)) { + + /* btr_insert_into_right_sibling() might cause + deleting node_ptr at upper level */ + + guess = NULL; + + if (height == 0) { + /* release the leaf pages if latched */ + for (uint i = 0; i < 3; i++) { + if (latch_leaves.blocks[i] != NULL) { + mtr_release_block_at_savepoint( + mtr, latch_leaves.savepoints[i], + latch_leaves.blocks[i]); + latch_leaves.blocks[i] = NULL; + } + } + } - ut_ad(index->is_btree() || index->is_ibuf()); - mem_heap_t *heap= nullptr; - rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; - rec_offs *offsets= offsets_; - rec_offs_init(offsets_); - ut_ad(level); - ut_ad(dict_index_check_search_tuple(index, tuple)); - ut_ad(index->is_ibuf() ? ibuf_inside(mtr) : index->is_btree()); - ut_ad(dtuple_check_typed(tuple)); - ut_ad(index->page != FIL_NULL); - - MEM_UNDEFINED(&cursor->up_bytes, sizeof cursor->up_bytes); - MEM_UNDEFINED(&cursor->low_bytes, sizeof cursor->low_bytes); - cursor->up_match= 0; - cursor->low_match= 0; - cursor->flag= BTR_CUR_BINARY; + goto need_opposite_intention; + } -#ifndef BTR_CUR_ADAPT - buf_block_t *block= nullptr; -#else - btr_search_t *info= btr_search_get_info(index); - buf_block_t *block= info->root_guess; -#endif /* BTR_CUR_ADAPT */ + if (level != 0) { + ut_ad(!autoinc); - ut_ad(mtr->memo_contains_flagged(&index->lock, - MTR_MEMO_X_LOCK | MTR_MEMO_SX_LOCK)); + if (upper_rw_latch == RW_NO_LATCH) { + ut_ad(latch_mode == BTR_CONT_MODIFY_TREE + || latch_mode == BTR_CONT_SEARCH_TREE); + btr_block_get( + *index, page_id.page_no(), + latch_mode == BTR_CONT_MODIFY_TREE + ? RW_X_LATCH : RW_SX_LATCH, false, mtr, &err); + } else { + ut_ad(mtr->memo_contains_flagged(block, + upper_rw_latch)); + + if (latch_by_caller) { + ut_ad(latch_mode == BTR_SEARCH_TREE); + /* to exclude modifying tree operations + should sx-latch the index. */ + ut_ad(mtr->memo_contains(index->lock, + MTR_MEMO_SX_LOCK)); + /* because has sx-latch of index, + can release upper blocks. */ + for (; n_releases < n_blocks; n_releases++) { + mtr_release_block_at_savepoint( + mtr, + tree_savepoints[n_releases], + tree_blocks[n_releases]); + } + } + } - const ulint zip_size= index->table->space->zip_size(); + if (page_mode <= PAGE_CUR_LE) { + cursor->low_match = low_match; + cursor->up_match = up_match; + } + } else { + cursor->low_match = low_match; + cursor->low_bytes = low_bytes; + cursor->up_match = up_match; + cursor->up_bytes = up_bytes; - /* Start with the root page. */ - page_id_t page_id(index->table->space_id, index->page); - ulint height= ULINT_UNDEFINED; + if (autoinc) { + page_set_autoinc(tree_blocks[0], autoinc, mtr, false); + } -search_loop: - dberr_t err= DB_SUCCESS; - if (buf_block_t *b= - mtr->get_already_latched(page_id, mtr_memo_type_t(rw_latch))) - block= b; - else if (!(block= buf_page_get_gen(page_id, zip_size, rw_latch, - block, BUF_GET, mtr, &err))) - { - if (err == DB_DECRYPTION_FAILED) - btr_decryption_failed(*index); - goto func_exit; - } +#ifdef BTR_CUR_HASH_ADAPT + /* We do a dirty read of btr_search_enabled here. We + will properly check btr_search_enabled again in + btr_search_build_page_hash_index() before building a + page hash index, while holding search latch. */ + if (!btr_search_enabled) { + } else if (tuple->info_bits & REC_INFO_MIN_REC_FLAG) { + /* This may be a search tuple for + btr_pcur_t::restore_position(). */ + ut_ad(tuple->is_metadata() + || (tuple->is_metadata(tuple->info_bits + ^ REC_STATUS_INSTANT))); + } else if (index->is_spatial()) { + } else if (index->table->is_temporary()) { + } else if (rec_is_metadata(btr_cur_get_rec(cursor), *index)) { + /* Only user records belong in the adaptive + hash index. */ + } else { + btr_search_info_update(index, cursor); + } +#endif /* BTR_CUR_HASH_ADAPT */ + ut_ad(cursor->up_match != ULINT_UNDEFINED + || mode != PAGE_CUR_GE); + ut_ad(cursor->up_match != ULINT_UNDEFINED + || mode != PAGE_CUR_LE); + ut_ad(cursor->low_match != ULINT_UNDEFINED + || mode != PAGE_CUR_LE); + } + + /* For spatial index, remember what blocks are still latched */ + if (dict_index_is_spatial(index) + && (latch_mode == BTR_MODIFY_TREE + || latch_mode == BTR_MODIFY_LEAF)) { + for (ulint i = 0; i < n_releases; i++) { + cursor->rtr_info->tree_blocks[i] = NULL; + cursor->rtr_info->tree_savepoints[i] = 0; + } -#ifdef UNIV_ZIP_DEBUG - if (const page_zip_des_t *page_zip= buf_block_get_page_zip(block)) - ut_a(page_zip_validate(page_zip, block->page.frame, index)); -#endif /* UNIV_ZIP_DEBUG */ + for (ulint i = n_releases; i <= n_blocks; i++) { + cursor->rtr_info->tree_blocks[i] = tree_blocks[i]; + cursor->rtr_info->tree_savepoints[i] = tree_savepoints[i]; + } + } - if (!!page_is_comp(block->page.frame) != index->table->not_redundant() || - btr_page_get_index_id(block->page.frame) != index->id || - fil_page_get_type(block->page.frame) == FIL_PAGE_RTREE || - !fil_page_index_page_check(block->page.frame)) - { - corrupted: - err= DB_CORRUPTION; - func_exit: - if (UNIV_LIKELY_NULL(heap)) - mem_heap_free(heap); - return err; - } +func_exit: + + if (UNIV_LIKELY_NULL(heap)) { + mem_heap_free(heap); + } - const uint32_t page_level= btr_page_get_level(block->page.frame); + ut_free(prev_tree_blocks); + ut_free(prev_tree_savepoints); - if (height == ULINT_UNDEFINED) - { - /* We are in the root node */ - height= page_level; - if (!height) - goto corrupted; - cursor->tree_height= height + 1; - } - else if (height != ulint{page_level}) - goto corrupted; - - cursor->page_cur.block= block; - - /* Search for complete index fields. */ - if (page_cur_search_with_match(tuple, PAGE_CUR_LE, &cursor->up_match, - &cursor->low_match, &cursor->page_cur, - nullptr)) - goto corrupted; - - /* If this is the desired level, leave the loop */ - if (level == height) - goto func_exit; - - ut_ad(height > level); - height--; - - offsets = rec_get_offsets(cursor->page_cur.rec, index, offsets, 0, - ULINT_UNDEFINED, &heap); - /* Go to the child node */ - page_id.set_page_no(btr_node_ptr_get_child_page_no(cursor->page_cur.rec, - offsets)); - block= nullptr; - goto search_loop; + if (mbr_adj) { + /* remember that we will need to adjust parent MBR */ + cursor->rtr_info->mbr_adj = true; + } + + DBUG_RETURN(err); } dberr_t btr_cur_t::open_leaf(bool first, dict_index_t *index, btr_latch_mode latch_mode, mtr_t *mtr) { + ulint node_ptr_max_size= srv_page_size / 2; btr_intention_t lock_intention; ulint n_blocks= 0; mem_heap_t *heap= nullptr; @@ -1860,21 +2424,29 @@ dberr_t btr_cur_t::open_leaf(bool first, dict_index_t *index, rec_offs_init(offsets_); const bool latch_by_caller= latch_mode & BTR_ALREADY_S_LATCHED; - latch_mode= btr_latch_mode(latch_mode & ~BTR_ALREADY_S_LATCHED); + latch_mode = btr_latch_mode(latch_mode & ~BTR_ALREADY_S_LATCHED); lock_intention= btr_cur_get_and_clear_intention(&latch_mode); + /* This function doesn't need to lock left page of the leaf page */ + if (latch_mode == BTR_SEARCH_PREV) + latch_mode= BTR_SEARCH_LEAF; + else if (latch_mode == BTR_MODIFY_PREV) + latch_mode= BTR_MODIFY_LEAF; + /* Store the position of the tree latch we push to mtr so that we know how to release it when we have latched the leaf node */ auto savepoint= mtr->get_savepoint(); rw_lock_type_t upper_rw_latch= RW_X_LATCH; - ulint node_ptr_max_size= 0; - if (latch_mode == BTR_MODIFY_TREE) - { - node_ptr_max_size= btr_node_ptr_max_size(index); + switch (latch_mode) { + case BTR_CONT_MODIFY_TREE: + case BTR_CONT_SEARCH_TREE: + abort(); + break; + case BTR_MODIFY_TREE: /* Most of delete-intended operations are purging. Free blocks and read IO bandwidth should be prioritized for them, when the history list is growing huge. */ @@ -1885,35 +2457,32 @@ dberr_t btr_cur_t::open_leaf(bool first, dict_index_t *index, mtr_x_lock_index(index, mtr); else mtr_sx_lock_index(index, mtr); - } - else - { - static_assert(int{BTR_CONT_MODIFY_TREE} == (12 | BTR_MODIFY_LEAF), ""); - ut_ad(!(latch_mode & 8)); - /* This function doesn't need to lock left page of the leaf page */ - static_assert(int{BTR_SEARCH_PREV} == (4 | BTR_SEARCH_LEAF), ""); - static_assert(int{BTR_MODIFY_PREV} == (4 | BTR_MODIFY_LEAF), ""); - latch_mode= btr_latch_mode(latch_mode & ~4); + break; + default: ut_ad(!latch_by_caller || mtr->memo_contains_flagged(&index->lock, MTR_MEMO_SX_LOCK | MTR_MEMO_S_LOCK)); upper_rw_latch= RW_S_LATCH; - if (!latch_by_caller) - { - savepoint++; - mtr_s_lock_index(index, mtr); - } + if (latch_by_caller) + break; + ut_ad(latch_mode != BTR_SEARCH_TREE); + savepoint++; + mtr_s_lock_index(index, mtr); } ut_ad(savepoint == mtr->get_savepoint()); - const rw_lock_type_t root_leaf_rw_latch= rw_lock_type_t(latch_mode & ~12); + const rw_lock_type_t root_leaf_rw_latch= + btr_cur_latch_for_root_leaf(latch_mode); page_cur.index = index; uint32_t page= index->page; const auto zip_size= index->table->space->zip_size(); + if (root_leaf_rw_latch == RW_X_LATCH) + node_ptr_max_size= btr_node_ptr_max_size(index); + for (ulint height= ULINT_UNDEFINED;;) { ut_ad(n_blocks < BTR_MAX_LEVELS); @@ -1962,15 +2531,20 @@ dberr_t btr_cur_t::open_leaf(bool first, dict_index_t *index, reached_leaf: const auto leaf_savepoint= mtr->get_savepoint(); ut_ad(leaf_savepoint); - ut_ad(block == mtr->at_savepoint(leaf_savepoint - 1)); if (rw_latch == RW_NO_LATCH) - btr_cur_latch_leaves(leaf_savepoint - 1, latch_mode, this, mtr); + btr_cur_latch_leaves(block, latch_mode, this, mtr); - if (latch_mode != BTR_MODIFY_TREE) + switch (latch_mode) { + case BTR_MODIFY_TREE: + case BTR_CONT_MODIFY_TREE: + case BTR_CONT_SEARCH_TREE: + break; + default: /* Release index->lock if needed, and the non-leaf pages. */ mtr->rollback_to_savepoint(savepoint - !latch_by_caller, leaf_savepoint - 1); + } break; } } @@ -4095,15 +4669,16 @@ btr_cur_pessimistic_update( } } -#if 0 // FIXME: this used to be a no-op, and will cause trouble if enabled - if (!big_rec_vec + if (!srv_read_only_mode + && !big_rec_vec && page_is_leaf(block->page.frame) && !dict_index_is_online_ddl(index)) { +#if 0 // FIXME: this used to be a no-op, and will cause trouble if enabled mtr->release(index->lock); +#endif /* NOTE: We cannot release root block latch here, because it has segment header and already modified in most of cases.*/ } -#endif err = DB_SUCCESS; goto return_after_reservations; @@ -4845,14 +5420,15 @@ return_after_reservations: err_exit: mem_heap_free(heap); -#if 0 // FIXME: this used to be a no-op, and will cause trouble if enabled - if (page_is_leaf(page) + if (!srv_read_only_mode + && page_is_leaf(page) && !dict_index_is_online_ddl(index)) { +#if 0 // FIXME: this used to be a no-op, and will cause trouble if enabled mtr->release(index->lock); +#endif /* NOTE: We cannot release root block latch here, because it has segment header and already modified in most of cases.*/ } -#endif index->table->space->release_free_extents(n_reserved); return(ret); @@ -4969,18 +5545,16 @@ public: buf_block_t *parent_block= m_block; ulint parent_savepoint= m_savepoint; + m_savepoint= mtr_set_savepoint(&mtr); m_block= btr_block_get(*index(), m_page_id.page_no(), RW_S_LATCH, !level, &mtr, nullptr); - if (!m_block) - return false; if (parent_block && parent_block != right_parent) - mtr.rollback_to_savepoint(parent_savepoint, parent_savepoint + 1); + mtr_release_block_at_savepoint(&mtr, parent_savepoint, parent_block); - m_savepoint= mtr.get_savepoint() - 1; - - return level == ULINT_UNDEFINED || - btr_page_get_level(m_block->page.frame) == level; + return m_block && + (level == ULINT_UNDEFINED || + btr_page_get_level(buf_block_get_frame(m_block)) == level); } /** Sets page mode for leaves */ @@ -5187,18 +5761,14 @@ static ha_rows btr_estimate_n_rows_in_range_on_level( buf_block_t *prev_block= block; ulint prev_savepoint= savepoint; - savepoint= mtr.get_savepoint(); + savepoint= mtr_set_savepoint(&mtr); /* Fetch the page. */ block= btr_block_get(*index, page_id.page_no(), RW_S_LATCH, !level, &mtr, nullptr); if (prev_block) - { - mtr.rollback_to_savepoint(prev_savepoint, prev_savepoint + 1); - if (block) - savepoint--; - } + mtr_release_block_at_savepoint(&mtr, prev_savepoint, prev_block); if (!block || btr_page_get_level(buf_block_get_frame(block)) != level) goto inexact; @@ -5227,20 +5797,14 @@ static ha_rows btr_estimate_n_rows_in_range_on_level( } while (page_id.page_no() != right_page_no); if (block) - { - ut_ad(block == mtr.at_savepoint(savepoint)); - mtr.rollback_to_savepoint(savepoint, savepoint + 1); - } + mtr_release_block_at_savepoint(&mtr, savepoint, block); return (n_rows); inexact: if (block) - { - ut_ad(block == mtr.at_savepoint(savepoint)); - mtr.rollback_to_savepoint(savepoint, savepoint + 1); - } + mtr_release_block_at_savepoint(&mtr, savepoint, block); is_n_rows_exact= false; @@ -5299,7 +5863,9 @@ ha_rows btr_estimate_n_rows_in_range(dict_index_t *index, mtr.start(); - ut_ad(mtr.get_savepoint() == 0); + /* Store the position of the tree latch we push to mtr so that we + know how to release it when we have latched leaf node(s) */ + ulint savepoint= mtr_set_savepoint(&mtr); mtr_s_lock_index(index, &mtr); ha_rows table_n_rows= dict_table_get_n_rows(index->table); @@ -5354,10 +5920,10 @@ search_loop: } if (height == 0) - /* There is no need to release non-leaf pages here as they must already be + /* There is no need to unlach non-leaf pages here as they must already be unlatched in btr_est_cur_t::fetch_child(). Try to search on pages after - releasing the index latch, to decrease contention. */ - mtr.rollback_to_savepoint(0, 1); + index->lock unlatching to decrease contention. */ + mtr_release_s_latch_at_savepoint(&mtr, savepoint, &index->lock); /* There is no need to search on left page if divergence_height != ULINT_UNDEFINED, as it was already searched before diff --git a/storage/innobase/btr/btr0defragment.cc b/storage/innobase/btr/btr0defragment.cc index 4e0a7d1f86a..76b173359da 100644 --- a/storage/innobase/btr/btr0defragment.cc +++ b/storage/innobase/btr/btr0defragment.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (C) 2012, 2014 Facebook, Inc. All Rights Reserved. -Copyright (C) 2014, 2023, MariaDB Corporation. +Copyright (C) 2014, 2022, 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 @@ -280,70 +280,6 @@ btr_defragment_calc_n_recs_for_size( return n_recs; } -MY_ATTRIBUTE((nonnull(2,3,4), warn_unused_result)) -/************************************************************//** -Returns the upper level node pointer to a page. It is assumed that mtr holds -an sx-latch on the tree. -@return rec_get_offsets() of the node pointer record */ -static -rec_offs* -btr_page_search_father_node_ptr( - rec_offs* offsets,/*!< in: work area for the return value */ - mem_heap_t* heap, /*!< in: memory heap to use */ - btr_cur_t* cursor, /*!< in: cursor pointing to user record, - out: cursor on node pointer record, - its page x-latched */ - mtr_t* mtr) /*!< in: mtr */ -{ - const uint32_t page_no = btr_cur_get_block(cursor)->page.id().page_no(); - dict_index_t* index = btr_cur_get_index(cursor); - ut_ad(!index->is_spatial()); - - ut_ad(mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK - | MTR_MEMO_SX_LOCK)); - ut_ad(dict_index_get_page(index) != page_no); - - const auto level = btr_page_get_level(btr_cur_get_page(cursor)); - - const rec_t* user_rec = btr_cur_get_rec(cursor); - ut_a(page_rec_is_user_rec(user_rec)); - - if (btr_cur_search_to_nth_level(level + 1, - dict_index_build_node_ptr(index, - user_rec, 0, - heap, level), - RW_X_LATCH, - cursor, mtr) != DB_SUCCESS) { - return nullptr; - } - - const rec_t* node_ptr = btr_cur_get_rec(cursor); - ut_ad(!btr_cur_get_block(cursor)->page.lock.not_recursive() - || mtr->memo_contains(index->lock, MTR_MEMO_X_LOCK)); - - offsets = rec_get_offsets(node_ptr, index, offsets, 0, - ULINT_UNDEFINED, &heap); - - if (btr_node_ptr_get_child_page_no(node_ptr, offsets) != page_no) { - offsets = nullptr; - } - - return(offsets); -} - -static bool btr_page_search_father(mtr_t *mtr, btr_cur_t *cursor) -{ - rec_t *rec= - page_rec_get_next(page_get_infimum_rec(cursor->block()->page.frame)); - if (UNIV_UNLIKELY(!rec)) - return false; - cursor->page_cur.rec= rec; - mem_heap_t *heap= mem_heap_create(100); - const bool got= btr_page_search_father_node_ptr(nullptr, heap, cursor, mtr); - mem_heap_free(heap); - return got; -} - /*************************************************************//** Merge as many records from the from_block to the to_block. Delete the from_block if all records are successfully merged to to_block. @@ -472,7 +408,7 @@ btr_defragment_merge_pages( parent.page_cur.index = index; parent.page_cur.block = from_block; - if (!btr_page_search_father(mtr, &parent)) { + if (!btr_page_get_father(mtr, &parent)) { to_block = nullptr; } else if (n_recs_to_move == n_recs) { /* The whole page is merged with the previous page, diff --git a/storage/innobase/btr/btr0pcur.cc b/storage/innobase/btr/btr0pcur.cc index 68699ede469..d731bcbb893 100644 --- a/storage/innobase/btr/btr0pcur.cc +++ b/storage/innobase/btr/btr0pcur.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2023, MariaDB Corporation. +Copyright (c) 2016, 2022, 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 @@ -212,100 +212,24 @@ btr_pcur_copy_stored_position( pcur_receive->old_n_fields = pcur_donate->old_n_fields; } -/** Optimistically latches the leaf page or pages requested. -@param[in] block guessed buffer block -@param[in,out] latch_mode BTR_SEARCH_LEAF, ... -@param[in,out] pcur cursor -@param[in,out] mtr mini-transaction -@return true if success */ -TRANSACTIONAL_TARGET -static bool btr_pcur_optimistic_latch_leaves(buf_block_t *block, - btr_pcur_t *pcur, - btr_latch_mode *latch_mode, - mtr_t *mtr) -{ - ut_ad(block->page.buf_fix_count()); - ut_ad(block->page.in_file()); - ut_ad(block->page.frame); - - static_assert(BTR_SEARCH_PREV & BTR_SEARCH_LEAF, ""); - static_assert(BTR_MODIFY_PREV & BTR_MODIFY_LEAF, ""); - static_assert((BTR_SEARCH_PREV ^ BTR_MODIFY_PREV) == - (RW_S_LATCH ^ RW_X_LATCH), ""); - - const rw_lock_type_t mode= - rw_lock_type_t(*latch_mode & (RW_X_LATCH | RW_S_LATCH)); - - switch (*latch_mode) { - default: - ut_ad(*latch_mode == BTR_SEARCH_LEAF || *latch_mode == BTR_MODIFY_LEAF); - return buf_page_optimistic_get(mode, block, pcur->modify_clock, mtr); - case BTR_SEARCH_PREV: /* btr_pcur_move_backward_from_page() */ - case BTR_MODIFY_PREV: /* Ditto, or ibuf_insert() */ - page_id_t id{0}; - uint32_t left_page_no; - ulint zip_size; - { - transactional_shared_lock_guard g{block->page.lock}; - if (block->modify_clock != pcur->modify_clock) - return false; - id= block->page.id(); - zip_size= block->zip_size(); - left_page_no= btr_page_get_prev(block->page.frame); - } - - if (left_page_no != FIL_NULL) - { - pcur->btr_cur.left_block= - buf_page_get_gen(page_id_t(id.space(), left_page_no), zip_size, - mode, nullptr, BUF_GET_POSSIBLY_FREED, mtr); - - if (pcur->btr_cur.left_block && - btr_page_get_next(pcur->btr_cur.left_block->page.frame) != - id.page_no()) - { -release_left_block: - mtr->release_last_page(); - return false; - } - } - else - pcur->btr_cur.left_block= nullptr; - - if (buf_page_optimistic_get(mode, block, pcur->modify_clock, mtr)) - { - if (btr_page_get_prev(block->page.frame) == left_page_no) - { - /* block was already buffer-fixed while entering the function and - buf_page_optimistic_get() buffer-fixes it again. */ - ut_ad(2 <= block->page.buf_fix_count()); - *latch_mode= btr_latch_mode(mode); - return true; - } - - mtr->release_last_page(); - } - - ut_ad(block->page.buf_fix_count()); - if (pcur->btr_cur.left_block) - goto release_left_block; - return false; - } -} - /** Structure acts as functor to do the latching of leaf pages. It returns true if latching of leaf pages succeeded and false otherwise. */ struct optimistic_latch_leaves { btr_pcur_t *const cursor; - btr_latch_mode *const latch_mode; + btr_latch_mode *latch_mode; mtr_t *const mtr; + optimistic_latch_leaves(btr_pcur_t *cursor, btr_latch_mode *latch_mode, + mtr_t *mtr) + : cursor(cursor), latch_mode(latch_mode), mtr(mtr) {} + bool operator() (buf_block_t *hint) const { - return hint && - btr_pcur_optimistic_latch_leaves(hint, cursor, latch_mode, mtr); + return hint && btr_cur_optimistic_latch_leaves( + hint, cursor->modify_clock, latch_mode, + btr_pcur_get_btr_cur(cursor), mtr); } }; @@ -379,8 +303,8 @@ btr_pcur_t::restore_position(btr_latch_mode restore_latch_mode, mtr_t *mtr) /* Try optimistic restoration. */ if (block_when_stored.run_with_hint( - optimistic_latch_leaves{this, &restore_latch_mode, - mtr})) { + optimistic_latch_leaves(this, &restore_latch_mode, + mtr))) { pos_state = BTR_PCUR_IS_POSITIONED; latch_mode = restore_latch_mode; @@ -541,9 +465,18 @@ btr_pcur_move_to_next_page( return DB_CORRUPTION; } + ulint mode = cursor->latch_mode; + switch (mode) { + case BTR_SEARCH_TREE: + mode = BTR_SEARCH_LEAF; + break; + case BTR_MODIFY_TREE: + mode = BTR_MODIFY_LEAF; + } + dberr_t err; buf_block_t* next_block = btr_block_get( - *cursor->index(), next_page_no, cursor->latch_mode & ~12, + *cursor->index(), next_page_no, mode, page_is_leaf(page), mtr, &err); if (UNIV_UNLIKELY(!next_block)) { diff --git a/storage/innobase/btr/btr0sea.cc b/storage/innobase/btr/btr0sea.cc index a1609248512..fc890f9233b 100644 --- a/storage/innobase/btr/btr0sea.cc +++ b/storage/innobase/btr/btr0sea.cc @@ -1055,24 +1055,26 @@ btr_search_guess_on_hash( index_id_t index_id; ut_ad(mtr->is_active()); - ut_ad(index->is_btree() || index->is_ibuf()); - /* Note that, for efficiency, the struct info may not be protected by - any latch here! */ - - if (latch_mode > BTR_MODIFY_LEAF - || !info->last_hash_succ || !info->n_hash_potential - || (tuple->info_bits & REC_INFO_MIN_REC_FLAG)) { + if (!btr_search_enabled) { return false; } - ut_ad(index->is_btree()); - ut_ad(!index->table->is_temporary()); - + ut_ad(!index->is_ibuf()); ut_ad(latch_mode == BTR_SEARCH_LEAF || latch_mode == BTR_MODIFY_LEAF); compile_time_assert(ulint{BTR_SEARCH_LEAF} == ulint{RW_S_LATCH}); compile_time_assert(ulint{BTR_MODIFY_LEAF} == ulint{RW_X_LATCH}); + /* Not supported for spatial index */ + ut_ad(!dict_index_is_spatial(index)); + + /* Note that, for efficiency, the struct info may not be protected by + any latch here! */ + + if (info->n_hash_potential == 0) { + return false; + } + cursor->n_fields = info->n_fields; cursor->n_bytes = info->n_bytes; diff --git a/storage/innobase/dict/dict0crea.cc b/storage/innobase/dict/dict0crea.cc index 614048b7ba0..daf3bc9a664 100644 --- a/storage/innobase/dict/dict0crea.cc +++ b/storage/innobase/dict/dict0crea.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2023, MariaDB Corporation. +Copyright (c) 2017, 2022, 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 @@ -722,7 +722,7 @@ dict_build_field_def_step( } /***************************************************************//** -Creates an index tree for the index. +Creates an index tree for the index if it is not a member of a cluster. @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ static MY_ATTRIBUTE((nonnull, warn_unused_result)) dberr_t @@ -755,8 +755,9 @@ dict_create_index_tree_step( pcur.btr_cur.page_cur.index = UT_LIST_GET_FIRST(dict_sys.sys_indexes->indexes); - dberr_t err = btr_pcur_open(search_tuple, PAGE_CUR_L, BTR_MODIFY_LEAF, - &pcur, &mtr); + dberr_t err = + btr_pcur_open(search_tuple, PAGE_CUR_L, BTR_MODIFY_LEAF, + &pcur, 0, &mtr); if (err != DB_SUCCESS) { func_exit: @@ -767,25 +768,10 @@ func_exit: btr_pcur_move_to_next_user_rec(&pcur, &mtr); if (UNIV_UNLIKELY(btr_pcur_is_after_last_on_page(&pcur))) { -corrupted: err = DB_CORRUPTION; goto func_exit; } - ulint len; - byte* data = rec_get_nth_field_old(btr_pcur_get_rec(&pcur), - DICT_FLD__SYS_INDEXES__ID, - &len); - if (UNIV_UNLIKELY(len != 8 || mach_read_from_8(data) != index->id)) { - goto corrupted; - } - - data = rec_get_nth_field_old(btr_pcur_get_rec(&pcur), - DICT_FLD__SYS_INDEXES__PAGE_NO, &len); - if (len != 4) { - goto corrupted; - } - if (index->is_readable()) { index->set_modified(mtr); @@ -798,6 +784,11 @@ corrupted: err = DB_OUT_OF_FILE_SPACE; ); } + ulint len; + byte* data = rec_get_nth_field_old(btr_pcur_get_rec(&pcur), + DICT_FLD__SYS_INDEXES__PAGE_NO, + &len); + ut_ad(len == 4); mtr.write<4,mtr_t::MAYBE_NOP>(*btr_pcur_get_block(&pcur), data, node->page_no); goto func_exit; diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index 53d1031d270..21efb525fa8 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -2,7 +2,7 @@ Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2013, 2023, MariaDB Corporation. +Copyright (c) 2013, 2022, 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 @@ -4143,7 +4143,8 @@ void dict_set_corrupted(dict_index_t *index, const char *ctx) dict_index_copy_types(tuple, sys_index, 2); cursor.page_cur.index = sys_index; - if (cursor.search_leaf(tuple, PAGE_CUR_LE, BTR_MODIFY_LEAF, &mtr) + if (btr_cur_search_to_nth_level(0, tuple, PAGE_CUR_LE, + BTR_MODIFY_LEAF, &cursor, &mtr) != DB_SUCCESS) { goto fail; } @@ -4218,7 +4219,8 @@ dict_index_set_merge_threshold( dict_index_copy_types(tuple, sys_index, 2); cursor.page_cur.index = sys_index; - if (cursor.search_leaf(tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF, &mtr) + if (btr_cur_search_to_nth_level(0, tuple, PAGE_CUR_GE, + BTR_MODIFY_LEAF, &cursor, &mtr) != DB_SUCCESS) { goto func_exit; } diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc index 9910a000b5b..004b00615e8 100644 --- a/storage/innobase/dict/dict0load.cc +++ b/storage/innobase/dict/dict0load.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2023, MariaDB Corporation. +Copyright (c) 2016, 2022, 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 @@ -1321,7 +1321,7 @@ static dberr_t dict_load_columns(dict_table_t *table, unsigned use_uncommitted, dict_index_copy_types(&tuple, sys_index, 1); pcur.btr_cur.page_cur.index = sys_index; - dberr_t err = btr_pcur_open_on_user_rec(&tuple, + dberr_t err = btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); if (err != DB_SUCCESS) { goto func_exit; @@ -1452,7 +1452,7 @@ dict_load_virtual_col(dict_table_t *table, bool uncommitted, ulint nth_v_col) dict_index_copy_types(&tuple, sys_virtual_index, 2); pcur.btr_cur.page_cur.index = sys_virtual_index; - dberr_t err = btr_pcur_open_on_user_rec(&tuple, + dberr_t err = btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); if (err != DB_SUCCESS) { goto func_exit; @@ -1688,7 +1688,8 @@ static dberr_t dict_load_fields(dict_index_t *index, bool uncommitted, dict_index_copy_types(&tuple, sys_index, 1); pcur.btr_cur.page_cur.index = sys_index; - dberr_t error = btr_pcur_open_on_user_rec(&tuple, BTR_SEARCH_LEAF, + dberr_t error = btr_pcur_open_on_user_rec(&tuple, + PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); if (error != DB_SUCCESS) { goto func_exit; @@ -1946,7 +1947,8 @@ dberr_t dict_load_indexes(dict_table_t *table, bool uncommitted, dict_index_copy_types(&tuple, sys_index, 1); pcur.btr_cur.page_cur.index = sys_index; - dberr_t error = btr_pcur_open_on_user_rec(&tuple, BTR_SEARCH_LEAF, + dberr_t error = btr_pcur_open_on_user_rec(&tuple, + PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); if (error != DB_SUCCESS) { goto func_exit; @@ -2347,7 +2349,7 @@ static dict_table_t *dict_load_table_one(const span &name, bool uncommitted = false; reload: mtr.start(); - dberr_t err = btr_pcur_open_on_user_rec(&tuple, + dberr_t err = btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); if (err != DB_SUCCESS || !btr_pcur_is_on_user_rec(&pcur)) { @@ -2605,7 +2607,8 @@ dict_load_table_on_id( dict_table_t* table = nullptr; - if (btr_pcur_open_on_user_rec(&tuple, BTR_SEARCH_LEAF, &pcur, &mtr) + if (btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, + BTR_SEARCH_LEAF, &pcur, &mtr) == DB_SUCCESS && btr_pcur_is_on_user_rec(&pcur)) { /*---------------------------------------------------*/ @@ -2711,7 +2714,7 @@ static dberr_t dict_load_foreign_cols(dict_foreign_t *foreign, trx_id_t trx_id) pcur.btr_cur.page_cur.index = sys_index; mem_heap_t* heap = nullptr; - dberr_t err = btr_pcur_open_on_user_rec(&tuple, + dberr_t err = btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); if (err != DB_SUCCESS) { goto func_exit; @@ -2888,7 +2891,7 @@ dict_load_foreign( mtr.start(); mem_heap_t* heap = nullptr; - dberr_t err = btr_pcur_open_on_user_rec(&tuple, + dberr_t err = btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); if (err != DB_SUCCESS) { goto err_exit; @@ -3099,7 +3102,7 @@ start_load: dict_index_copy_types(&tuple, sec_index, 1); pcur.btr_cur.page_cur.index = sec_index; - dberr_t err = btr_pcur_open_on_user_rec(&tuple, + dberr_t err = btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); if (err != DB_SUCCESS) { DBUG_RETURN(err); diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index 845f133f1a6..44fcf9f2c18 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2009, 2019, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2015, 2023, MariaDB Corporation. +Copyright (c) 2015, 2022, 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 @@ -1697,7 +1697,7 @@ static dberr_t page_cur_open_level(page_cur_t *page_cur, ulint level, static dberr_t btr_pcur_open_level(btr_pcur_t *pcur, ulint level, mtr_t *mtr, dict_index_t *index) { - pcur->latch_mode= BTR_SEARCH_LEAF; + pcur->latch_mode= BTR_SEARCH_TREE; pcur->search_mode= PAGE_CUR_G; pcur->pos_state= BTR_PCUR_IS_POSITIONED; pcur->btr_cur.page_cur.index= index; diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index e9f3106feb0..12e9a6913ba 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2023, MariaDB Corporation. +Copyright (c) 2017, 2022, 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 @@ -122,22 +122,15 @@ MY_ATTRIBUTE((nonnull, warn_unused_result)) static buf_block_t *fsp_get_header(const fil_space_t *space, mtr_t *mtr, dberr_t *err) { - const page_id_t id{space->id, 0}; - buf_block_t *block= mtr->get_already_latched(id, MTR_MEMO_PAGE_SX_FIX); - if (block) - *err= DB_SUCCESS; - else + buf_block_t *block= buf_page_get_gen(page_id_t(space->id, 0), + space->zip_size(), RW_SX_LATCH, + nullptr, BUF_GET_POSSIBLY_FREED, + mtr, err); + if (block && space->id != mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_ID + + block->page.frame)) { - block= buf_page_get_gen(id, space->zip_size(), RW_SX_LATCH, - nullptr, BUF_GET_POSSIBLY_FREED, - mtr, err); - if (block && - space->id != mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_ID + - block->page.frame)) - { - *err= DB_CORRUPTION; - block= nullptr; - } + *err= DB_CORRUPTION; + block= nullptr; } return block; } diff --git a/storage/innobase/gis/gis0sea.cc b/storage/innobase/gis/gis0sea.cc index 9eeeb9aeed3..207d49abeba 100644 --- a/storage/innobase/gis/gis0sea.cc +++ b/storage/innobase/gis/gis0sea.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2016, 2018, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2023, MariaDB Corporation. +Copyright (c) 2017, 2022, 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 @@ -44,6 +44,7 @@ Created 2014/01/16 Jimmy Yang static bool rtr_cur_restore_position( + ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ btr_cur_t* cursor, /*!< in: detached persistent cursor */ ulint level, /*!< in: index level */ mtr_t* mtr); /*!< in: mtr */ @@ -134,7 +135,6 @@ rtr_pcur_getnext_from_path( && (my_latch_mode | 4) == BTR_CONT_MODIFY_TREE; if (!index_locked) { - ut_ad(mtr->is_empty()); mtr_s_lock_index(index, mtr); } else { ut_ad(mtr->memo_contains_flagged(&index->lock, @@ -154,12 +154,14 @@ rtr_pcur_getnext_from_path( node_seq_t path_ssn; const page_t* page; rw_lock_type_t rw_latch; + ulint tree_idx; mysql_mutex_lock(&rtr_info->rtr_path_mutex); next_rec = rtr_info->path->back(); rtr_info->path->pop_back(); level = next_rec.level; path_ssn = next_rec.seq_no; + tree_idx = btr_cur->tree_height - level - 1; /* Maintain the parent path info as well, if needed */ if (need_parent && !skip_parent && !new_split) { @@ -221,15 +223,37 @@ rtr_pcur_getnext_from_path( rw_latch = RW_X_LATCH; } - if (my_latch_mode == BTR_MODIFY_LEAF) { - mtr->rollback_to_savepoint(1); + /* Release previous locked blocks */ + if (my_latch_mode != BTR_SEARCH_LEAF) { + for (ulint idx = 0; idx < btr_cur->tree_height; + idx++) { + if (rtr_info->tree_blocks[idx]) { + mtr_release_block_at_savepoint( + mtr, + rtr_info->tree_savepoints[idx], + rtr_info->tree_blocks[idx]); + rtr_info->tree_blocks[idx] = NULL; + } + } + for (ulint idx = RTR_MAX_LEVELS; idx < RTR_MAX_LEVELS + 3; + idx++) { + if (rtr_info->tree_blocks[idx]) { + mtr_release_block_at_savepoint( + mtr, + rtr_info->tree_savepoints[idx], + rtr_info->tree_blocks[idx]); + rtr_info->tree_blocks[idx] = NULL; + } + } } + /* set up savepoint to record any locks to be taken */ + rtr_info->tree_savepoints[tree_idx] = mtr_set_savepoint(mtr); + ut_ad((my_latch_mode | 4) == BTR_CONT_MODIFY_TREE || !page_is_leaf(btr_cur_get_page(btr_cur)) || !btr_cur->page_cur.block->page.lock.have_any()); - const auto block_savepoint = mtr->get_savepoint(); block = buf_page_get_gen( page_id_t(index->table->space_id, next_rec.page_no), zip_size, @@ -240,6 +264,8 @@ rtr_pcur_getnext_from_path( break; } + rtr_info->tree_blocks[tree_idx] = block; + page = buf_block_get_frame(block); page_ssn = page_get_ssn_id(page); @@ -370,23 +396,24 @@ rtr_pcur_getnext_from_path( if (found) { if (level == target_level) { - ut_ad(block - == mtr->at_savepoint(block_savepoint)); + page_cur_t* r_cur;; if (my_latch_mode == BTR_MODIFY_TREE && level == 0) { ut_ad(rw_latch == RW_NO_LATCH); btr_cur_latch_leaves( - block_savepoint, + block, BTR_MODIFY_TREE, btr_cur, mtr); } + r_cur = btr_cur_get_page_cur(btr_cur); + page_cur_position( page_cur_get_rec(page_cursor), page_cur_get_block(page_cursor), - btr_cur_get_page_cur(btr_cur)); + r_cur); btr_cur->low_match = level != 0 ? DICT_INDEX_SPATIAL_NODEPTR_SIZE + 1 @@ -398,7 +425,13 @@ rtr_pcur_getnext_from_path( last node just located */ skip_parent = true; } else { - mtr->release_last_page(); + /* Release latch on the current page */ + ut_ad(rtr_info->tree_blocks[tree_idx]); + + mtr_release_block_at_savepoint( + mtr, rtr_info->tree_savepoints[tree_idx], + rtr_info->tree_blocks[tree_idx]); + rtr_info->tree_blocks[tree_idx] = NULL; } } while (!rtr_info->path->empty()); @@ -476,524 +509,50 @@ static void rtr_compare_cursor_rec(const rec_t *rec, dict_index_t *index, } #endif -TRANSACTIONAL_TARGET -dberr_t rtr_search_to_nth_level(ulint level, const dtuple_t *tuple, - page_cur_mode_t mode, - btr_latch_mode latch_mode, - btr_cur_t *cur, mtr_t *mtr) -{ - page_cur_mode_t page_mode; - page_cur_mode_t search_mode= PAGE_CUR_UNSUPP; - - bool mbr_adj= false; - bool found= false; - dict_index_t *const index= cur->index(); - - mem_heap_t *heap= nullptr; - rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; - rec_offs *offsets= offsets_; - rec_offs_init(offsets_); - ut_ad(level == 0 || mode == PAGE_CUR_LE || RTREE_SEARCH_MODE(mode)); - ut_ad(dict_index_check_search_tuple(index, tuple)); - ut_ad(dtuple_check_typed(tuple)); - ut_ad(index->is_spatial()); - ut_ad(index->page != FIL_NULL); - - MEM_UNDEFINED(&cur->up_match, sizeof cur->up_match); - MEM_UNDEFINED(&cur->up_bytes, sizeof cur->up_bytes); - MEM_UNDEFINED(&cur->low_match, sizeof cur->low_match); - MEM_UNDEFINED(&cur->low_bytes, sizeof cur->low_bytes); - ut_d(cur->up_match= ULINT_UNDEFINED); - ut_d(cur->low_match= ULINT_UNDEFINED); - - const bool latch_by_caller= latch_mode & BTR_ALREADY_S_LATCHED; - - ut_ad(!latch_by_caller - || mtr->memo_contains_flagged(&index->lock, MTR_MEMO_S_LOCK - | MTR_MEMO_SX_LOCK)); - latch_mode= BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); - - ut_ad(!latch_by_caller || latch_mode == BTR_SEARCH_LEAF || - latch_mode == BTR_MODIFY_LEAF); - - cur->flag= BTR_CUR_BINARY; - -#ifndef BTR_CUR_ADAPT - buf_block_t *guess= nullptr; -#else - btr_search_t *const info= btr_search_get_info(index); - buf_block_t *guess= info->root_guess; -#endif - - /* Store the position of the tree latch we push to mtr so that we - know how to release it when we have latched leaf node(s) */ - - const ulint savepoint= mtr->get_savepoint(); - - rw_lock_type_t upper_rw_latch, root_leaf_rw_latch= RW_NO_LATCH; - - switch (latch_mode) { - case BTR_MODIFY_TREE: - mtr_x_lock_index(index, mtr); - upper_rw_latch= root_leaf_rw_latch= RW_X_LATCH; - break; - case BTR_CONT_MODIFY_TREE: - ut_ad(mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK | - MTR_MEMO_SX_LOCK)); - upper_rw_latch= RW_X_LATCH; - break; - default: - ut_ad(latch_mode != BTR_MODIFY_PREV); - ut_ad(latch_mode != BTR_SEARCH_PREV); - if (!latch_by_caller) - mtr_s_lock_index(index, mtr); - upper_rw_latch= root_leaf_rw_latch= RW_S_LATCH; - if (latch_mode == BTR_MODIFY_LEAF) - root_leaf_rw_latch= RW_X_LATCH; - } - - auto root_savepoint= mtr->get_savepoint(); - const ulint zip_size= index->table->space->zip_size(); - - /* Start with the root page. */ - page_id_t page_id(index->table->space_id, index->page); - - ulint up_match= 0, up_bytes= 0, low_match= 0, low_bytes= 0; - ulint height= ULINT_UNDEFINED; - - /* We use these modified search modes on non-leaf levels of the - B-tree. These let us end up in the right B-tree leaf. In that leaf - we use the original search mode. */ - - switch (mode) { - case PAGE_CUR_GE: - page_mode= PAGE_CUR_L; - break; - case PAGE_CUR_G: - page_mode= PAGE_CUR_LE; - break; - default: -#ifdef PAGE_CUR_LE_OR_EXTENDS - ut_ad(mode == PAGE_CUR_L || mode == PAGE_CUR_LE - || RTREE_SEARCH_MODE(mode) - || mode == PAGE_CUR_LE_OR_EXTENDS); -#else /* PAGE_CUR_LE_OR_EXTENDS */ - ut_ad(mode == PAGE_CUR_L || mode == PAGE_CUR_LE - || RTREE_SEARCH_MODE(mode)); -#endif /* PAGE_CUR_LE_OR_EXTENDS */ - page_mode= mode; - break; - } - - search_loop: - auto buf_mode= BUF_GET; - ulint rw_latch= RW_NO_LATCH; - - if (height) - { - /* We are about to fetch the root or a non-leaf page. */ - if (latch_mode != BTR_MODIFY_TREE || height == level) - /* If doesn't have SX or X latch of index, - each page should be latched before reading. */ - rw_latch= upper_rw_latch; - } - else if (latch_mode <= BTR_MODIFY_LEAF) - rw_latch= latch_mode; - - dberr_t err; - auto block_savepoint= mtr->get_savepoint(); - buf_block_t *block= buf_page_get_gen(page_id, zip_size, rw_latch, guess, - buf_mode, mtr, &err, false); - if (!block) - { - if (err == DB_DECRYPTION_FAILED) - btr_decryption_failed(*index); - func_exit: - if (UNIV_LIKELY_NULL(heap)) - mem_heap_free(heap); - - if (mbr_adj) - /* remember that we will need to adjust parent MBR */ - cur->rtr_info->mbr_adj= true; - - return err; - } - - const page_t *page= buf_block_get_frame(block); -#ifdef UNIV_ZIP_DEBUG - if (rw_latch != RW_NO_LATCH) { - const page_zip_des_t *page_zip= buf_block_get_page_zip(block); - ut_a(!page_zip || page_zip_validate(page_zip, page, index)); - } -#endif /* UNIV_ZIP_DEBUG */ - - ut_ad(fil_page_index_page_check(page)); - ut_ad(index->id == btr_page_get_index_id(page)); - - if (height != ULINT_UNDEFINED); - else if (page_is_leaf(page) && - rw_latch != RW_NO_LATCH && rw_latch != root_leaf_rw_latch) - { - /* The root page is also a leaf page (root_leaf). - We should reacquire the page, because the root page - is latched differently from leaf pages. */ - ut_ad(root_leaf_rw_latch != RW_NO_LATCH); - ut_ad(rw_latch == RW_S_LATCH || rw_latch == RW_SX_LATCH); - - ut_ad(block == mtr->at_savepoint(block_savepoint)); - mtr->rollback_to_savepoint(block_savepoint); - - upper_rw_latch= root_leaf_rw_latch; - goto search_loop; - } - else - { - /* We are in the root node */ - - height= btr_page_get_level(page); - cur->tree_height= height + 1; - - ut_ad(cur->rtr_info); - - /* If SSN in memory is not initialized, fetch it from root page */ - if (!rtr_get_current_ssn_id(index)) - /* FIXME: do this in dict_load_table_one() */ - index->set_ssn(page_get_ssn_id(page) + 1); - - /* Save the MBR */ - cur->rtr_info->thr= cur->thr; - rtr_get_mbr_from_tuple(tuple, &cur->rtr_info->mbr); - -#ifdef BTR_CUR_ADAPT - info->root_guess= block; -#endif - } - - if (height == 0) { - if (rw_latch == RW_NO_LATCH) - { - ut_ad(block == mtr->at_savepoint(block_savepoint)); - btr_cur_latch_leaves(block_savepoint, latch_mode, cur, mtr); - } - - switch (latch_mode) { - case BTR_MODIFY_TREE: - case BTR_CONT_MODIFY_TREE: - break; - default: - if (!latch_by_caller) - { - /* Release the tree s-latch */ - mtr->rollback_to_savepoint(savepoint, - savepoint + 1); - block_savepoint--; - root_savepoint--; - } - /* release upper blocks */ - if (savepoint < block_savepoint) - mtr->rollback_to_savepoint(savepoint, block_savepoint); - } - - page_mode= mode; - } - - /* Remember the page search mode */ - search_mode= page_mode; - - /* Some adjustment on search mode, when the page search mode is - PAGE_CUR_RTREE_LOCATE or PAGE_CUR_RTREE_INSERT, as we are searching - with MBRs. When it is not the target level, we should search all - sub-trees that "CONTAIN" the search range/MBR. When it is at the - target level, the search becomes PAGE_CUR_LE */ - - if (page_mode == PAGE_CUR_RTREE_INSERT) - { - page_mode= (level == height) - ? PAGE_CUR_LE - : PAGE_CUR_RTREE_INSERT; - - ut_ad(!page_is_leaf(page) || page_mode == PAGE_CUR_LE); - } - else if (page_mode == PAGE_CUR_RTREE_LOCATE && level == height) - page_mode= level == 0 ? PAGE_CUR_LE : PAGE_CUR_RTREE_GET_FATHER; - - up_match= 0; - low_match= 0; - - if (latch_mode == BTR_MODIFY_TREE || latch_mode == BTR_CONT_MODIFY_TREE) - /* Tree are locked, no need for Page Lock to protect the "path" */ - cur->rtr_info->need_page_lock= false; - - cur->page_cur.block= block; - - if (page_mode >= PAGE_CUR_CONTAIN) - { - found= rtr_cur_search_with_match(block, index, tuple, page_mode, - &cur->page_cur, cur->rtr_info); - - /* Need to use BTR_MODIFY_TREE to do the MBR adjustment */ - if (search_mode == PAGE_CUR_RTREE_INSERT && cur->rtr_info->mbr_adj) { - static_assert(BTR_MODIFY_TREE == (8 | BTR_MODIFY_LEAF), ""); - - if (!(latch_mode & 8)) - /* Parent MBR needs updated, should retry with BTR_MODIFY_TREE */ - goto func_exit; - - cur->rtr_info->mbr_adj= false; - mbr_adj= true; - } - - if (found && page_mode == PAGE_CUR_RTREE_GET_FATHER) - cur->low_match= DICT_INDEX_SPATIAL_NODEPTR_SIZE + 1; - } - else - { - /* Search for complete index fields. */ - up_bytes= low_bytes= 0; - if (page_cur_search_with_match(tuple, page_mode, &up_match, - &low_match, &cur->page_cur, nullptr)) { - err= DB_CORRUPTION; - goto func_exit; - } - } - - /* If this is the desired level, leave the loop */ - - ut_ad(height == btr_page_get_level(btr_cur_get_page(cur))); - - /* Add Predicate lock if it is serializable isolation - and only if it is in the search case */ - if (mode >= PAGE_CUR_CONTAIN && mode != PAGE_CUR_RTREE_INSERT && - mode != PAGE_CUR_RTREE_LOCATE && cur->rtr_info->need_prdt_lock) - { - lock_prdt_t prdt; - - { - trx_t* trx= thr_get_trx(cur->thr); - TMLockTrxGuard g{TMLockTrxArgs(*trx)}; - lock_init_prdt_from_mbr(&prdt, &cur->rtr_info->mbr, mode, - trx->lock.lock_heap); - } - - if (rw_latch == RW_NO_LATCH && height != 0) - block->page.lock.s_lock(); - - lock_prdt_lock(block, &prdt, index, LOCK_S, LOCK_PREDICATE, cur->thr); - - if (rw_latch == RW_NO_LATCH && height != 0) - block->page.lock.s_unlock(); - } - - if (level != height) - { - ut_ad(height > 0); - - height--; - guess= nullptr; - - const rec_t *node_ptr= btr_cur_get_rec(cur); - - offsets= rec_get_offsets(node_ptr, index, offsets, 0, - ULINT_UNDEFINED, &heap); - - if (page_rec_is_supremum(node_ptr)) - { - cur->low_match= 0; - cur->up_match= 0; - goto func_exit; - } - - /* If we are doing insertion or record locating, - remember the tree nodes we visited */ - if (page_mode == PAGE_CUR_RTREE_INSERT || - (search_mode == PAGE_CUR_RTREE_LOCATE && - latch_mode != BTR_MODIFY_LEAF)) - { - const bool add_latch= latch_mode == BTR_MODIFY_TREE && - rw_latch == RW_NO_LATCH; - - if (add_latch) - { - ut_ad(mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK | - MTR_MEMO_SX_LOCK)); - block->page.lock.s_lock(); - } - - /* Store the parent cursor location */ - ut_d(auto num_stored=) - rtr_store_parent_path(block, cur, latch_mode, height + 1, mtr); - - if (page_mode == PAGE_CUR_RTREE_INSERT) - { - btr_pcur_t *r_cursor= rtr_get_parent_cursor(cur, height + 1, true); - /* If it is insertion, there should be only one parent for - each level traverse */ - ut_ad(num_stored == 1); - node_ptr= btr_pcur_get_rec(r_cursor); - } - - if (add_latch) - block->page.lock.s_unlock(); - - ut_ad(!page_rec_is_supremum(node_ptr)); - } - - ut_ad(page_mode == search_mode || - (page_mode == PAGE_CUR_WITHIN && - search_mode == PAGE_CUR_RTREE_LOCATE)); - page_mode= search_mode; - - if (height == level && latch_mode == BTR_MODIFY_TREE) - { - ut_ad(upper_rw_latch == RW_X_LATCH); - for (auto i= root_savepoint, n= mtr->get_savepoint(); i < n; i++) - mtr->x_latch_at_savepoint(i, mtr->at_savepoint(i)); - } - - /* Go to the child node */ - page_id.set_page_no(btr_node_ptr_get_child_page_no(node_ptr, offsets)); - - if (page_mode >= PAGE_CUR_CONTAIN && page_mode != PAGE_CUR_RTREE_INSERT) - { - rtr_node_path_t *path= cur->rtr_info->path; - - if (found && !path->empty()) - { - ut_ad(path->back().page_no == page_id.page_no()); - path->pop_back(); -#ifdef UNIV_DEBUG - if (page_mode == PAGE_CUR_RTREE_LOCATE && - latch_mode != BTR_MODIFY_LEAF) - { - btr_pcur_t* pcur= cur->rtr_info->parent_path->back().cursor; - rec_t *my_node_ptr= btr_pcur_get_rec(pcur); - - offsets= rec_get_offsets(my_node_ptr, index, offsets, - 0, ULINT_UNDEFINED, &heap); - - ut_ad(page_id.page_no() == - btr_node_ptr_get_child_page_no(my_node_ptr, offsets)); - } -#endif - } - } - - goto search_loop; - } - - if (level) - { - if (upper_rw_latch == RW_NO_LATCH) - { - ut_ad(latch_mode == BTR_CONT_MODIFY_TREE); - btr_block_get(*index, page_id.page_no(), RW_X_LATCH, false, mtr, &err); - } - else - { - ut_ad(mtr->memo_contains_flagged(block, upper_rw_latch)); - ut_ad(!latch_by_caller); - } - - if (page_mode <= PAGE_CUR_LE) - { - cur->low_match= low_match; - cur->up_match= up_match; - } - } - else - { - cur->low_match= low_match; - cur->low_bytes= low_bytes; - cur->up_match= up_match; - cur->up_bytes= up_bytes; - - ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_GE); - ut_ad(up_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); - ut_ad(low_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); - } - - goto func_exit; -} - -dberr_t rtr_search_leaf(btr_cur_t *cur, const dtuple_t *tuple, - btr_latch_mode latch_mode, - mtr_t *mtr, page_cur_mode_t mode) -{ - return rtr_search_to_nth_level(0, tuple, mode, latch_mode, cur, mtr); -} - -/** Search for a spatial index leaf page record. -@param pcur cursor -@param tuple search tuple -@param mode search mode -@param mtr mini-transaction */ -dberr_t rtr_search_leaf(btr_pcur_t *pcur, const dtuple_t *tuple, - page_cur_mode_t mode, mtr_t *mtr) -{ -#ifdef UNIV_DEBUG - switch (mode) { - case PAGE_CUR_CONTAIN: - case PAGE_CUR_INTERSECT: - case PAGE_CUR_WITHIN: - case PAGE_CUR_DISJOINT: - case PAGE_CUR_MBR_EQUAL: - break; - default: - ut_ad("invalid mode" == 0); - } -#endif - pcur->latch_mode= BTR_SEARCH_LEAF; - pcur->search_mode= mode; - pcur->pos_state= BTR_PCUR_IS_POSITIONED; - pcur->trx_if_known= nullptr; - return rtr_search_leaf(&pcur->btr_cur, tuple, BTR_SEARCH_LEAF, mtr, mode); -} - /**************************************************************//** Initializes and opens a persistent cursor to an index tree. It should be -closed with btr_pcur_close. */ -bool rtr_search( +closed with btr_pcur_close. Mainly called by row_search_index_entry() */ +bool +rtr_pcur_open( + dict_index_t* index, /*!< in: index */ const dtuple_t* tuple, /*!< in: tuple on which search done */ - btr_latch_mode latch_mode,/*!< in: BTR_MODIFY_LEAF, ... */ + btr_latch_mode latch_mode,/*!< in: BTR_SEARCH_LEAF, ... */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ mtr_t* mtr) /*!< in: mtr */ { static_assert(BTR_MODIFY_TREE == (8 | BTR_MODIFY_LEAF), ""); ut_ad(latch_mode & BTR_MODIFY_LEAF); - ut_ad(!(latch_mode & BTR_ALREADY_S_LATCHED)); - ut_ad(mtr->is_empty()); /* Initialize the cursor */ btr_pcur_init(cursor); cursor->latch_mode = BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); - cursor->search_mode = PAGE_CUR_RTREE_LOCATE; - cursor->trx_if_known = nullptr; - - if (latch_mode & 8) { - mtr_x_lock_index(cursor->index(), mtr); - } else { - latch_mode - = btr_latch_mode(latch_mode | BTR_ALREADY_S_LATCHED); - mtr_sx_lock_index(cursor->index(), mtr); - } + cursor->search_mode = PAGE_CUR_RTREE_LOCATE; + cursor->trx_if_known = NULL; /* Search with the tree cursor */ btr_cur_t* btr_cursor = btr_pcur_get_btr_cur(cursor); + btr_cursor->page_cur.index = index; - btr_cursor->rtr_info - = rtr_create_rtr_info(false, false, - btr_cursor, cursor->index()); + btr_cursor->rtr_info = rtr_create_rtr_info(false, false, + btr_cursor, index); + /* Purge will SX lock the tree instead of take Page Locks */ if (btr_cursor->thr) { btr_cursor->rtr_info->need_page_lock = true; btr_cursor->rtr_info->thr = btr_cursor->thr; } - if (rtr_search_leaf(btr_cursor, tuple, latch_mode, mtr) - != DB_SUCCESS) { + if ((latch_mode & 8) && index->lock.have_u_not_x()) { + index->lock.u_x_upgrade(SRW_LOCK_CALL); + mtr->lock_upgrade(index->lock); + } + + if (btr_cur_search_to_nth_level(0, tuple, PAGE_CUR_RTREE_LOCATE, + latch_mode, + btr_cursor, mtr) != DB_SUCCESS) { return true; } @@ -1001,8 +560,7 @@ bool rtr_search( const rec_t* rec = btr_pcur_get_rec(cursor); - const bool d= rec_get_deleted_flag( - rec, cursor->index()->table->not_redundant()); + const bool d= rec_get_deleted_flag(rec, index->table->not_redundant()); if (page_rec_is_infimum(rec) || btr_pcur_get_low_match(cursor) != dtuple_get_n_fields(tuple) @@ -1013,12 +571,26 @@ bool rtr_search( btr_cursor->rtr_info->fd_del = true; btr_cursor->low_match = 0; } - - mtr->rollback_to_savepoint(1); + /* Did not find matched row in first dive. Release + latched block if any before search more pages */ + if (!(latch_mode & 8)) { + ulint tree_idx = btr_cursor->tree_height - 1; + rtr_info_t* rtr_info = btr_cursor->rtr_info; + + if (rtr_info->tree_blocks[tree_idx]) { + mtr_release_block_at_savepoint( + mtr, + rtr_info->tree_savepoints[tree_idx], + rtr_info->tree_blocks[tree_idx]); + rtr_info->tree_blocks[tree_idx] = NULL; + } + } if (!rtr_pcur_getnext_from_path(tuple, PAGE_CUR_RTREE_LOCATE, btr_cursor, 0, latch_mode, - true, mtr)) { + latch_mode + & (8 | BTR_ALREADY_S_LATCHED), + mtr)) { return true; } @@ -1026,10 +598,6 @@ bool rtr_search( == dtuple_get_n_fields(tuple)); } - if (!(latch_mode & 8)) { - mtr->rollback_to_savepoint(0, 1); - } - return false; } @@ -1073,7 +641,8 @@ static const rec_t* rtr_get_father_node( if (sea_cur && sea_cur->tree_height > level) { ut_ad(mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK | MTR_MEMO_SX_LOCK)); - if (rtr_cur_restore_position(sea_cur, level, mtr)) { + if (rtr_cur_restore_position(BTR_CONT_MODIFY_TREE, sea_cur, + level, mtr)) { btr_pcur_t* r_cursor = rtr_get_parent_cursor( sea_cur, level, false); @@ -1099,8 +668,9 @@ static const rec_t* rtr_get_father_node( btr_cur->rtr_info = rtr_create_rtr_info(false, false, btr_cur, index); - if (rtr_search_to_nth_level(level, tuple, PAGE_CUR_RTREE_LOCATE, - BTR_CONT_MODIFY_TREE, btr_cur, mtr) + if (btr_cur_search_to_nth_level(level, tuple, + PAGE_CUR_RTREE_LOCATE, + BTR_CONT_MODIFY_TREE, btr_cur, mtr) != DB_SUCCESS) { } else if (sea_cur && sea_cur->tree_height == level) { rec = btr_cur_get_rec(btr_cur); @@ -1159,8 +729,9 @@ rtr_page_get_father_node_ptr( page_no = btr_cur_get_block(cursor)->page.id().page_no(); index = btr_cur_get_index(cursor); - ut_ad(mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK - | MTR_MEMO_SX_LOCK)); + ut_ad(srv_read_only_mode + || mtr->memo_contains_flagged(&index->lock, MTR_MEMO_X_LOCK + | MTR_MEMO_SX_LOCK)); ut_ad(dict_index_get_page(index) != page_no); @@ -1308,10 +879,32 @@ rtr_init_rtr_info( if (!reinit) { /* Reset all members. */ - memset(rtr_info, 0, sizeof *rtr_info); - static_assert(PAGE_CUR_UNSUPP == 0, "compatibility"); + rtr_info->path = NULL; + rtr_info->parent_path = NULL; + rtr_info->matches = NULL; + mysql_mutex_init(rtr_path_mutex_key, &rtr_info->rtr_path_mutex, nullptr); + + memset(rtr_info->tree_blocks, 0x0, + sizeof(rtr_info->tree_blocks)); + memset(rtr_info->tree_savepoints, 0x0, + sizeof(rtr_info->tree_savepoints)); + rtr_info->mbr.xmin = 0.0; + rtr_info->mbr.xmax = 0.0; + rtr_info->mbr.ymin = 0.0; + rtr_info->mbr.ymax = 0.0; + rtr_info->thr = NULL; + rtr_info->heap = NULL; + rtr_info->cursor = NULL; + rtr_info->index = NULL; + rtr_info->need_prdt_lock = false; + rtr_info->need_page_lock = false; + rtr_info->allocated = false; + rtr_info->mbr_adj = false; + rtr_info->fd_del = false; + rtr_info->search_tuple = NULL; + rtr_info->search_mode = PAGE_CUR_UNSUPP; } ut_ad(!rtr_info->matches || rtr_info->matches->matched_recs->empty()); @@ -1537,6 +1130,7 @@ struct optimistic_get static bool rtr_cur_restore_position( + ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ btr_cur_t* btr_cur, /*!< in: detached persistent cursor */ ulint level, /*!< in: index level */ mtr_t* mtr) /*!< in: mtr */ @@ -1564,6 +1158,8 @@ rtr_cur_restore_position( r_cursor->modify_clock = 100; ); + ut_ad(latch_mode == BTR_CONT_MODIFY_TREE); + if (r_cursor->block_when_stored.run_with_hint( optimistic_get(r_cursor, mtr))) { ut_ad(r_cursor->pos_state == BTR_PCUR_IS_POSITIONED); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 244d535f020..949a9eae0d7 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1549,7 +1549,8 @@ static void innodb_drop_database(handlerton*, char *path) mtr_t mtr; mtr.start(); pcur.btr_cur.page_cur.index = sys_index; - err= btr_pcur_open_on_user_rec(&tuple, BTR_SEARCH_LEAF, &pcur, &mtr); + err= btr_pcur_open_on_user_rec(&tuple, PAGE_CUR_GE, + BTR_SEARCH_LEAF, &pcur, &mtr); if (err != DB_SUCCESS) goto err_exit; diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 8ff36bc4bdd..7c162b9af6a 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -6081,8 +6081,7 @@ func_exit: que_thr_t* thr = pars_complete_graph_for_exec( NULL, trx, ctx->heap, NULL); - page_id_t id{block->page.id()}; - const bool is_root = id.page_no() == index->page; + const bool is_root = block->page.id().page_no() == index->page; if (rec_is_metadata(rec, *index)) { ut_ad(page_rec_is_user_rec(rec)); @@ -6099,10 +6098,8 @@ func_exit: } /* Ensure that the root page is in the correct format. */ - id.set_page_no(index->page); - buf_block_t* root = mtr.get_already_latched( - id, MTR_MEMO_PAGE_SX_FIX); - + buf_block_t* root = btr_root_block_get(index, RW_X_LATCH, + &mtr, &err); if (UNIV_UNLIKELY(!root)) { goto func_exit; } diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc index c3dddf8251d..5a97ea4ebe0 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.cc +++ b/storage/innobase/ibuf/ibuf0ibuf.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2023, MariaDB Corporation. +Copyright (c) 2016, 2022, 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 @@ -2297,7 +2297,7 @@ loop: btr_pcur_t pcur; pcur.btr_cur.page_cur.index= ibuf.index; ibuf_mtr_start(&mtr); - if (btr_pcur_open(&tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF, &pcur, &mtr)) + if (btr_pcur_open(&tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF, &pcur, 0, &mtr)) goto func_exit; if (!btr_pcur_is_on_user_rec(&pcur)) { @@ -2493,8 +2493,8 @@ ibuf_merge_space( /* Position the cursor on the first matching record. */ pcur.btr_cur.page_cur.index = ibuf.index; - dberr_t err = btr_pcur_open(&tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, - &pcur, &mtr); + dberr_t err = btr_pcur_open(&tuple, PAGE_CUR_GE, + BTR_SEARCH_LEAF, &pcur, 0, &mtr); ut_ad(err != DB_SUCCESS || page_validate(btr_pcur_get_page(&pcur), ibuf.index)); @@ -3238,7 +3238,7 @@ ibuf_insert_low( ibuf_mtr_start(&mtr); pcur.btr_cur.page_cur.index = ibuf.index; - err = btr_pcur_open(ibuf_entry, PAGE_CUR_LE, mode, &pcur, &mtr); + err = btr_pcur_open(ibuf_entry, PAGE_CUR_LE, mode, &pcur, 0, &mtr); if (err != DB_SUCCESS) { func_exit: ibuf_mtr_commit(&mtr); @@ -4037,12 +4037,12 @@ bool ibuf_delete_rec(const page_id_t page_id, btr_pcur_t* pcur, ibuf_mtr_start(mtr); mysql_mutex_lock(&ibuf_mutex); - ibuf.index->lock.u_lock(SRW_LOCK_ARGS(__FILE__, __LINE__)); if (!ibuf_restore_pos(page_id, search_tuple, BTR_PURGE_TREE, pcur, mtr)) { + mysql_mutex_unlock(&ibuf_mutex); - ibuf.index->lock.u_unlock(); + ut_ad(mtr->has_committed()); goto func_exit; } @@ -4053,11 +4053,13 @@ bool ibuf_delete_rec(const page_id_t page_id, btr_pcur_t* pcur, ut_a(err == DB_SUCCESS); ibuf_size_update(ibuf_root->page.frame); + mysql_mutex_unlock(&ibuf_mutex); + ibuf.empty = page_is_empty(ibuf_root->page.frame); + } else { + mysql_mutex_unlock(&ibuf_mutex); } - mysql_mutex_unlock(&ibuf_mutex); - ibuf.index->lock.u_unlock(); ibuf_btr_pcur_commit_specify_mtr(pcur, mtr); func_exit: @@ -4235,7 +4237,7 @@ loop: /* Position pcur in the insert buffer at the first entry for this index page */ - if (btr_pcur_open_on_user_rec(search_tuple, + if (btr_pcur_open_on_user_rec(search_tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF, &pcur, &mtr) != DB_SUCCESS) { err = DB_CORRUPTION; @@ -4452,7 +4454,7 @@ loop: /* Position pcur in the insert buffer at the first entry for the space */ - if (btr_pcur_open_on_user_rec(&search_tuple, + if (btr_pcur_open_on_user_rec(&search_tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF, &pcur, &mtr) != DB_SUCCESS) { goto leave_loop; diff --git a/storage/innobase/include/btr0btr.h b/storage/innobase/include/btr0btr.h index a1cc10b05db..a2aa46b62da 100644 --- a/storage/innobase/include/btr0btr.h +++ b/storage/innobase/include/btr0btr.h @@ -2,7 +2,7 @@ Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2014, 2023, MariaDB Corporation. +Copyright (c) 2014, 2022, 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 @@ -211,12 +211,13 @@ btr_write_autoinc(dict_index_t* index, ib_uint64_t autoinc, bool reset = false) @param[in,out] mtr mini-transaction */ void btr_set_instant(buf_block_t* root, const dict_index_t& index, mtr_t* mtr); -ATTRIBUTE_COLD __attribute__((nonnull)) +ATTRIBUTE_COLD __attribute__((nonnull, warn_unused_result)) /** Reset the table to the canonical format on ROLLBACK of instant ALTER TABLE. @param[in] index clustered index with instant ALTER TABLE @param[in] all whether to reset FIL_PAGE_TYPE as well -@param[in,out] mtr mini-transaction */ -void btr_reset_instant(const dict_index_t &index, bool all, mtr_t *mtr); +@param[in,out] mtr mini-transaction +@return error code */ +dberr_t btr_reset_instant(const dict_index_t &index, bool all, mtr_t *mtr); /*************************************************************//** Makes tree one level higher by splitting the root, and inserts @@ -240,7 +241,7 @@ btr_root_raise_and_insert( ulint n_ext, /*!< in: number of externally stored columns */ mtr_t* mtr, /*!< in: mtr */ dberr_t* err) /*!< out: error code */ - MY_ATTRIBUTE((nonnull, warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /*************************************************************//** Reorganizes an index page. diff --git a/storage/innobase/include/btr0cur.h b/storage/innobase/include/btr0cur.h index e9c44d6d9bf..49bc8a4ff1b 100644 --- a/storage/innobase/include/btr0cur.h +++ b/storage/innobase/include/btr0cur.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2023, MariaDB Corporation. +Copyright (c) 2017, 2022, 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 @@ -63,6 +63,12 @@ enum { BTR_KEEP_IBUF_BITMAP = 32 }; +/* btr_cur_latch_leaves() returns latched blocks and savepoints. */ +struct btr_latch_leaves_t { + buf_block_t* blocks[3]; + ulint savepoints[3]; +}; + #include "que0types.h" #include "row0types.h" @@ -120,28 +126,51 @@ bool btr_cur_instant_root_init(dict_index_t* index, const page_t* page) ATTRIBUTE_COLD __attribute__((nonnull, warn_unused_result)); +/** Optimistically latches the leaf page or pages requested. +@param[in] block guessed buffer block +@param[in] modify_clock modify clock value +@param[in,out] latch_mode BTR_SEARCH_LEAF, ... +@param[in,out] cursor cursor +@param[in] mtr mini-transaction +@return true if success */ +bool +btr_cur_optimistic_latch_leaves( + buf_block_t* block, + ib_uint64_t modify_clock, + btr_latch_mode* latch_mode, + btr_cur_t* cursor, + mtr_t* mtr); + MY_ATTRIBUTE((warn_unused_result)) -/********************************************************************//** -Searches an index tree and positions a tree cursor on a given non-leaf level. +/** Searches an index tree and positions a tree cursor on a given level. NOTE: n_fields_cmp in tuple must be set so that it cannot be compared to node pointer page number fields on the upper levels of the tree! +Note that if mode is PAGE_CUR_LE, which is used in inserts, then cursor->up_match and cursor->low_match both will have sensible values. -Cursor is left at the place where an insert of the -search tuple should be performed in the B-tree. InnoDB does an insert -immediately after the cursor. Thus, the cursor may end up on a user record, -or on a page infimum record. +If mode is PAGE_CUR_GE, then up_match will a have a sensible value. @param level the tree level of search @param tuple data tuple; NOTE: n_fields_cmp in tuple must be set so that it cannot get compared to the node ptr page number field! -@param latch RW_S_LATCH or RW_X_LATCH +@param mode PAGE_CUR_L, ...; NOTE that if the search is made using a + unique prefix of a record, mode should be PAGE_CUR_LE, not + PAGE_CUR_GE, as the latter may end up on the previous page of + the record! Inserts should always be made using PAGE_CUR_LE + to search the position! +@param latch_mode BTR_SEARCH_LEAF, ..., ORed with at most one of BTR_INSERT, + BTR_DELETE_MARK, or BTR_DELETE; + cursor->left_block is used to store a pointer to the left + neighbor page @param cursor tree cursor; the cursor page is s- or x-latched, but see also above! @param mtr mini-transaction +@param autoinc PAGE_ROOT_AUTO_INC to be written (0 if none) @return DB_SUCCESS on success or error code otherwise */ dberr_t btr_cur_search_to_nth_level(ulint level, const dtuple_t *tuple, - rw_lock_type_t rw_latch, - btr_cur_t *cursor, mtr_t *mtr); + page_cur_mode_t mode, + btr_latch_mode latch_mode, + btr_cur_t *cursor, mtr_t *mtr, + ib_uint64_t autoinc= 0); /*************************************************************//** Tries to perform an insert to a page in an index tree, next to cursor. @@ -628,13 +657,15 @@ btr_rec_copy_externally_stored_field( @param[in] block leaf page where the search converged @param[in] latch_mode BTR_SEARCH_LEAF, ... @param[in] cursor cursor -@param[in,out] mtr mini-transaction */ +@param[in,out] mtr mini-transaction +@param[out] latch_leaves latched blocks and savepoints */ void btr_cur_latch_leaves( - ulint block_savepoint, + buf_block_t* block, btr_latch_mode latch_mode, btr_cur_t* cursor, - mtr_t* mtr); + mtr_t* mtr, + btr_latch_leaves_t* latch_leaves = nullptr); /*######################################################################*/ @@ -703,14 +734,14 @@ struct btr_cur_t { BTR_MODIFY_PREV */ /*------------------------------*/ que_thr_t* thr; /*!< this field is only used - when search_leaf() + when btr_cur_search_to_nth_level is called for an index entry insertion: the calling query thread is passed here to be used in the insert buffer */ /*------------------------------*/ /** The following fields are used in - search_leaf() to pass information: */ + btr_cur_search_to_nth_level to pass information: */ /* @{ */ enum btr_cur_method flag; /*!< Search method used */ ulint tree_height; /*!< Tree height if the search is done @@ -719,7 +750,8 @@ struct btr_cur_t { ulint up_match; /*!< If the search mode was PAGE_CUR_LE, the number of matched fields to the the first user record to the right of - the cursor record after search_leaf(); + the cursor record after + btr_cur_search_to_nth_level; for the mode PAGE_CUR_GE, the matched fields to the first user record AT THE CURSOR or to the right of it; @@ -736,7 +768,8 @@ struct btr_cur_t { ulint low_match; /*!< if search mode was PAGE_CUR_LE, the number of matched fields to the first user record AT THE CURSOR or - to the left of it after search_leaf(); + to the left of it after + btr_cur_search_to_nth_level; NOT defined for PAGE_CUR_GE or any other search modes; see also the NOTE in up_match! */ @@ -770,24 +803,6 @@ struct btr_cur_t { dberr_t open_leaf(bool first, dict_index_t *index, btr_latch_mode latch_mode, mtr_t *mtr); - /** Search the leaf page record corresponding to a key. - @param tuple key to search for, with correct n_fields_cmp - @param mode search mode; PAGE_CUR_LE for unique prefix or for inserting - @param latch_mode latch mode - @param mtr mini-transaction - @return error code */ - dberr_t search_leaf(const dtuple_t *tuple, page_cur_mode_t mode, - btr_latch_mode latch_mode, mtr_t *mtr); - - /** Search the leaf page record corresponding to a key, exclusively latching - all sibling pages on the way. - @param tuple key to search for, with correct n_fields_cmp - @param mode search mode; PAGE_CUR_LE for unique prefix or for inserting - @param mtr mini-transaction - @return error code */ - dberr_t pessimistic_search_leaf(const dtuple_t *tuple, page_cur_mode_t mode, - mtr_t *mtr); - /** Open the cursor at a random leaf page record. @param offsets temporary memory for rec_get_offsets() @param heap memory heap for rec_get_offsets() @@ -847,14 +862,14 @@ inherited external field. */ #define BTR_EXTERN_INHERITED_FLAG 64U #ifdef BTR_CUR_HASH_ADAPT -/** Number of searches down the B-tree in btr_cur_t::search_leaf(). */ +/** Number of searches down the B-tree in btr_cur_search_to_nth_level(). */ extern ib_counter_t btr_cur_n_non_sea; /** Old value of btr_cur_n_non_sea. Copied by srv_refresh_innodb_monitor_stats(). Referenced by srv_printf_innodb_monitor(). */ extern ulint btr_cur_n_non_sea_old; /** Number of successful adaptive hash index lookups in -btr_cur_t::search_leaf(). */ +btr_cur_search_to_nth_level(). */ extern ib_counter_t btr_cur_n_sea; /** Old value of btr_cur_n_sea. Copied by srv_refresh_innodb_monitor_stats(). Referenced by diff --git a/storage/innobase/include/btr0pcur.h b/storage/innobase/include/btr0pcur.h index a25704ede53..cd8eacdc212 100644 --- a/storage/innobase/include/btr0pcur.h +++ b/storage/innobase/include/btr0pcur.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2023, MariaDB Corporation. +Copyright (c) 2017, 2022, 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 @@ -70,6 +70,24 @@ btr_pcur_init( /*==========*/ btr_pcur_t* pcur); /*!< in: persistent cursor */ +/**************************************************************//** +Initializes and opens a persistent cursor to an index tree. */ +inline +dberr_t +btr_pcur_open( + const dtuple_t* tuple, /*!< in: tuple on which search done */ + page_cur_mode_t mode, /*!< in: PAGE_CUR_L, ...; + NOTE that if the search is made using a unique + prefix of a record, mode should be + PAGE_CUR_LE, not PAGE_CUR_GE, as the latter + may end up on the previous page from the + record! */ + btr_latch_mode latch_mode,/*!< in: BTR_SEARCH_LEAF, ... */ + btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ + ib_uint64_t autoinc,/*!< in: PAGE_ROOT_AUTO_INC to be written + (0 if none) */ + mtr_t* mtr) /*!< in: mtr */ + MY_ATTRIBUTE((nonnull, warn_unused_result)); /** Opens an persistent cursor to an index tree without initializing the cursor. @param tuple tuple on which search done @@ -82,7 +100,8 @@ cursor. @param mtr mini-transaction @return DB_SUCCESS on success or error code otherwise. */ inline -dberr_t btr_pcur_open_with_no_init(const dtuple_t *tuple, page_cur_mode_t mode, +dberr_t btr_pcur_open_with_no_init(const dtuple_t *tuple, + page_cur_mode_t mode, btr_latch_mode latch_mode, btr_pcur_t *cursor, mtr_t *mtr); @@ -390,7 +409,8 @@ struct btr_pcur_t pos_state= BTR_PCUR_IS_POSITIONED; old_rec= nullptr; - return btr_cur.open_leaf(first, index, this->latch_mode, mtr); + return btr_cur.open_leaf(first, index, + BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode), mtr); } }; @@ -413,24 +433,6 @@ inline rec_t *btr_pcur_get_rec(const btr_pcur_t *cursor) return cursor->btr_cur.page_cur.rec; } -/**************************************************************//** -Initializes and opens a persistent cursor to an index tree. */ -inline -dberr_t -btr_pcur_open( - const dtuple_t* tuple, /*!< in: tuple on which search done */ - page_cur_mode_t mode, /*!< in: PAGE_CUR_LE, ... */ - btr_latch_mode latch_mode,/*!< in: BTR_SEARCH_LEAF, ... */ - btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ - mtr_t* mtr) /*!< in: mtr */ -{ - cursor->latch_mode= BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); - cursor->search_mode= mode; - cursor->pos_state= BTR_PCUR_IS_POSITIONED; - cursor->trx_if_known= nullptr; - return cursor->btr_cur.search_leaf(tuple, mode, latch_mode, mtr); -} - /** Open a cursor on the first user record satisfying the search condition; in case of no match, after the last index record. */ MY_ATTRIBUTE((nonnull, warn_unused_result)) @@ -438,15 +440,16 @@ inline dberr_t btr_pcur_open_on_user_rec( const dtuple_t* tuple, /*!< in: tuple on which search done */ + page_cur_mode_t mode, /*!< in: PAGE_CUR_L, ... */ btr_latch_mode latch_mode, /*!< in: BTR_SEARCH_LEAF or BTR_MODIFY_LEAF */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ mtr_t* mtr) /*!< in: mtr */ { + ut_ad(mode == PAGE_CUR_GE || mode == PAGE_CUR_G); ut_ad(latch_mode == BTR_SEARCH_LEAF || latch_mode == BTR_MODIFY_LEAF); - if (dberr_t err= - btr_pcur_open(tuple, PAGE_CUR_GE, latch_mode, cursor, mtr)) + if (dberr_t err= btr_pcur_open(tuple, mode, latch_mode, cursor, 0, mtr)) return err; if (!btr_pcur_is_after_last_on_page(cursor) || btr_pcur_is_after_last_in_tree(cursor)) diff --git a/storage/innobase/include/btr0pcur.inl b/storage/innobase/include/btr0pcur.inl index b827d70dc47..551f8f20fca 100644 --- a/storage/innobase/include/btr0pcur.inl +++ b/storage/innobase/include/btr0pcur.inl @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2015, 2023, MariaDB Corporation. +Copyright (c) 2015, 2022, 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 @@ -299,10 +299,38 @@ btr_pcur_init( pcur->btr_cur.rtr_info = NULL; } +/**************************************************************//** +Initializes and opens a persistent cursor to an index tree. */ +inline +dberr_t +btr_pcur_open( + const dtuple_t* tuple, /*!< in: tuple on which search done */ + page_cur_mode_t mode, /*!< in: PAGE_CUR_L, ...; + NOTE that if the search is made using a unique + prefix of a record, mode should be + PAGE_CUR_LE, not PAGE_CUR_GE, as the latter + may end up on the previous page from the + record! */ + btr_latch_mode latch_mode,/*!< in: BTR_SEARCH_LEAF, ... */ + btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ + ib_uint64_t autoinc,/*!< in: PAGE_ROOT_AUTO_INC to be written + (0 if none) */ + mtr_t* mtr) /*!< in: mtr */ +{ + ut_ad(!cursor->index()->is_spatial()); + cursor->latch_mode= BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); + cursor->search_mode= mode; + cursor->pos_state= BTR_PCUR_IS_POSITIONED; + cursor->trx_if_known= nullptr; + return btr_cur_search_to_nth_level(0, tuple, mode, latch_mode, + btr_pcur_get_btr_cur(cursor), + mtr, autoinc); +} + /** Opens an persistent cursor to an index tree without initializing the cursor. @param tuple tuple on which search done -@param mode search mode; NOTE that if the search is made using a +@param mode PAGE_CUR_L, ...; NOTE that if the search is made using a unique prefix of a record, mode should be PAGE_CUR_LE, not PAGE_CUR_GE, as the latter may end up on the previous page of the record! @@ -311,7 +339,8 @@ cursor. @param mtr mini-transaction @return DB_SUCCESS on success or error code otherwise. */ inline -dberr_t btr_pcur_open_with_no_init(const dtuple_t *tuple, page_cur_mode_t mode, +dberr_t btr_pcur_open_with_no_init(const dtuple_t *tuple, + page_cur_mode_t mode, btr_latch_mode latch_mode, btr_pcur_t *cursor, mtr_t *mtr) { @@ -319,7 +348,10 @@ dberr_t btr_pcur_open_with_no_init(const dtuple_t *tuple, page_cur_mode_t mode, cursor->search_mode= mode; cursor->pos_state= BTR_PCUR_IS_POSITIONED; cursor->trx_if_known= nullptr; - return cursor->btr_cur.search_leaf(tuple, mode, latch_mode, mtr); + + /* Search with the tree cursor */ + return btr_cur_search_to_nth_level(0, tuple, mode, latch_mode, + btr_pcur_get_btr_cur(cursor), mtr); } /**************************************************************//** diff --git a/storage/innobase/include/btr0types.h b/storage/innobase/include/btr0types.h index 912c022c64f..6118bfbc128 100644 --- a/storage/innobase/include/btr0types.h +++ b/storage/innobase/include/btr0types.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2018, 2023, MariaDB Corporation. +Copyright (c) 2018, 2022, 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 @@ -55,26 +55,25 @@ in the index record. */ #define BTR_EXTERN_LOCAL_STORED_MAX_SIZE \ (BTR_EXTERN_FIELD_REF_SIZE * 2) -/** Latching modes for btr_cur_t::search_leaf(). */ +/** Latching modes for btr_cur_search_to_nth_level(). */ enum btr_latch_mode { /** Search a record on a leaf page and S-latch it. */ BTR_SEARCH_LEAF = RW_S_LATCH, /** (Prepare to) modify a record on a leaf page and X-latch it. */ BTR_MODIFY_LEAF = RW_X_LATCH, - /** U-latch root and X-latch a leaf page */ - BTR_MODIFY_ROOT_AND_LEAF = RW_SX_LATCH, /** Obtain no latches. */ BTR_NO_LATCHES = RW_NO_LATCH, - /** Search the previous record. - Used in btr_pcur_move_backward_from_page(). */ + /** Search the previous record. */ BTR_SEARCH_PREV = 4 | BTR_SEARCH_LEAF, - /** Modify the previous record. - Used in btr_pcur_move_backward_from_page() and ibuf_insert(). */ + /** Modify the previous record. */ BTR_MODIFY_PREV = 4 | BTR_MODIFY_LEAF, - /** Start modifying the entire B-tree. */ + /** Start searching the entire B-tree. */ + BTR_SEARCH_TREE = 8 | BTR_SEARCH_LEAF, + /** Start modifying1 the entire B-tree. */ BTR_MODIFY_TREE = 8 | BTR_MODIFY_LEAF, - /** Continue modifying the entire R-tree. - Only used by rtr_search_to_nth_level(). */ + /** Continue searching the entire B-tree. */ + BTR_CONT_SEARCH_TREE = 4 | BTR_SEARCH_TREE, + /** Continue modifying the entire B-tree. */ BTR_CONT_MODIFY_TREE = 4 | BTR_MODIFY_TREE, /* BTR_INSERT, BTR_DELETE and BTR_DELETE_MARK are mutually @@ -99,14 +98,14 @@ enum btr_latch_mode { dict_index_t::lock S-latch is being held. */ BTR_SEARCH_LEAF_ALREADY_S_LATCHED = BTR_SEARCH_LEAF | BTR_ALREADY_S_LATCHED, + /** Search the entire index tree, assuming that the + dict_index_t::lock S-latch is being held. */ + BTR_SEARCH_TREE_ALREADY_S_LATCHED = BTR_SEARCH_TREE + | BTR_ALREADY_S_LATCHED, /** Search and X-latch a leaf page, assuming that the dict_index_t::lock is being held in non-exclusive mode. */ BTR_MODIFY_LEAF_ALREADY_LATCHED = BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED, - /** U-latch root and X-latch a leaf page, assuming that - dict_index_t::lock is being held in U mode. */ - BTR_MODIFY_ROOT_AND_LEAF_ALREADY_LATCHED = BTR_MODIFY_ROOT_AND_LEAF - | BTR_ALREADY_S_LATCHED, /** Attempt to delete-mark a secondary index record. */ BTR_DELETE_MARK_LEAF = BTR_MODIFY_LEAF | BTR_DELETE_MARK, @@ -133,9 +132,6 @@ enum btr_latch_mode { /** Attempt to delete a record in the tree. */ BTR_PURGE_TREE = BTR_MODIFY_TREE | BTR_LATCH_FOR_DELETE, - /** Attempt to delete a record in an x-latched tree. */ - BTR_PURGE_TREE_ALREADY_LATCHED = BTR_PURGE_TREE - | BTR_ALREADY_S_LATCHED, /** Attempt to insert a record into the tree. */ BTR_INSERT_TREE = BTR_MODIFY_TREE | BTR_LATCH_FOR_INSERT, diff --git a/storage/innobase/include/gis0rtree.h b/storage/innobase/include/gis0rtree.h index b07261ce042..777f2432c93 100644 --- a/storage/innobase/include/gis0rtree.h +++ b/storage/innobase/include/gis0rtree.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2014, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2023, MariaDB Corporation. +Copyright (c) 2017, 2022, 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 @@ -59,44 +59,6 @@ Created 2013/03/27 Jimmy Yang and Allen Lai /* Geometry data header */ #define GEO_DATA_HEADER_SIZE 4 - -/** Search for a spatial index leaf page record. -@param cur cursor -@param tuple search tuple -@param latch_mode latching mode -@param mtr mini-transaction -@param mode search mode */ -dberr_t rtr_search_leaf(btr_cur_t *cur, const dtuple_t *tuple, - btr_latch_mode latch_mode, mtr_t *mtr, - page_cur_mode_t mode= PAGE_CUR_RTREE_LOCATE) - MY_ATTRIBUTE((nonnull, warn_unused_result)); - -/** Search for inserting a spatial index leaf page record. -@param cur cursor -@param tuple search tuple -@param latch_mode latching mode -@param mtr mini-transaction */ -inline dberr_t rtr_insert_leaf(btr_cur_t *cur, const dtuple_t *tuple, - btr_latch_mode latch_mode, mtr_t *mtr) -{ - return rtr_search_leaf(cur, tuple, latch_mode, mtr, PAGE_CUR_RTREE_INSERT); -} - -/** Search for a spatial index leaf page record. -@param pcur cursor -@param tuple search tuple -@param mode search mode -@param mtr mini-transaction */ -dberr_t rtr_search_leaf(btr_pcur_t *pcur, const dtuple_t *tuple, - page_cur_mode_t mode, mtr_t *mtr) - MY_ATTRIBUTE((nonnull, warn_unused_result)); - -dberr_t rtr_search_to_nth_level(ulint level, const dtuple_t *tuple, - page_cur_mode_t mode, - btr_latch_mode latch_mode, - btr_cur_t *cur, mtr_t *mtr) - MY_ATTRIBUTE((nonnull, warn_unused_result)); - /**********************************************************************//** Builds a Rtree node pointer out of a physical record and a page number. @return own: node pointer */ @@ -333,9 +295,11 @@ rtr_store_parent_path( /**************************************************************//** Initializes and opens a persistent cursor to an index tree. It should be closed with btr_pcur_close. */ -bool rtr_search( +bool +rtr_pcur_open( + dict_index_t* index, /*!< in: index */ const dtuple_t* tuple, /*!< in: tuple on which search done */ - btr_latch_mode latch_mode,/*!< in: BTR_MODIFY_LEAF, ... */ + btr_latch_mode latch_mode,/*!< in: BTR_SEARCH_LEAF, ... */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ mtr_t* mtr) /*!< in: mtr */ MY_ATTRIBUTE((warn_unused_result)); diff --git a/storage/innobase/include/gis0type.h b/storage/innobase/include/gis0type.h index d6a4ef67a38..4fccfdb6c26 100644 --- a/storage/innobase/include/gis0type.h +++ b/storage/innobase/include/gis0type.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2014, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2018, 2023, MariaDB Corporation. +Copyright (c) 2018, 2020, 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 @@ -105,6 +105,12 @@ typedef struct rtr_info{ matched_rec_t* matches;/*!< struct holding matching leaf records */ mysql_mutex_t rtr_path_mutex; /*!< mutex protect the "path" vector */ + buf_block_t* tree_blocks[RTR_MAX_LEVELS + RTR_LEAF_LATCH_NUM]; + /*!< tracking pages that would be locked + at leaf level, for future free */ + ulint tree_savepoints[RTR_MAX_LEVELS + RTR_LEAF_LATCH_NUM]; + /*!< savepoint used to release latches/blocks + on each level and leaf level */ rtr_mbr_t mbr; /*!< the search MBR */ que_thr_t* thr; /*!< the search thread */ mem_heap_t* heap; /*!< memory heap */ diff --git a/storage/innobase/include/ibuf0ibuf.inl b/storage/innobase/include/ibuf0ibuf.inl index 1e21f74ff2b..9f4e937f31d 100644 --- a/storage/innobase/include/ibuf0ibuf.inl +++ b/storage/innobase/include/ibuf0ibuf.inl @@ -100,8 +100,9 @@ ibuf_should_try( decide */ { return(innodb_change_buffering - && !(index->type & (DICT_CLUSTERED | DICT_IBUF)) && ibuf.max_size != 0 + && !dict_index_is_clust(index) + && !dict_index_is_spatial(index) && index->table->quiesce == QUIESCE_NONE && (ignore_sec_unique || !dict_index_is_unique(index))); } diff --git a/storage/innobase/include/mtr0log.h b/storage/innobase/include/mtr0log.h index 0dfb50125bd..093b706c1de 100644 --- a/storage/innobase/include/mtr0log.h +++ b/storage/innobase/include/mtr0log.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2019, 2023, MariaDB Corporation. +Copyright (c) 2019, 2022, 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 @@ -401,9 +401,7 @@ inline byte *mtr_t::log_write(const page_id_t id, const buf_page_t *bpage, ut_ad(have_offset || offset == 0); ut_ad(offset + len <= srv_page_size); static_assert(MIN_4BYTE >= UNIV_PAGE_SIZE_MAX, "consistency"); - ut_ad(type == FREE_PAGE || type == OPTION || (type == EXTENDED && !bpage) || - memo_contains_flagged(bpage, - MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX)); + size_t max_len; if (!have_len) max_len= 1 + 5 + 5; diff --git a/storage/innobase/include/mtr0mtr.h b/storage/innobase/include/mtr0mtr.h index 3c4e9da69a2..41f9b473856 100644 --- a/storage/innobase/include/mtr0mtr.h +++ b/storage/innobase/include/mtr0mtr.h @@ -37,6 +37,15 @@ Created 11/26/1995 Heikki Tuuri /** Commit a mini-transaction. */ #define mtr_commit(m) (m)->commit() +/** Set and return a savepoint in mtr. +@return savepoint */ +#define mtr_set_savepoint(m) (m)->get_savepoint() + +/** Release the (index tree) s-latch stored in an mtr memo after a +savepoint. */ +#define mtr_release_s_latch_at_savepoint(m, s, l) \ + (m)->release_s_latch_at_savepoint((s), (l)) + /** Change the logging mode of a mini-transaction. @return old mode */ #define mtr_set_log_mode(m, d) (m)->set_log_mode((d)) @@ -51,10 +60,13 @@ Created 11/26/1995 Heikki Tuuri # define mtr_sx_lock_index(i,m) (m)->u_lock(&(i)->lock) #endif +#define mtr_release_block_at_savepoint(m, s, b) \ + (m)->release_block_at_savepoint((s), (b)) + /** Mini-transaction memo stack slot. */ struct mtr_memo_slot_t { - /** pointer to the object */ + /** pointer to the object, or nullptr if released */ void *object; /** type of the stored object */ mtr_memo_type_t type; @@ -113,36 +125,41 @@ struct mtr_t { return m_memo.size(); } - /** Get the block at a savepoint */ - buf_block_t *at_savepoint(ulint savepoint) const + /** Release the (index tree) s-latch stored in an mtr memo after a savepoint. + @param savepoint value returned by get_savepoint() + @param lock index latch to release */ + void release_s_latch_at_savepoint(ulint savepoint, index_lock *lock) { ut_ad(is_active()); - const mtr_memo_slot_t &slot= m_memo[savepoint]; - ut_ad(slot.type < MTR_MEMO_S_LOCK); - ut_ad(slot.object); - return static_cast(slot.object); + mtr_memo_slot_t &slot= m_memo[savepoint]; + ut_ad(slot.object == lock); + ut_ad(slot.type == MTR_MEMO_S_LOCK); + slot.object= nullptr; + lock->s_unlock(); } - - /** Try to get a block at a savepoint. - @param savepoint the savepoint right before the block was acquired - @return the block at the savepoint - @retval nullptr if no buffer block was registered at that savepoint */ - buf_block_t *block_at_savepoint(ulint savepoint) const + /** Release the block in an mtr memo after a savepoint. */ + void release_block_at_savepoint(ulint savepoint, buf_block_t *block) { ut_ad(is_active()); - const mtr_memo_slot_t &slot= m_memo[savepoint]; - return slot.type < MTR_MEMO_S_LOCK - ? static_cast(slot.object) - : nullptr; + mtr_memo_slot_t &slot= m_memo[savepoint]; + ut_ad(slot.object == block); + ut_ad(!(slot.type & MTR_MEMO_MODIFY)); + slot.object= nullptr; + block->page.unfix(); + + switch (slot.type) { + case MTR_MEMO_PAGE_S_FIX: + block->page.lock.s_unlock(); + break; + case MTR_MEMO_PAGE_SX_FIX: + case MTR_MEMO_PAGE_X_FIX: + block->page.lock.u_or_x_unlock(slot.type == MTR_MEMO_PAGE_SX_FIX); + break; + default: + break; + } } - /** Retrieve a page that has already been latched. - @param id page identifier - @param type page latch type - @return block */ - buf_block_t *get_already_latched(const page_id_t id, mtr_memo_type_t type) - const; - /** @return if we are about to make a clean buffer block dirty */ static bool is_block_dirtied(const buf_page_t &b) { @@ -391,17 +408,28 @@ public: @param rw_latch latch to acquire */ void upgrade_buffer_fix(ulint savepoint, rw_lock_type_t rw_latch); - /** Register a page latch on a previously buffer-fixed block. */ - void lock_register(ulint savepoint, mtr_memo_type_t type) + /** Register a page latch on a buffer-fixed block was buffer-fixed. + @param latch latch type */ + void u_lock_register(ulint savepoint) + { + mtr_memo_slot_t &slot= m_memo[savepoint]; + ut_ad(slot.type == MTR_MEMO_BUF_FIX); + slot.type= MTR_MEMO_PAGE_SX_FIX; + } + + /** Register a page latch on a buffer-fixed block was buffer-fixed. + @param latch latch type */ + void s_lock_register(ulint savepoint) { mtr_memo_slot_t &slot= m_memo[savepoint]; ut_ad(slot.type == MTR_MEMO_BUF_FIX); - ut_ad(type <= MTR_MEMO_PAGE_SX_FIX); - slot.type= type; + slot.type= MTR_MEMO_PAGE_S_FIX; } /** Upgrade U locks on a block to X */ void page_lock_upgrade(const buf_block_t &block); + /** Upgrade U lock to X */ + void lock_upgrade(const index_lock &lock); /** Check if we are holding tablespace latch @param space tablespace to search for diff --git a/storage/innobase/include/small_vector.h b/storage/innobase/include/small_vector.h index d28a36184b8..76069cfc168 100644 --- a/storage/innobase/include/small_vector.h +++ b/storage/innobase/include/small_vector.h @@ -71,7 +71,6 @@ public: using const_iterator= const T *; using reverse_iterator= std::reverse_iterator; using reference= T &; - using const_reference= const T&; iterator begin() { return static_cast(BeginX); } const_iterator begin() const { return static_cast(BeginX); } @@ -82,8 +81,6 @@ public: reverse_iterator rend() { return reverse_iterator(begin()); } reference operator[](size_t i) { assert(i < size()); return begin()[i]; } - const_reference operator[](size_t i) const - { return const_cast(*this)[i]; } void erase(const_iterator S, const_iterator E) { diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc index 66a3df3a1ce..2c93d7ffe5a 100644 --- a/storage/innobase/mtr/mtr0mtr.cc +++ b/storage/innobase/mtr/mtr0mtr.cc @@ -40,8 +40,6 @@ Created 11/26/1995 Heikki Tuuri void mtr_memo_slot_t::release() const { - ut_ad(object); - switch (type) { case MTR_MEMO_S_LOCK: static_cast(object)->s_unlock(); @@ -123,7 +121,11 @@ inline void mtr_t::release_resources() void mtr_t::release() { for (auto it= m_memo.rbegin(); it != m_memo.rend(); it++) - it->release(); + { + mtr_memo_slot_t &slot= *it; + if (slot.object) + slot.release(); + } m_memo.clear(); } @@ -189,7 +191,7 @@ void mtr_t::commit() for (const mtr_memo_slot_t &slot : m_memo) { - if (slot.type & MTR_MEMO_MODIFY) + if (slot.object && slot.type & MTR_MEMO_MODIFY) { ut_ad(slot.type == MTR_MEMO_PAGE_X_MODIFY || slot.type == MTR_MEMO_PAGE_SX_MODIFY); @@ -224,7 +226,8 @@ void mtr_t::rollback_to_savepoint(ulint begin, ulint end) while (s-- > begin) { const mtr_memo_slot_t &slot= m_memo[s]; - ut_ad(slot.object); + if (!slot.object) + continue; /* This is intended for releasing latches on indexes or unmodified buffer pool pages. */ ut_ad(slot.type <= MTR_MEMO_SX_LOCK); @@ -268,7 +271,8 @@ void mtr_t::commit_shrink(fil_space_t &space) for (mtr_memo_slot_t &slot : m_memo) { - ut_ad(slot.object); + if (!slot.object) + continue; switch (slot.type) { default: ut_ad("invalid type" == 0); @@ -932,6 +936,15 @@ void mtr_t::page_lock_upgrade(const buf_block_t &block) #endif /* BTR_CUR_HASH_ADAPT */ } +void mtr_t::lock_upgrade(const index_lock &lock) +{ + ut_ad(lock.have_x()); + + for (mtr_memo_slot_t &slot : m_memo) + if (slot.object == &lock && slot.type == MTR_MEMO_SX_LOCK) + slot.type= MTR_MEMO_X_LOCK; +} + /** Latch a buffer pool block. @param block block to be latched @param rw_latch RW_S_LATCH, RW_SX_LATCH, RW_X_LATCH, RW_NO_LATCH */ @@ -1103,8 +1116,7 @@ buf_block_t* mtr_t::memo_contains_page_flagged(const byte *ptr, ulint flags) for (const mtr_memo_slot_t &slot : m_memo) { - ut_ad(slot.object); - if (!(flags & slot.type)) + if (!slot.object || !(flags & slot.type)) continue; buf_page_t *bpage= static_cast(slot.object); @@ -1164,23 +1176,20 @@ void mtr_t::free(const fil_space_t &space, uint32_t offset) buf_block_t *freed= nullptr; const page_id_t id{space.id, offset}; - for (auto it= m_memo.end(); it != m_memo.begin(); ) + for (auto it= m_memo.rbegin(); it != m_memo.rend(); it++) { - it--; - next: mtr_memo_slot_t &slot= *it; buf_block_t *block= static_cast(slot.object); - ut_ad(block); - if (block == freed) + if (!block); + else if (block == freed) { if (slot.type & (MTR_MEMO_PAGE_SX_FIX | MTR_MEMO_PAGE_X_FIX)) slot.type= MTR_MEMO_PAGE_X_FIX; else { ut_ad(slot.type == MTR_MEMO_BUF_FIX); + slot.object= nullptr; block->page.unfix(); - m_memo.erase(it, it + 1); - goto next; } } else if (slot.type & (MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX) && diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc index 861095b421e..45c35bc6995 100644 --- a/storage/innobase/row/row0import.cc +++ b/storage/innobase/row/row0import.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2012, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2015, 2023, MariaDB Corporation. +Copyright (c) 2015, 2022, 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 @@ -1629,9 +1629,6 @@ inline dberr_t IndexPurge::purge_pessimistic_delete() noexcept dberr_t IndexPurge::purge() noexcept { btr_pcur_store_position(&m_pcur, &m_mtr); - m_mtr.commit(); - m_mtr.start(); - m_mtr.set_log_mode(MTR_LOG_NO_REDO); dberr_t err= purge_pessimistic_delete(); m_mtr.start(); diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index eea66ae05aa..81babd0082e 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2023, MariaDB Corporation. +Copyright (c) 2016, 2022, 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 @@ -314,10 +314,8 @@ row_ins_clust_index_entry_by_modify( } if (mode != BTR_MODIFY_TREE) { - ut_ad(mode == BTR_MODIFY_LEAF - || mode == BTR_MODIFY_LEAF_ALREADY_LATCHED - || mode == BTR_MODIFY_ROOT_AND_LEAF - || mode == BTR_MODIFY_ROOT_AND_LEAF_ALREADY_LATCHED); + ut_ad((mode & ulint(~BTR_ALREADY_S_LATCHED)) + == BTR_MODIFY_LEAF); /* Try optimistic updating of the record, keeping changes within the page */ @@ -1623,7 +1621,8 @@ row_ins_check_foreign_constraint( dtuple_set_n_fields_cmp(entry, foreign->n_fields); pcur.btr_cur.page_cur.index = check_index; - err = btr_pcur_open(entry, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); + err = btr_pcur_open(entry, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, 0, + &mtr); if (UNIV_UNLIKELY(err != DB_SUCCESS)) { goto end_scan; } @@ -2120,7 +2119,7 @@ row_ins_scan_sec_index_for_duplicate( pcur.btr_cur.page_cur.index = index; trx_t* const trx = thr_get_trx(thr); dberr_t err = btr_pcur_open(entry, PAGE_CUR_GE, BTR_SEARCH_LEAF, - &pcur, mtr); + &pcur, 0, mtr); if (err != DB_SUCCESS) { goto end_scan; } @@ -2540,8 +2539,8 @@ row_ins_index_entry_big_rec( index->set_modified(mtr); } - dberr_t error = btr_pcur_open(entry, PAGE_CUR_LE, BTR_MODIFY_TREE, - &pcur, &mtr); + dberr_t error = btr_pcur_open(entry, PAGE_CUR_LE, + BTR_MODIFY_TREE, &pcur, 0, &mtr); if (error != DB_SUCCESS) { return error; } @@ -2599,10 +2598,11 @@ row_ins_clust_index_entry_low( que_thr_t* thr) /*!< in: query thread */ { btr_pcur_t pcur; + btr_cur_t* cursor; dberr_t err = DB_SUCCESS; big_rec_t* big_rec = NULL; mtr_t mtr; - uint64_t auto_inc = 0; + ib_uint64_t auto_inc = 0; mem_heap_t* offsets_heap = NULL; rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs* offsets = offsets_; @@ -2618,7 +2618,7 @@ row_ins_clust_index_entry_low( ut_ad(!n_uniq || n_uniq == dict_index_get_n_unique(index)); ut_ad(!trx->in_rollback); - mtr.start(); + mtr_start(&mtr); if (index->table->is_temporary()) { /* Disable REDO logging as the lifetime of temp-tables is @@ -2658,13 +2658,6 @@ row_ins_clust_index_entry_low( dfield->type.mtype, dfield->type.prtype & DATA_UNSIGNED); - if (auto_inc - && mode != BTR_MODIFY_TREE) { - mode = btr_latch_mode( - BTR_MODIFY_ROOT_AND_LEAF - ^ BTR_MODIFY_LEAF - ^ mode); - } } } } @@ -2674,26 +2667,20 @@ row_ins_clust_index_entry_low( the function will return in both low_match and up_match of the cursor sensible values */ pcur.btr_cur.page_cur.index = index; - err = btr_pcur_open(entry, PAGE_CUR_LE, mode, &pcur, &mtr); + err = btr_pcur_open(entry, PAGE_CUR_LE, mode, &pcur, auto_inc, &mtr); if (err != DB_SUCCESS) { index->table->file_unreadable = true; -err_exit: +commit_exit: mtr.commit(); goto func_exit; } - if (auto_inc) { - buf_block_t* root - = mtr.at_savepoint(mode != BTR_MODIFY_ROOT_AND_LEAF); - ut_ad(index->page == root->page.id().page_no()); - page_set_autoinc(root, auto_inc, &mtr, false); - } - - btr_pcur_get_btr_cur(&pcur)->thr = thr; + cursor = btr_pcur_get_btr_cur(&pcur); + cursor->thr = thr; #ifdef UNIV_DEBUG { - page_t* page = btr_pcur_get_page(&pcur); + page_t* page = btr_cur_get_page(cursor); rec_t* first_rec = page_rec_get_next( page_get_infimum_rec(page)); @@ -2702,7 +2689,7 @@ err_exit: } #endif /* UNIV_DEBUG */ - block = btr_pcur_get_block(&pcur); + block = btr_cur_get_block(cursor); DBUG_EXECUTE_IF("row_ins_row_level", goto skip_bulk_insert;); @@ -2724,7 +2711,7 @@ err_exit: if (err != DB_SUCCESS) { trx->error_state = err; - goto err_exit; + goto commit_exit; } if (index->table->n_rec_locks) { @@ -2754,7 +2741,7 @@ skip_bulk_insert: ut_ad(index->is_instant()); ut_ad(!dict_index_is_online_ddl(index)); - const rec_t* rec = btr_pcur_get_rec(&pcur); + const rec_t* rec = btr_cur_get_rec(cursor); if (rec_get_info_bits(rec, page_rec_is_comp(rec)) & REC_INFO_MIN_REC_FLAG) { @@ -2763,17 +2750,16 @@ skip_bulk_insert: goto err_exit; } - ut_ad(!row_ins_must_modify_rec(&pcur.btr_cur)); + ut_ad(!row_ins_must_modify_rec(cursor)); goto do_insert; } - if (rec_is_metadata(btr_pcur_get_rec(&pcur), *index)) { + if (rec_is_metadata(btr_cur_get_rec(cursor), *index)) { goto do_insert; } if (n_uniq - && (pcur.btr_cur.up_match >= n_uniq - || pcur.btr_cur.low_match >= n_uniq)) { + && (cursor->up_match >= n_uniq || cursor->low_match >= n_uniq)) { if (flags == (BTR_CREATE_FLAG | BTR_NO_LOCKING_FLAG @@ -2781,7 +2767,7 @@ skip_bulk_insert: /* Set no locks when applying log in online table rebuild. Only check for duplicates. */ err = row_ins_duplicate_error_in_clust_online( - n_uniq, entry, &pcur.btr_cur, + n_uniq, entry, cursor, &offsets, &offsets_heap); switch (err) { @@ -2792,24 +2778,26 @@ skip_bulk_insert: /* fall through */ case DB_SUCCESS_LOCKED_REC: case DB_DUPLICATE_KEY: - trx->error_info = index; + trx->error_info = cursor->index(); } } else { /* Note that the following may return also DB_LOCK_WAIT */ err = row_ins_duplicate_error_in_clust( - flags, &pcur.btr_cur, entry, thr); + flags, cursor, entry, thr); } if (err != DB_SUCCESS) { - goto err_exit; +err_exit: + mtr_commit(&mtr); + goto func_exit; } } /* Note: Allowing duplicates would qualify for modification of an existing record as the new entry is exactly same as old entry. */ - if (row_ins_must_modify_rec(&pcur.btr_cur)) { + if (row_ins_must_modify_rec(cursor)) { /* There is already an index entry with a long enough common prefix, we must convert the insert into a modify of an existing record */ @@ -2827,13 +2815,10 @@ do_insert: rec_t* insert_rec; if (mode != BTR_MODIFY_TREE) { - ut_ad(mode == BTR_MODIFY_LEAF - || mode == BTR_MODIFY_LEAF_ALREADY_LATCHED - || mode == BTR_MODIFY_ROOT_AND_LEAF - || mode - == BTR_MODIFY_ROOT_AND_LEAF_ALREADY_LATCHED); + ut_ad(mode == BTR_MODIFY_LEAF || + mode == BTR_MODIFY_LEAF_ALREADY_LATCHED); err = btr_cur_optimistic_insert( - flags, &pcur.btr_cur, &offsets, &offsets_heap, + flags, cursor, &offsets, &offsets_heap, entry, &insert_rec, &big_rec, n_ext, thr, &mtr); } else { @@ -2842,15 +2827,17 @@ do_insert: goto err_exit; } + DEBUG_SYNC_C("before_insert_pessimitic_row_ins_clust"); + err = btr_cur_optimistic_insert( - flags, &pcur.btr_cur, + flags, cursor, &offsets, &offsets_heap, entry, &insert_rec, &big_rec, n_ext, thr, &mtr); if (err == DB_FAIL) { err = btr_cur_pessimistic_insert( - flags, &pcur.btr_cur, + flags, cursor, &offsets, &offsets_heap, entry, &insert_rec, &big_rec, n_ext, thr, &mtr); @@ -2962,7 +2949,9 @@ row_ins_sec_index_entry_low( rtr_init_rtr_info(&rtr_info, false, &cursor, index, false); rtr_info_update_btr(&cursor, &rtr_info); - err = rtr_insert_leaf(&cursor, entry, search_mode, &mtr); + err = btr_cur_search_to_nth_level(0, entry, + PAGE_CUR_RTREE_INSERT, + search_mode, &cursor, &mtr); if (err == DB_SUCCESS && search_mode == BTR_MODIFY_LEAF && rtr_info.mbr_adj) { @@ -2978,8 +2967,9 @@ row_ins_sec_index_entry_low( } else { index->set_modified(mtr); } - err = rtr_insert_leaf(&cursor, entry, - search_mode, &mtr); + err = btr_cur_search_to_nth_level( + 0, entry, PAGE_CUR_RTREE_INSERT, + search_mode, &cursor, &mtr); } DBUG_EXECUTE_IF( @@ -2995,8 +2985,8 @@ row_ins_sec_index_entry_low( : BTR_INSERT)); } - err = cursor.search_leaf(entry, PAGE_CUR_LE, search_mode, - &mtr); + err = btr_cur_search_to_nth_level(0, entry, PAGE_CUR_LE, + search_mode, &cursor, &mtr); } if (err != DB_SUCCESS) { @@ -3072,12 +3062,12 @@ row_ins_sec_index_entry_low( prevent any insertion of a duplicate by another transaction. Let us now reposition the cursor and continue the insertion (bypassing the change buffer). */ - err = cursor.search_leaf( - entry, PAGE_CUR_LE, + err = btr_cur_search_to_nth_level( + 0, entry, PAGE_CUR_LE, btr_latch_mode(search_mode & ~(BTR_INSERT | BTR_IGNORE_SEC_UNIQUE)), - &mtr); + &cursor, &mtr); if (err != DB_SUCCESS) { goto func_exit; } diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc index 0743dc2bb50..94d69d88fb5 100644 --- a/storage/innobase/row/row0log.cc +++ b/storage/innobase/row/row0log.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2011, 2018, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2023, MariaDB Corporation. +Copyright (c) 2017, 2022, 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 @@ -1696,8 +1696,8 @@ err_exit: mtr->start(); index->set_modified(*mtr); pcur->btr_cur.page_cur.index = index; - error = btr_pcur_open(entry, PAGE_CUR_LE, BTR_PURGE_TREE, pcur, - mtr); + error = btr_pcur_open(entry, PAGE_CUR_LE, + BTR_PURGE_TREE, pcur, 0, mtr); if (error) { goto err_exit; } @@ -1780,8 +1780,8 @@ row_log_table_apply_delete( mtr_start(&mtr); index->set_modified(mtr); - dberr_t err = btr_pcur_open(old_pk, PAGE_CUR_LE, BTR_PURGE_TREE, &pcur, - &mtr); + dberr_t err = btr_pcur_open(old_pk, PAGE_CUR_LE, + BTR_PURGE_TREE, &pcur, 0, &mtr); if (err != DB_SUCCESS) { goto all_done; } @@ -1917,8 +1917,8 @@ row_log_table_apply_update( mtr.start(); index->set_modified(mtr); - error = btr_pcur_open(old_pk, PAGE_CUR_LE, BTR_MODIFY_TREE, &pcur, - &mtr); + error = btr_pcur_open(old_pk, PAGE_CUR_LE, + BTR_MODIFY_TREE, &pcur, 0, &mtr); if (error != DB_SUCCESS) { func_exit: mtr.commit(); @@ -3084,8 +3084,11 @@ row_log_apply_op_low( record. The operation may already have been performed, depending on when the row in the clustered index was scanned. */ - *error = cursor.search_leaf(entry, PAGE_CUR_LE, has_index_lock - ? BTR_MODIFY_TREE : BTR_MODIFY_LEAF, &mtr); + *error = btr_cur_search_to_nth_level(0, entry, PAGE_CUR_LE, + has_index_lock + ? BTR_MODIFY_TREE + : BTR_MODIFY_LEAF, + &cursor, &mtr); if (UNIV_UNLIKELY(*error != DB_SUCCESS)) { goto func_exit; } @@ -3135,9 +3138,9 @@ row_log_apply_op_low( mtr_commit(&mtr); mtr_start(&mtr); index->set_modified(mtr); - *error = cursor.search_leaf(entry, PAGE_CUR_LE, - BTR_MODIFY_TREE, - &mtr); + *error = btr_cur_search_to_nth_level( + 0, entry, PAGE_CUR_LE, + BTR_MODIFY_TREE, &cursor, &mtr); if (UNIV_UNLIKELY(*error != DB_SUCCESS)) { goto func_exit; } @@ -3239,9 +3242,9 @@ insert_the_rec: mtr_commit(&mtr); mtr_start(&mtr); index->set_modified(mtr); - *error = cursor.search_leaf(entry, PAGE_CUR_LE, - BTR_MODIFY_TREE, - &mtr); + *error = btr_cur_search_to_nth_level( + 0, entry, PAGE_CUR_LE, + BTR_MODIFY_TREE, &cursor, &mtr); if (*error != DB_SUCCESS) { break; } diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index 535762ee105..caa2646eee2 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -150,8 +150,9 @@ public: false); rtr_info_update_btr(&ins_cur, &rtr_info); - error = rtr_insert_leaf(&ins_cur, dtuple, - BTR_MODIFY_LEAF, &mtr); + error = btr_cur_search_to_nth_level( + 0, dtuple, PAGE_CUR_RTREE_INSERT, + BTR_MODIFY_LEAF, &ins_cur, &mtr); /* It need to update MBR in parent entry, so change search mode to BTR_MODIFY_TREE */ @@ -163,8 +164,10 @@ public: rtr_info_update_btr(&ins_cur, &rtr_info); mtr.start(); index->set_modified(mtr); - error = rtr_insert_leaf(&ins_cur, dtuple, - BTR_MODIFY_TREE, &mtr); + error = btr_cur_search_to_nth_level( + 0, dtuple, + PAGE_CUR_RTREE_INSERT, + BTR_MODIFY_TREE, &ins_cur, &mtr); } if (error == DB_SUCCESS) { @@ -186,8 +189,11 @@ public: &ins_cur, index, false); rtr_info_update_btr(&ins_cur, &rtr_info); - error = rtr_insert_leaf(&ins_cur, dtuple, - BTR_MODIFY_TREE, &mtr); + error = btr_cur_search_to_nth_level( + 0, dtuple, + PAGE_CUR_RTREE_INSERT, + BTR_MODIFY_TREE, + &ins_cur, &mtr); if (error == DB_SUCCESS) { error = btr_cur_pessimistic_insert( diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc index 753b42332fc..e216d9faa3b 100644 --- a/storage/innobase/row/row0purge.cc +++ b/storage/innobase/row/row0purge.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2023, MariaDB Corporation. +Copyright (c) 2017, 2022, 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 @@ -104,7 +104,7 @@ bool row_purge_remove_clust_if_poss_low( /*===============================*/ purge_node_t* node, /*!< in/out: row purge node */ - btr_latch_mode mode) /*!< in: BTR_MODIFY_LEAF or BTR_PURGE_TREE */ + btr_latch_mode mode) /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */ { dict_index_t* index = dict_table_get_first_index(node->table); table_id_t table_id = 0; @@ -342,20 +342,17 @@ row_purge_remove_sec_if_poss_tree( ibool success = TRUE; dberr_t err; mtr_t mtr; + enum row_search_result search_result; log_free_check(); mtr.start(); index->set_modified(mtr); pcur.btr_cur.page_cur.index = index; - if (index->is_spatial()) { - if (!rtr_search(entry, BTR_PURGE_TREE, &pcur, &mtr)) { - goto found; - } - goto func_exit; - } + search_result = row_search_index_entry(entry, BTR_PURGE_TREE, + &pcur, &mtr); - switch (row_search_index_entry(entry, BTR_PURGE_TREE, &pcur, &mtr)) { + switch (search_result) { case ROW_NOT_FOUND: /* Not found. This is a legitimate condition. In a rollback, InnoDB will remove secondary recs that would @@ -384,7 +381,6 @@ row_purge_remove_sec_if_poss_tree( which cannot be purged yet, requires its existence. If some requires, we should do nothing. */ -found: if (row_purge_poss_sec(node, index, entry, &pcur, &mtr, true)) { /* Remove the index record, which should have been @@ -443,6 +439,8 @@ row_purge_remove_sec_if_poss_leaf( { mtr_t mtr; btr_pcur_t pcur; + enum btr_latch_mode mode; + enum row_search_result search_result; bool success = true; log_free_check(); @@ -451,27 +449,31 @@ row_purge_remove_sec_if_poss_leaf( mtr.start(); index->set_modified(mtr); + /* Change buffering is disabled for spatial index and + virtual index. */ + mode = (index->type & (DICT_SPATIAL | DICT_VIRTUAL)) + ? BTR_MODIFY_LEAF : BTR_PURGE_LEAF; pcur.btr_cur.page_cur.index = index; /* Set the purge node for the call to row_purge_poss_sec(). */ pcur.btr_cur.purge_node = node; if (index->is_spatial()) { pcur.btr_cur.thr = NULL; - if (!rtr_search(entry, BTR_MODIFY_LEAF, &pcur, &mtr)) { - goto found; - } - goto func_exit; + index->lock.u_lock(SRW_LOCK_CALL); + search_result = row_search_index_entry( + entry, mode, &pcur, &mtr); + index->lock.u_unlock(); + } else { + /* Set the query thread, so that ibuf_insert_low() will be + able to invoke thd_get_trx(). */ + pcur.btr_cur.thr = static_cast( + que_node_get_parent(node)); + search_result = row_search_index_entry( + entry, mode, &pcur, &mtr); } - /* Set the query thread, so that ibuf_insert_low() will be - able to invoke thd_get_trx(). */ - pcur.btr_cur.thr = static_cast(que_node_get_parent(node)); - - switch (row_search_index_entry(entry, index->has_virtual() - ? BTR_MODIFY_LEAF : BTR_PURGE_LEAF, - &pcur, &mtr)) { + switch (search_result) { case ROW_FOUND: -found: /* Before attempting to purge a record, check if it is safe to do so. */ if (row_purge_poss_sec(node, index, entry, &pcur, &mtr, false)) { diff --git a/storage/innobase/row/row0row.cc b/storage/innobase/row/row0row.cc index 4a00b2a430e..b998d27d836 100644 --- a/storage/innobase/row/row0row.cc +++ b/storage/innobase/row/row0row.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2018, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2018, 2023, MariaDB Corporation. +Copyright (c) 2018, 2022, 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 @@ -1216,7 +1216,7 @@ row_search_on_row_ref( & REC_INFO_MIN_REC_FLAG; } else { ut_a(ref->n_fields == index->n_uniq); - if (btr_pcur_open(ref, PAGE_CUR_LE, mode, pcur, mtr) + if (btr_pcur_open(ref, PAGE_CUR_LE, mode, pcur, 0, mtr) != DB_SUCCESS) { return false; } @@ -1278,13 +1278,21 @@ row_search_index_entry( ut_ad(dtuple_check_typed(entry)); - if (btr_pcur_open(entry, PAGE_CUR_LE, mode, pcur, mtr) != DB_SUCCESS) { - return ROW_NOT_FOUND; + if (pcur->index()->is_spatial()) { + if (rtr_pcur_open(pcur->index(), entry, mode, pcur, mtr)) { + return ROW_NOT_FOUND; + } + } else { + if (btr_pcur_open(entry, PAGE_CUR_LE, mode, pcur, 0, mtr) + != DB_SUCCESS) { + return ROW_NOT_FOUND; + } } switch (btr_pcur_get_btr_cur(pcur)->flag) { case BTR_CUR_DELETE_REF: ut_ad(!(~mode & BTR_DELETE)); + ut_ad(!pcur->index()->is_spatial()); return(ROW_NOT_DELETED_REF); case BTR_CUR_DEL_MARK_IBUF: diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index e44cc466295..87e3ca43b1c 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -2,7 +2,7 @@ Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. -Copyright (c) 2015, 2023, MariaDB Corporation. +Copyright (c) 2015, 2022, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -4776,7 +4776,7 @@ wait_table_again: pcur->btr_cur.thr = thr; pcur->old_rec = nullptr; - if (index->is_spatial()) { + if (dict_index_is_spatial(index)) { if (!prebuilt->rtr_info) { prebuilt->rtr_info = rtr_create_rtr_info( set_also_gap_locks, true, @@ -4792,14 +4792,11 @@ wait_table_again: prebuilt->rtr_info->search_tuple = search_tuple; prebuilt->rtr_info->search_mode = mode; } - - err = rtr_search_leaf(pcur, search_tuple, mode, &mtr); - } else { - err = btr_pcur_open_with_no_init(search_tuple, mode, - BTR_SEARCH_LEAF, - pcur, &mtr); } + err = btr_pcur_open_with_no_init(search_tuple, mode, + BTR_SEARCH_LEAF, pcur, &mtr); + if (err != DB_SUCCESS) { page_corrupted: rec = NULL; @@ -5774,7 +5771,8 @@ next_rec_after_check: if (spatial_search) { /* No need to do store restore for R-tree */ - mtr.rollback_to_savepoint(0); + mtr.commit(); + mtr.start(); } else if (mtr_extra_clust_savepoint) { /* We must release any clustered index latches if we are moving to the next non-clustered diff --git a/storage/innobase/row/row0uins.cc b/storage/innobase/row/row0uins.cc index 50196e78092..6567019a33d 100644 --- a/storage/innobase/row/row0uins.cc +++ b/storage/innobase/row/row0uins.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2023, MariaDB Corporation. +Copyright (c) 2017, 2022, 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 @@ -233,7 +233,7 @@ func_exit: if (err == DB_SUCCESS && node->rec_type == TRX_UNDO_INSERT_METADATA) { /* When rolling back the very first instant ADD COLUMN operation, reset the root page to the basic state. */ - btr_reset_instant(*index, true, &mtr); + err = btr_reset_instant(*index, true, &mtr); } btr_pcur_commit_specify_mtr(&node->pcur, &mtr); @@ -268,32 +268,21 @@ row_undo_ins_remove_sec_low( pcur.btr_cur.page_cur.index = index; row_mtr_start(&mtr, index, !modify_leaf); + if (modify_leaf) { + mode = BTR_MODIFY_LEAF_ALREADY_LATCHED; + mtr_s_lock_index(index, &mtr); + } else { + ut_ad(mode == BTR_PURGE_TREE); + mtr_sx_lock_index(index, &mtr); + } + if (index->is_spatial()) { mode = modify_leaf - ? btr_latch_mode(BTR_MODIFY_LEAF + ? btr_latch_mode(BTR_MODIFY_LEAF_ALREADY_LATCHED | BTR_RTREE_DELETE_MARK | BTR_RTREE_UNDO_INS) : btr_latch_mode(BTR_PURGE_TREE | BTR_RTREE_UNDO_INS); btr_pcur_get_btr_cur(&pcur)->thr = thr; - if (rtr_search(entry, mode, &pcur, &mtr)) { - goto func_exit; - } - - if (rec_get_deleted_flag( - btr_pcur_get_rec(&pcur), - dict_table_is_comp(index->table))) { - ib::error() << "Record found in index " << index->name - << " is deleted marked on insert rollback."; - ut_ad(0); - } - goto found; - } else if (modify_leaf) { - mode = BTR_MODIFY_LEAF_ALREADY_LATCHED; - mtr_s_lock_index(index, &mtr); - } else { - ut_ad(mode == BTR_PURGE_TREE); - mode = BTR_PURGE_TREE_ALREADY_LATCHED; - mtr_x_lock_index(index, &mtr); } switch (row_search_index_entry(entry, mode, &pcur, &mtr)) { @@ -306,7 +295,15 @@ row_undo_ins_remove_sec_low( case ROW_NOT_FOUND: break; case ROW_FOUND: - found: + if (dict_index_is_spatial(index) + && rec_get_deleted_flag( + btr_pcur_get_rec(&pcur), + dict_table_is_comp(index->table))) { + ib::error() << "Record found in index " << index->name + << " is deleted marked on insert rollback."; + ut_ad(0); + } + btr_cur_t* btr_cur = btr_pcur_get_btr_cur(&pcur); if (modify_leaf) { @@ -321,7 +318,6 @@ row_undo_ins_remove_sec_low( } } -func_exit: btr_pcur_close(&pcur); mtr_commit(&mtr); diff --git a/storage/innobase/row/row0umod.cc b/storage/innobase/row/row0umod.cc index 50e15e03cc9..2d04dca4003 100644 --- a/storage/innobase/row/row0umod.cc +++ b/storage/innobase/row/row0umod.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2023, MariaDB Corporation. +Copyright (c) 2017, 2022, 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 @@ -133,7 +133,8 @@ row_undo_mod_clust_low( && node->ref == &trx_undo_metadata && btr_cur_get_index(btr_cur)->table->instant && node->update->info_bits == REC_INFO_METADATA_ADD) { - btr_reset_instant(*btr_cur->index(), false, mtr); + err = btr_reset_instant(*btr_cur_get_index(btr_cur), + false, mtr); } } @@ -489,6 +490,7 @@ row_undo_mod_del_mark_or_remove_sec_low( dberr_t err = DB_SUCCESS; mtr_t mtr; mtr_t mtr_vers; + row_search_result search_result; const bool modify_leaf = mode == BTR_MODIFY_LEAF; row_mtr_start(&mtr, index, !modify_leaf); @@ -503,11 +505,6 @@ row_undo_mod_del_mark_or_remove_sec_low( | BTR_RTREE_UNDO_INS) : btr_latch_mode(BTR_PURGE_TREE | BTR_RTREE_UNDO_INS); btr_cur->thr = thr; - if (UNIV_LIKELY(!rtr_search(entry, mode, &pcur, &mtr))) { - goto found; - } else { - goto func_exit; - } } else if (!index->is_committed()) { /* The index->online_status may change if the index is or was being created online, but not committed yet. It @@ -517,8 +514,7 @@ row_undo_mod_del_mark_or_remove_sec_low( mtr_s_lock_index(index, &mtr); } else { ut_ad(mode == BTR_PURGE_TREE); - mode = BTR_PURGE_TREE_ALREADY_LATCHED; - mtr_x_lock_index(index, &mtr); + mtr_sx_lock_index(index, &mtr); } } else { /* For secondary indexes, @@ -527,8 +523,9 @@ row_undo_mod_del_mark_or_remove_sec_low( ut_ad(!dict_index_is_online_ddl(index)); } - switch (UNIV_EXPECT(row_search_index_entry(entry, mode, &pcur, &mtr), - ROW_FOUND)) { + search_result = row_search_index_entry(entry, mode, &pcur, &mtr); + + switch (UNIV_EXPECT(search_result, ROW_FOUND)) { case ROW_NOT_FOUND: /* In crash recovery, the secondary index record may be missing if the UPDATE did not have time to insert @@ -550,7 +547,6 @@ row_undo_mod_del_mark_or_remove_sec_low( ut_error; } -found: /* We should remove the index record if no prior version of the row, which cannot be purged yet, requires its existence. If some requires, we should delete mark the record. */ @@ -669,12 +665,13 @@ row_undo_mod_del_unmark_sec_and_undo_update( trx_t* trx = thr_get_trx(thr); const ulint flags = BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG; + row_search_result search_result; const auto orig_mode = mode; pcur.btr_cur.page_cur.index = index; ut_ad(trx->id != 0); - if (index->is_spatial()) { + if (dict_index_is_spatial(index)) { /* FIXME: Currently we do a 2-pass search for the undo due to avoid undel-mark a wrong rec in rolling back in partial update. Later, we could log some info in @@ -689,22 +686,9 @@ try_again: btr_cur->thr = thr; - if (index->is_spatial()) { - if (!rtr_search(entry, mode, &pcur, &mtr)) { - goto found; - } + search_result = row_search_index_entry(entry, mode, &pcur, &mtr); - if (mode != orig_mode && btr_cur->rtr_info->fd_del) { - mode = orig_mode; - btr_pcur_close(&pcur); - mtr.commit(); - goto try_again; - } - - goto not_found; - } - - switch (row_search_index_entry(entry, mode, &pcur, &mtr)) { + switch (search_result) { mem_heap_t* heap; mem_heap_t* offsets_heap; rec_offs* offsets; @@ -715,7 +699,17 @@ try_again: flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */ ut_error; case ROW_NOT_FOUND: -not_found: + /* For spatial index, if first search didn't find an + undel-marked rec, try to find a del-marked rec. */ + if (dict_index_is_spatial(index) && btr_cur->rtr_info->fd_del) { + if (mode != orig_mode) { + mode = orig_mode; + btr_pcur_close(&pcur); + mtr_commit(&mtr); + goto try_again; + } + } + if (btr_cur->up_match >= dict_index_get_n_unique(index) || btr_cur->low_match >= dict_index_get_n_unique(index)) { ib::warn() << "Record in index " << index->name @@ -773,7 +767,6 @@ not_found: break; case ROW_FOUND: -found: btr_rec_set_deleted(btr_cur_get_block(btr_cur), btr_cur_get_rec(btr_cur), &mtr); heap = mem_heap_create( diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc index fe88fce58a2..a3f940adff5 100644 --- a/storage/innobase/row/row0upd.cc +++ b/storage/innobase/row/row0upd.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2015, 2023, MariaDB Corporation. +Copyright (c) 2015, 2022, 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 @@ -1832,10 +1832,12 @@ row_upd_sec_index_entry( que_thr_t* thr) /*!< in: query thread */ { mtr_t mtr; + const rec_t* rec; btr_pcur_t pcur; mem_heap_t* heap; dtuple_t* entry; dict_index_t* index; + btr_cur_t* btr_cur; dberr_t err = DB_SUCCESS; trx_t* trx = thr_get_trx(thr); btr_latch_mode mode; @@ -1874,6 +1876,10 @@ row_upd_sec_index_entry( case SRV_TMP_SPACE_ID: mtr.set_log_mode(MTR_LOG_NO_REDO); flags = BTR_NO_LOCKING_FLAG; + if (index->is_spatial()) { + mode = btr_latch_mode(BTR_MODIFY_LEAF + | BTR_RTREE_DELETE_MARK); + } break; default: index->set_modified(mtr); @@ -1882,35 +1888,26 @@ row_upd_sec_index_entry( flags = index->table->no_rollback() ? BTR_NO_ROLLBACK : 0; /* We can only buffer delete-mark operations if there are no foreign key constraints referring to the index. */ - if (!referenced) { - mode = BTR_DELETE_MARK_LEAF; - } + mode = index->is_spatial() + ? btr_latch_mode(BTR_MODIFY_LEAF + | BTR_RTREE_DELETE_MARK) + : referenced + ? BTR_MODIFY_LEAF : BTR_DELETE_MARK_LEAF; break; } /* Set the query thread, so that ibuf_insert_low() will be able to invoke thd_get_trx(). */ - pcur.btr_cur.thr = thr; + btr_pcur_get_btr_cur(&pcur)->thr = thr; pcur.btr_cur.page_cur.index = index; - if (index->is_spatial()) { - mode = btr_latch_mode(BTR_MODIFY_LEAF | BTR_RTREE_DELETE_MARK); - if (UNIV_LIKELY(!rtr_search(entry, mode, &pcur, &mtr))) { - goto found; - } - - if (pcur.btr_cur.rtr_info->fd_del) { - /* We found the record, but a delete marked */ - goto close; - } + search_result = row_search_index_entry(entry, mode, &pcur, &mtr); - goto not_found; - } + btr_cur = btr_pcur_get_btr_cur(&pcur); - search_result = row_search_index_entry(entry, mode, &pcur, &mtr); + rec = btr_cur_get_rec(btr_cur); switch (search_result) { - const rec_t* rec; case ROW_NOT_DELETED_REF: /* should only occur for BTR_DELETE */ ut_error; break; @@ -1919,8 +1916,11 @@ row_upd_sec_index_entry( break; case ROW_NOT_FOUND: -not_found: - rec = btr_pcur_get_rec(&pcur); + if (dict_index_is_spatial(index) && btr_cur->rtr_info->fd_del) { + /* We found the record, but a delete marked */ + break; + } + ib::error() << "Record in index " << index->name << " of table " << index->table->name @@ -1934,9 +1934,7 @@ not_found: #endif /* UNIV_DEBUG */ break; case ROW_FOUND: -found: ut_ad(err == DB_SUCCESS); - rec = btr_pcur_get_rec(&pcur); /* Delete mark the old index record; it can already be delete marked if we return after a lock wait in @@ -1945,14 +1943,14 @@ found: rec, dict_table_is_comp(index->table))) { err = lock_sec_rec_modify_check_and_lock( flags, - btr_pcur_get_block(&pcur), - btr_pcur_get_rec(&pcur), index, thr, &mtr); + btr_cur_get_block(btr_cur), + btr_cur_get_rec(btr_cur), index, thr, &mtr); if (err != DB_SUCCESS) { break; } - btr_rec_set_deleted(btr_pcur_get_block(&pcur), - btr_pcur_get_rec(&pcur), + btr_rec_set_deleted(btr_cur_get_block(btr_cur), + btr_cur_get_rec(btr_cur), &mtr); #ifdef WITH_WSREP if (!referenced && foreign @@ -2011,7 +2009,6 @@ found: } } -close: btr_pcur_close(&pcur); mtr_commit(&mtr); -- cgit v1.2.1 From 8c2dcfab031f14d7d6269f160217d2068236833b Mon Sep 17 00:00:00 2001 From: Ian Gilfillan Date: Mon, 23 Jan 2023 12:54:13 +0200 Subject: Update 10.6 HELP tables --- scripts/fill_help_tables.sql | 62 ++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/scripts/fill_help_tables.sql b/scripts/fill_help_tables.sql index ff4ca0fec80..1077848eb1c 100644 --- a/scripts/fill_help_tables.sql +++ b/scripts/fill_help_tables.sql @@ -84,8 +84,8 @@ insert into help_category (help_category_id,name,parent_category_id,url) values insert into help_category (help_category_id,name,parent_category_id,url) values (49,'Replication',1,''); insert into help_category (help_category_id,name,parent_category_id,url) values (50,'Prepared Statements',1,''); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (1,9,'HELP_DATE','Help Contents generated from the MariaDB Knowledge Base on 22 October 2022.','',''); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (2,9,'HELP_VERSION','Help Contents generated for MariaDB 10.6 from the MariaDB Knowledge Base on 22 October 2022.','',''); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (1,9,'HELP_DATE','Help Contents generated from the MariaDB Knowledge Base on 23 January 2023.','',''); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (2,9,'HELP_VERSION','Help Contents generated for MariaDB 10.6 from the MariaDB Knowledge Base on 23 January 2023.','',''); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (3,2,'AREA','A synonym for ST_AREA.\n\nURL: https://mariadb.com/kb/en/polygon-properties-area/','','https://mariadb.com/kb/en/polygon-properties-area/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (4,2,'CENTROID','A synonym for ST_CENTROID.\n\nURL: https://mariadb.com/kb/en/centroid/','','https://mariadb.com/kb/en/centroid/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (5,2,'ExteriorRing','A synonym for ST_ExteriorRing.\n\nURL: https://mariadb.com/kb/en/polygon-properties-exteriorring/','','https://mariadb.com/kb/en/polygon-properties-exteriorring/'); @@ -151,7 +151,7 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (65,4,'POWER','Syntax\n------\n\nPOWER(X,Y)\n\nDescription\n-----------\n\nThis is a synonym for POW(), which returns the value of X raised to the power\nof Y.\n\nURL: https://mariadb.com/kb/en/power/','','https://mariadb.com/kb/en/power/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (66,4,'RADIANS','Syntax\n------\n\nRADIANS(X)\n\nDescription\n-----------\n\nReturns the argument X, converted from degrees to radians. Note that π radians\nequals 180 degrees.\n\nThis is the converse of the DEGREES() function.\n\nExamples\n--------\n\nSELECT RADIANS(45);\n+-------------------+\n| RADIANS(45) |\n+-------------------+\n| 0.785398163397448 |\n+-------------------+\n\nSELECT RADIANS(90);\n+-----------------+\n| RADIANS(90) |\n+-----------------+\n| 1.5707963267949 |\n+-----------------+\n\nSELECT RADIANS(PI());\n+--------------------+\n| RADIANS(PI()) |\n+--------------------+\n| 0.0548311355616075 |\n+--------------------+\n\nSELECT RADIANS(180);\n+------------------+\n| RADIANS(180) |\n+------------------+\n| 3.14159265358979 |\n+------------------+\n\nURL: https://mariadb.com/kb/en/radians/','','https://mariadb.com/kb/en/radians/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (67,4,'RAND','Syntax\n------\n\nRAND(), RAND(N)\n\nDescription\n-----------\n\nReturns a random DOUBLE precision floating point value v in the range 0 <= v <\n1.0. If a constant integer argument N is specified, it is used as the seed\nvalue, which produces a repeatable sequence of column values. In the example\nbelow, note that the sequences of values produced by RAND(3) is the same both\nplaces where it occurs.\n\nIn a WHERE clause, RAND() is evaluated each time the WHERE is executed.\n\nStatements using the RAND() function are not safe for statement-based\nreplication.\n\nPractical uses\n--------------\n\nThe expression to get a random integer from a given range is the following:\n\nFLOOR(min_value + RAND() * (max_value - min_value +1))\n\nRAND() is often used to read random rows from a table, as follows:\n\nSELECT * FROM my_table ORDER BY RAND() LIMIT 10;\n\nNote, however, that this technique should never be used on a large table as it\nwill be extremely slow. MariaDB will read all rows in the table, generate a\nrandom value for each of them, order them, and finally will apply the LIMIT\nclause.\n\nExamples\n--------\n\nCREATE TABLE t (i INT);\n\nINSERT INTO t VALUES(1),(2),(3);\n\nSELECT i, RAND() FROM t;\n+------+-------------------+\n| i | RAND() |\n+------+-------------------+\n| 1 | 0.255651095188829 |\n| 2 | 0.833920199269355 |\n| 3 | 0.40264774151393 |\n+------+-------------------+\n\nSELECT i, RAND(3) FROM t;\n+------+-------------------+\n| i | RAND(3) |\n+------+-------------------+\n| 1 | 0.90576975597606 |\n| 2 | 0.373079058130345 |\n| 3 | 0.148086053457191 |\n+------+-------------------+\n\nSELECT i, RAND() FROM t;\n+------+-------------------+\n| i | RAND() |\n+------+-------------------+\n| 1 | 0.511478140495232 |\n| 2 | 0.349447508668012 |\n| 3 | 0.212803152588013 |\n+------+-------------------+\n\nUsing the same seed, the same sequence will be returned:\n\nSELECT i, RAND(3) FROM t;\n+------+-------------------+\n| i | RAND(3) |\n+------+-------------------+\n| 1 | 0.90576975597606 |\n| 2 | 0.373079058130345 |\n| 3 | 0.148086053457191 |\n+------+-------------------+\n\nGenerating a random number from 5 to 15:\n\nSELECT FLOOR(5 + (RAND() * 11));\n\nURL: https://mariadb.com/kb/en/rand/','','https://mariadb.com/kb/en/rand/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (68,4,'ROUND','Syntax\n------\n\nROUND(X), ROUND(X,D)\n\nDescription\n-----------\n\nRounds the argument X to D decimal places. The rounding algorithm depends on\nthe data type of X. D defaults to 0 if not specified. D can be negative to\ncause D digits left of the decimal point of the value X to become zero.\n\nExamples\n--------\n\nSELECT ROUND(-1.23);\n+--------------+\n| ROUND(-1.23) |\n+--------------+\n| -1 |\n+--------------+\n\nSELECT ROUND(-1.58);\n+--------------+\n| ROUND(-1.58) |\n+--------------+\n| -2 |\n+--------------+\n\nSELECT ROUND(1.58); \n+-------------+\n| ROUND(1.58) |\n+-------------+\n| 2 |\n+-------------+\n\nSELECT ROUND(1.298, 1);\n+-----------------+\n| ROUND(1.298, 1) |\n+-----------------+\n| 1.3 |\n+-----------------+\n\nSELECT ROUND(1.298, 0);\n+-----------------+\n| ROUND(1.298, 0) |\n+-----------------+\n| 1 |\n+-----------------+\n\nSELECT ROUND(23.298, -1);\n+-------------------+\n| ROUND(23.298, -1) |\n+-------------------+\n| 20 |\n+-------------------+\n\nURL: https://mariadb.com/kb/en/round/','','https://mariadb.com/kb/en/round/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (68,4,'ROUND','Syntax\n------\n\nROUND(X), ROUND(X,D)\n\nDescription\n-----------\n\nRounds the argument X to D decimal places. D defaults to 0 if not specified. D\ncan be negative to cause D digits left of the decimal point of the value X to\nbecome zero.\n\nThe rounding algorithm depends on the data type of X:\n\n* for floating point types (FLOAT, DOUBLE) the C libraries rounding function\nis used, so the behavior *may* differ between operating systems\n* for fixed point types (DECIMAL, DEC/NUMBER/FIXED) the \"round half up\" rule\nis used, meaning that e.g. a value ending in exactly .5 is always rounded up.\n\nExamples\n--------\n\nSELECT ROUND(-1.23);\n+--------------+\n| ROUND(-1.23) |\n+--------------+\n| -1 |\n+--------------+\n\nSELECT ROUND(-1.58);\n+--------------+\n| ROUND(-1.58) |\n+--------------+\n| -2 |\n+--------------+\n\nSELECT ROUND(1.58); \n+-------------+\n| ROUND(1.58) |\n+-------------+\n| 2 |\n+-------------+\n\nSELECT ROUND(1.298, 1);\n+-----------------+\n| ROUND(1.298, 1) |\n+-----------------+\n| 1.3 |\n+-----------------+\n\nSELECT ROUND(1.298, 0);\n+-----------------+\n| ROUND(1.298, 0) |\n+-----------------+\n| 1 |\n+-----------------+\n\nSELECT ROUND(23.298, -1);\n+-------------------+\n| ROUND(23.298, -1) |\n+-------------------+\n| 20 |\n+-------------------+\n\nURL: https://mariadb.com/kb/en/round/','','https://mariadb.com/kb/en/round/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (69,4,'SIGN','Syntax\n------\n\nSIGN(X)\n\nDescription\n-----------\n\nReturns the sign of the argument as -1, 0, or 1, depending on whether X is\nnegative, zero, or positive.\n\nExamples\n--------\n\nSELECT SIGN(-32);\n+-----------+\n| SIGN(-32) |\n+-----------+\n| -1 |\n+-----------+\n\nSELECT SIGN(0);\n+---------+\n| SIGN(0) |\n+---------+\n| 0 |\n+---------+\n\nSELECT SIGN(234);\n+-----------+\n| SIGN(234) |\n+-----------+\n| 1 |\n+-----------+\n\nURL: https://mariadb.com/kb/en/sign/','','https://mariadb.com/kb/en/sign/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (70,4,'SIN','Syntax\n------\n\nSIN(X)\n\nDescription\n-----------\n\nReturns the sine of X, where X is given in radians.\n\nExamples\n--------\n\nSELECT SIN(1.5707963267948966);\n+-------------------------+\n| SIN(1.5707963267948966) |\n+-------------------------+\n| 1 |\n+-------------------------+\n\nSELECT SIN(PI());\n+----------------------+\n| SIN(PI()) |\n+----------------------+\n| 1.22460635382238e-16 |\n+----------------------+\n\nSELECT ROUND(SIN(PI()));\n+------------------+\n| ROUND(SIN(PI())) |\n+------------------+\n| 0 |\n+------------------+\n\nURL: https://mariadb.com/kb/en/sin/','','https://mariadb.com/kb/en/sin/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (71,4,'SQRT','Syntax\n------\n\nSQRT(X)\n\nDescription\n-----------\n\nReturns the square root of X. If X is negative, NULL is returned.\n\nExamples\n--------\n\nSELECT SQRT(4);\n+---------+\n| SQRT(4) |\n+---------+\n| 2 |\n+---------+\n\nSELECT SQRT(20);\n+------------------+\n| SQRT(20) |\n+------------------+\n| 4.47213595499958 |\n+------------------+\n\nSELECT SQRT(-16);\n+-----------+\n| SQRT(-16) |\n+-----------+\n| NULL |\n+-----------+\n\nSELECT SQRT(1764);\n+------------+\n| SQRT(1764) |\n+------------+\n| 42 |\n+------------+\n\nURL: https://mariadb.com/kb/en/sqrt/','','https://mariadb.com/kb/en/sqrt/'); @@ -192,11 +192,11 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, update help_topic set description = CONCAT(description, '\n| | executed by the user. See also |\n| | Aborting Statements that Exceed a |\n| | Certain Time to Execute. |\n+--------------------------------------+--------------------------------------+\n\nIf any of these limits are set to 0, then there is no limit for that resource\nfor that user.\n\nHere is an example showing how to create a user with resource limits:\n\nCREATE USER \'someone\'@\'localhost\' WITH\n MAX_USER_CONNECTIONS 10\n MAX_QUERIES_PER_HOUR 200;\n\nThe resources are tracked per account, which means \'user\'@\'server\'; not per\nuser name or per connection.\n\nThe count can be reset for all users using FLUSH USER_RESOURCES, FLUSH\nPRIVILEGES or mysqladmin reload.\n\nPer account resource limits are stored in the user table, in the mysql\ndatabase. Columns used for resources limits are named max_questions,\nmax_updates, max_connections (for MAX_CONNECTIONS_PER_HOUR), and\nmax_user_connections (for MAX_USER_CONNECTIONS).\n\nAccount Names\n-------------\n\nAccount names have both a user name component and a host name component, and\nare specified as \'user_name\'@\'host_name\'.\n\nThe user name and host name may be unquoted, quoted as strings using double\nquotes (\") or single quotes (\'), or quoted as identifiers using backticks (`).\nYou must use quotes when using special characters (such as a hyphen) or\nwildcard characters. If you quote, you must quote the user name and host name\nseparately (for example \'user_name\'@\'host_name\').\n\nHost Name Component\n-------------------\n\nIf the host name is not provided, it is assumed to be \'%\'.\n\nHost names may contain the wildcard characters % and _. They are matched as if\nby the LIKE clause. If you need to use a wildcard character literally (for\nexample, to match a domain name with an underscore), prefix the character with\na backslash. See LIKE for more information on escaping wildcard characters.\n\nHost name matches are case-insensitive. Host names can match either domain\nnames or IP addresses. Use \'localhost\' as the host name to allow only local\nclient connections.\n\nYou can use a netmask to match a range of IP addresses using \'base_ip/netmask\'\nas the host name. A user with an IP address ip_addr will be allowed to connect\nif the following condition is true:\n\nip_addr & netmask = base_ip\n\nFor example, given a user:\n\nCREATE USER \'maria\'@\'247.150.130.0/255.255.255.0\';\n\nthe IP addresses satisfying this condition range from 247.150.130.0 to\n247.150.130.255.\n\nUsing 255.255.255.255 is equivalent to not using a netmask at all. Netmasks\ncannot be used for IPv6 addresses.\n\nNote that the credentials added when creating a user with the \'%\' wildcard\nhost will not grant access in all cases. For example, some systems come with\nan anonymous localhost user, and when connecting from localhost this will take\nprecedence.\n\nBefore MariaDB 10.6, the host name component could be up to 60 characters in\nlength. Starting from MariaDB 10.6, it can be up to 255 characters.\n\nUser Name Component\n-------------------\n\nUser names must match exactly, including case. A user name that is empty is\nknown as an anonymous account and is allowed to match a login attempt with any\nuser name component. These are described more in the next section.\n\nFor valid identifiers to use as user names, see Identifier Names.\n\nIt is possible for more than one account to match when a user connects.\nMariaDB selects the first matching account after sorting according to the\nfollowing criteria:\n\n* Accounts with an exact host name are sorted before accounts using a wildcard\nin the\nhost name. Host names using a netmask are considered to be exact for sorting.\n* Accounts with a wildcard in the host name are sorted according to the\nposition of\nthe first wildcard character. Those with a wildcard character later in the\nhost name\nsort before those with a wildcard character earlier in the host name.\n* Accounts with a non-empty user name sort before accounts with an empty user\nname.\n* Accounts with an empty user name are sorted last. As mentioned previously,\nthese are known as anonymous accounts. These are described more in the next\nsection.\n\nThe following table shows a list of example account as sorted by these\ncriteria:\n\n+---------+-------------+\n| User | Host |\n+---------+-------------+\n| joffrey | 192.168.0.3 |\n| | 192.168.0.% |\n| joffrey | 192.168.% |\n| | 192.168.% |\n+---------+-------------+\n\nOnce connected, you only have the privileges granted to the account that\nmatched, not all accounts that could have matched. For example, consider the\nfollowing commands:\n\nCREATE USER \'joffrey\'@\'192.168.0.3\';\nCREATE USER \'joffrey\'@\'%\';\nGRANT SELECT ON test.t1 to \'joffrey\'@\'192.168.0.3\';\nGRANT SELECT ON test.t2 to \'joffrey\'@\'%\';\n\nIf you connect as joffrey from 192.168.0.3, you will have the SELECT privilege\non the table test.t1, but not on the table test.t2. If you connect as joffrey\nfrom any other IP address, you will have the SELECT privilege on the table\ntest.t2, but not on the table test.t1.\n\nUsernames can be up to 80 characters long before 10.6 and starting from 10.6\nit can be 128 characters long.\n\nAnonymous Accounts\n------------------\n\nAnonymous accounts are accounts where the user name portion of the account\nname is empty. These accounts act as special catch-all accounts. If a user\nattempts to log into the system from a host, and an anonymous account exists\nwith a host name portion that matches the user\'s host, then the user will log\nin as the anonymous account if there is no more specific account match for the\nuser name that the user entered.\n\nFor example, here are some anonymous accounts:\n\nCREATE USER \'\'@\'localhost\';\nCREATE USER \'\'@\'192.168.0.3\';\n\nFixing a Legacy Default Anonymous Account\n-----------------------------------------\n\nOn some systems, the mysql.db table has some entries for the \'\'@\'%\' anonymous\naccount by default. Unfortunately, there is no matching entry in the\nmysql.user/mysql.global_priv_table table, which means that this anonymous\naccount doesn\'t exactly exist, but it does have privileges--usually on the\ndefault test database created by mysql_install_db. These account-less\nprivileges are a legacy that is leftover from a time when MySQL\'s privilege\nsystem was less advanced.\n\nThis situation means that you will run into errors if you try to create a\n\'\'@\'%\' account. For example:\n\nCREATE USER \'\'@\'%\';\nERROR 1396 (HY000): Operation CREATE USER failed for \'\'@\'%\'\n\nThe fix is to DELETE the row in the mysql.db table and then execute FLUSH\nPRIVILEGES:\n\nDELETE FROM mysql.db WHERE User=\'\' AND Host=\'%\';\nFLUSH PRIVILEGES;\n\nAnd then the account can be created:\n\nCREATE USER \'\'@\'%\';\nQuery OK, 0 rows affected (0.01 sec)\n\nSee MDEV-13486 for more information.\n\nPassword Expiry\n---------------\n\nMariaDB starting with 10.4.3\n----------------------------\nBesides automatic password expiry, as determined by default_password_lifetime,\npassword expiry times can be set on an individual user basis, overriding the\nglobal setting, for example:\n\nCREATE USER \'monty\'@\'localhost\' PASSWORD EXPIRE INTERVAL 120 DAY;\n\nSee User Password Expiry for more details.\n\nAccount Locking\n---------------\n\nMariaDB starting with 10.4.2\n----------------------------\nAccount locking permits privileged administrators to lock/unlock user\naccounts. No new client connections will be permitted if an account is locked\n(existing connections are not affected). For example:\n\nCREATE USER \'marijn\'@\'localhost\' ACCOUNT LOCK;\n\nSee Account Locking for more details.\n\nFrom MariaDB 10.4.7 and MariaDB 10.5.8, the lock_option and password_option\nclauses can occur in either order.\n\nURL: https://mariadb.com/kb/en/create-user/') WHERE help_topic_id = 104; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (105,10,'ALTER USER','Syntax\n------\n\nALTER USER [IF EXISTS] \n user_specification [,user_specification] ...\n [REQUIRE {NONE | tls_option [[AND] tls_option] ...}]\n [WITH resource_option [resource_option] ...]\n [lock_option] [password_option]\n\nuser_specification:\n username [authentication_option]\n\nauthentication_option:\n IDENTIFIED BY \'password\'\n | IDENTIFIED BY PASSWORD \'password_hash\'\n | IDENTIFIED {VIA|WITH} authentication_rule [OR authentication_rule] ...\n\nauthentication_rule:\n authentication_plugin\n | authentication_plugin {USING|AS} \'authentication_string\'\n | authentication_plugin {USING|AS} PASSWORD(\'password\')\n\ntls_option\n SSL\n | X509\n | CIPHER \'cipher\'\n | ISSUER \'issuer\'\n | SUBJECT \'subject\'\n\nresource_option\n MAX_QUERIES_PER_HOUR count\n | MAX_UPDATES_PER_HOUR count\n | MAX_CONNECTIONS_PER_HOUR count\n | MAX_USER_CONNECTIONS count\n | MAX_STATEMENT_TIME time\n\npassword_option:\n PASSWORD EXPIRE\n | PASSWORD EXPIRE DEFAULT\n | PASSWORD EXPIRE NEVER\n | PASSWORD EXPIRE INTERVAL N DAY\n\nlock_option:\n ACCOUNT LOCK\n | ACCOUNT UNLOCK\n}\n\nDescription\n-----------\n\nThe ALTER USER statement modifies existing MariaDB accounts. To use it, you\nmust have the global CREATE USER privilege or the UPDATE privilege for the\nmysql database. The global SUPER privilege is also required if the read_only\nsystem variable is enabled.\n\nIf any of the specified user accounts do not yet exist, an error results. If\nan error occurs, ALTER USER will still modify the accounts that do not result\nin an error. Only one error is produced for all users which have not been\nmodified.\n\nIF EXISTS\n---------\n\nWhen the IF EXISTS clause is used, MariaDB will return a warning instead of an\nerror for each specified user that does not exist.\n\nAccount Names\n-------------\n\nFor ALTER USER statements, account names are specified as the username\nargument in the same way as they are for CREATE USER statements. See account\nnames from the CREATE USER page for details on how account names are specified.\n\nCURRENT_USER or CURRENT_USER() can also be used to alter the account logged\ninto the current session. For example, to change the current user\'s password\nto mariadb:\n\nALTER USER CURRENT_USER() IDENTIFIED BY \'mariadb\';\n\nAuthentication Options\n----------------------\n\nMariaDB starting with 10.4\n--------------------------\nFrom MariaDB 10.4, it is possible to use more than one authentication plugin\nfor each user account. For example, this can be useful to slowly migrate users\nto the more secure ed25519 authentication plugin over time, while allowing the\nold mysql_native_password authentication plugin as an alternative for the\ntransitional period. See Authentication from MariaDB 10.4 for more.\n\nWhen running ALTER USER, not specifying an authentication option in the\nIDENTIFIED VIA clause will remove that authentication method. (However this\nwas not the case before MariaDB 10.4.13, see MDEV-21928)\n\nFor example, a user is created with the ability to authenticate via both a\npassword and unix_socket:\n\nCREATE USER \'bob\'@\'localhost\' \n IDENTIFIED VIA mysql_native_password USING PASSWORD(\'pwd\')\n OR unix_socket;\n\nSHOW CREATE USER \'bob\'@\'localhost\'\\G\n*************************** 1. row ***************************\nCREATE USER for bob@localhost: CREATE USER `bob`@`localhost` \n IDENTIFIED VIA mysql_native_password\n USING \'*975B2CD4FF9AE554FE8AD33168FBFC326D2021DD\'\n OR unix_socket\n\nIf the user\'s password is updated, but unix_socket authentication is not\nspecified in the IDENTIFIED VIA clause, unix_socket authentication will no\nlonger be permitted.\n\nALTER USER \'bob\'@\'localhost\' IDENTIFIED VIA mysql_native_password \n USING PASSWORD(\'pwd2\');\n\nSHOW CREATE USER \'bob\'@\'localhost\'\\G\n*************************** 1. row ***************************\nCREATE USER for bob@localhost: CREATE USER `bob`@`localhost` \n IDENTIFIED BY PASSWORD \'*38366FDA01695B6A5A9DD4E428D9FB8F7EB75512\'\n\nIDENTIFIED BY \'password\'\n------------------------\n\nThe optional IDENTIFIED BY clause can be used to provide an account with a\npassword. The password should be specified in plain text. It will be hashed by\nthe PASSWORD function prior to being stored to the mysql.user table.\n\nFor example, if our password is mariadb, then we can set the account\'s\npassword with:\n\nALTER USER foo2@test IDENTIFIED BY \'mariadb\';\n\nIf you do not specify a password with the IDENTIFIED BY clause, the user will\nbe able to connect without a password. A blank password is not a wildcard to\nmatch any password. The user must connect without providing a password if no\npassword is set.\n\nThe only authentication plugins that this clause supports are\nmysql_native_password and mysql_old_password.\n\nIDENTIFIED BY PASSWORD \'password_hash\'\n--------------------------------------\n\nThe optional IDENTIFIED BY PASSWORD clause can be used to provide an account\nwith a password that has already been hashed. The password should be specified\nas a hash that was provided by the PASSWORD#function. It will be stored to the\nmysql.user table as-is.\n\nFor example, if our password is mariadb, then we can find the hash with:\n\nSELECT PASSWORD(\'mariadb\');\n+-------------------------------------------+\n| PASSWORD(\'mariadb\') |\n+-------------------------------------------+\n| *54958E764CE10E50764C2EECBB71D01F08549980 |\n+-------------------------------------------+\n\nAnd then we can set an account\'s password with the hash:\n\nALTER USER foo2@test \n IDENTIFIED BY PASSWORD \'*54958E764CE10E50764C2EECBB71D01F08549980\';\n\nIf you do not specify a password with the IDENTIFIED BY clause, the user will\nbe able to connect without a password. A blank password is not a wildcard to\nmatch any password. The user must connect without providing a password if no\npassword is set.\n\nThe only authentication plugins that this clause supports are\nmysql_native_password and mysql_old_password.\n\nIDENTIFIED {VIA|WITH} authentication_plugin\n-------------------------------------------\n\nThe optional IDENTIFIED VIA authentication_plugin allows you to specify that\nthe account should be authenticated by a specific authentication plugin. The\nplugin name must be an active authentication plugin as per SHOW PLUGINS. If it\ndoesn\'t show up in that output, then you will need to install it with INSTALL\nPLUGIN or INSTALL SONAME.\n\nFor example, this could be used with the PAM authentication plugin:\n\nALTER USER foo2@test IDENTIFIED VIA pam;\n\nSome authentication plugins allow additional arguments to be specified after a\nUSING or AS keyword. For example, the PAM authentication plugin accepts a\nservice name:\n\nALTER USER foo2@test IDENTIFIED VIA pam USING \'mariadb\';\n\nThe exact meaning of the additional argument would depend on the specific\nauthentication plugin.\n\nIn MariaDB 10.4 and later, the USING or AS keyword can also be used to provide\na plain-text password to a plugin if it\'s provided as an argument to the\nPASSWORD() function. This is only valid for authentication plugins that have\nimplemented a hook for the PASSWORD() function. For example, the ed25519\nauthentication plugin supports this:\n\nALTER USER safe@\'%\' IDENTIFIED VIA ed25519 USING PASSWORD(\'secret\');\n\nTLS Options\n-----------\n\nBy default, MariaDB transmits data between the server and clients without\nencrypting it. This is generally acceptable when the server and client run on\nthe same host or in networks where security is guaranteed through other means.\nHowever, in cases where the server and client exist on separate networks or\nthey are in a high-risk network, the lack of encryption does introduce\nsecurity concerns as a malicious actor could potentially eavesdrop on the\ntraffic as it is sent over the network between them.\n\nTo mitigate this concern, MariaDB allows you to encrypt data in transit\nbetween the server and clients using the Transport Layer Security (TLS)\nprotocol. TLS was formerly known as Secure Socket Layer (SSL), but strictly\nspeaking the SSL protocol is a predecessor to TLS and, that version of the\nprotocol is now considered insecure. The documentation still uses the term SSL\noften and for compatibility reasons TLS-related server system and status\nvariables still use the prefix ssl_, but internally, MariaDB only supports its\nsecure successors.\n\nSee Secure Connections Overview for more information about how to determine\nwhether your MariaDB server has TLS support.\n\nYou can set certain TLS-related restrictions for specific user accounts. For\ninstance, you might use this with user accounts that require access to\nsensitive data while sending it across networks that you do not control. These\nrestrictions can be enabled for a user account with the CREATE USER, ALTER\nUSER, or GRANT statements. The following options are available:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| REQUIRE NONE | TLS is not required for this account, but can |\n| | still be used. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SSL | The account must use TLS, but no valid X509 |\n| | certificate is required. This option cannot |\n| | be combined with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE X509 | The account must use TLS and must have a |\n| | valid X509 certificate. This option implies |\n| | REQUIRE SSL. This option cannot be combined |\n| | with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE ISSUER \'issuer\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the Certificate |\n| | Authority must be the one specified via the |\n| | string issuer. This option implies REQUIRE |\n| | X509. This option can be combined with the |\n| | SUBJECT, and CIPHER options in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SUBJECT \'subject\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the |\n| | certificate\'s Subject must be the one |\n| | specified via the string subject. This option |\n| | implies REQUIRE X509. This option can be |\n| | combined with the ISSUER, and CIPHER options |\n| | in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE CIPHER \'cipher\' | The account must use TLS, but no valid X509 |\n| | certificate is required. Also, the encryption |\n| | used for the connection must use a specific |\n| | cipher method specified in the string cipher. |\n| | This option implies REQUIRE SSL. This option |\n| | can be combined with the ISSUER, and SUBJECT |\n| | options in any order. |\n+---------------------------+------------------------------------------------+\n\nThe REQUIRE keyword must be used only once for all specified options, and the\nAND keyword can be used to separate individual options, but it is not required.\n\nFor example, you can alter a user account to require these TLS options with\nthe following:\n\nALTER USER \'alice\'@\'%\'\n REQUIRE SUBJECT \'/CN=alice/O=My Dom, Inc./C=US/ST=Oregon/L=Portland\' AND\n ISSUER \'/C=FI/ST=Somewhere/L=City/ O=Some Company/CN=Peter\nParker/emailAddress=p.parker@marvel.com\'\n AND CIPHER \'SHA-DES-CBC3-EDH-RSA\';\n\nIf any of these options are set for a specific user account, then any client\nwho tries to connect with that user account will have to be configured to\nconnect with TLS.\n\nSee Securing Connections for Client and Server for information on how to\nenable TLS on the client and server.\n\nResource Limit Options\n----------------------\n\nIt is possible to set per-account limits for certain server resources. The\nfollowing table shows the values that can be set per account:\n\n+------------------------------------+---------------------------------------+\n| Limit Type | Description |\n+------------------------------------+---------------------------------------+\n| MAX_QUERIES_PER_HOUR | Number of statements that the |\n| | account can issue per hour |\n| | (including updates) |\n+------------------------------------+---------------------------------------+\n| MAX_UPDATES_PER_HOUR | Number of updates (not queries) that |\n| | the account can issue per hour |\n+------------------------------------+---------------------------------------+\n| MAX_CONNECTIONS_PER_HOUR | Number of connections that the |\n| | account can start per hour |\n+------------------------------------+---------------------------------------+\n| MAX_USER_CONNECTIONS | Number of simultaneous connections |\n| | that can be accepted from the same |\n| | account; if it is 0, max_connections |\n| | will be used instead; if |\n| | max_connections is 0, there is no |\n| | limit for this account\'s |\n| | simultaneous connections. |','','https://mariadb.com/kb/en/alter-user/'); update help_topic set description = CONCAT(description, '\n+------------------------------------+---------------------------------------+\n| MAX_STATEMENT_TIME | Timeout, in seconds, for statements |\n| | executed by the user. See also |\n| | Aborting Statements that Exceed a |\n| | Certain Time to Execute. |\n+------------------------------------+---------------------------------------+\n\nIf any of these limits are set to 0, then there is no limit for that resource\nfor that user.\n\nHere is an example showing how to set an account\'s resource limits:\n\nALTER USER \'someone\'@\'localhost\' WITH\n MAX_USER_CONNECTIONS 10\n MAX_QUERIES_PER_HOUR 200;\n\nThe resources are tracked per account, which means \'user\'@\'server\'; not per\nuser name or per connection.\n\nThe count can be reset for all users using FLUSH USER_RESOURCES, FLUSH\nPRIVILEGES or mysqladmin reload.\n\nPer account resource limits are stored in the user table, in the mysql\ndatabase. Columns used for resources limits are named max_questions,\nmax_updates, max_connections (for MAX_CONNECTIONS_PER_HOUR), and\nmax_user_connections (for MAX_USER_CONNECTIONS).\n\nPassword Expiry\n---------------\n\nMariaDB starting with 10.4.3\n----------------------------\nBesides automatic password expiry, as determined by default_password_lifetime,\npassword expiry times can be set on an individual user basis, overriding the\nglobal setting, for example:\n\nALTER USER \'monty\'@\'localhost\' PASSWORD EXPIRE INTERVAL 120 DAY;\nALTER USER \'monty\'@\'localhost\' PASSWORD EXPIRE NEVER;\nALTER USER \'monty\'@\'localhost\' PASSWORD EXPIRE DEFAULT;\n\nSee User Password Expiry for more details.\n\nAccount Locking\n---------------\n\nMariaDB starting with 10.4.2\n----------------------------\nAccount locking permits privileged administrators to lock/unlock user\naccounts. No new client connections will be permitted if an account is locked\n(existing connections are not affected). For example:\n\nALTER USER \'marijn\'@\'localhost\' ACCOUNT LOCK;\n\nSee Account Locking for more details.\n\nFrom MariaDB 10.4.7 and MariaDB 10.5.8, the lock_option and password_option\nclauses can occur in either order.\n\nURL: https://mariadb.com/kb/en/alter-user/') WHERE help_topic_id = 105; -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (106,10,'DROP USER','Syntax\n------\n\nDROP USER [IF EXISTS] user_name [, user_name] ...\n\nDescription\n-----------\n\nThe DROP USER statement removes one or more MariaDB accounts. It removes\nprivilege rows for the account from all grant tables. To use this statement,\nyou must have the global CREATE USER privilege or the DELETE privilege for the\nmysql database. Each account is named using the same format as for the CREATE\nUSER statement; for example, \'jeffrey\'@\'localhost\'. If you specify only the\nuser name part of the account name, a host name part of \'%\' is used. For\nadditional information about specifying account names, see CREATE USER.\n\nNote that, if you specify an account that is currently connected, it will not\nbe deleted until the connection is closed. The connection will not be\nautomatically closed.\n\nIf any of the specified user accounts do not exist, ERROR 1396 (HY000)\nresults. If an error occurs, DROP USER will still drop the accounts that do\nnot result in an error. Only one error is produced for all users which have\nnot been dropped:\n\nERROR 1396 (HY000): Operation DROP USER failed for \'u1\'@\'%\',\'u2\'@\'%\'\n\nFailed CREATE or DROP operations, for both users and roles, produce the same\nerror code.\n\nIF EXISTS\n---------\n\nIf the IF EXISTS clause is used, MariaDB will return a note instead of an\nerror if the user does not exist.\n\nExamples\n--------\n\nDROP USER bob;\n\nIF EXISTS:\n\nDROP USER bob;\nERROR 1396 (HY000): Operation DROP USER failed for \'bob\'@\'%\'\n\nDROP USER IF EXISTS bob;\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+---------------------------------------------+\n| Level | Code | Message |\n+-------+------+---------------------------------------------+\n| Note | 1974 | Can\'t drop user \'bob\'@\'%\'; it doesn\'t exist |\n+-------+------+---------------------------------------------+\n\nURL: https://mariadb.com/kb/en/drop-user/','','https://mariadb.com/kb/en/drop-user/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (106,10,'DROP USER','Syntax\n------\n\nDROP USER [IF EXISTS] user_name [, user_name] ...\n\nDescription\n-----------\n\nThe DROP USER statement removes one or more MariaDB accounts. It removes\nprivilege rows for the account from all grant tables. To use this statement,\nyou must have the global CREATE USER privilege or the DELETE privilege for the\nmysql database. Each account is named using the same format as for the CREATE\nUSER statement; for example, \'jeffrey\'@\'localhost\'. If you specify only the\nuser name part of the account name, a host name part of \'%\' is used. For\nadditional information about specifying account names, see CREATE USER.\n\nNote that, if you specify an account that is currently connected, it will not\nbe deleted until the connection is closed. The connection will not be\nautomatically closed.\n\nIf any of the specified user accounts do not exist, ERROR 1396 (HY000)\nresults. If an error occurs, DROP USER will still drop the accounts that do\nnot result in an error. Only one error is produced for all users which have\nnot been dropped:\n\nERROR 1396 (HY000): Operation DROP USER failed for \'u1\'@\'%\',\'u2\'@\'%\'\n\nFailed CREATE or DROP operations, for both users and roles, produce the same\nerror code.\n\nIF EXISTS\n---------\n\nIf the IF EXISTS clause is used, MariaDB will return a note instead of an\nerror if the user does not exist.\n\nExamples\n--------\n\nDROP USER bob;\n\nDROP USER foo2@localhost,foo2@\'127.%\';\n\nIF EXISTS:\n\nDROP USER bob;\nERROR 1396 (HY000): Operation DROP USER failed for \'bob\'@\'%\'\n\nDROP USER IF EXISTS bob;\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+---------------------------------------------+\n| Level | Code | Message |\n+-------+------+---------------------------------------------+\n| Note | 1974 | Can\'t drop user \'bob\'@\'%\'; it doesn\'t exist |\n+-------+------+---------------------------------------------+\n\nURL: https://mariadb.com/kb/en/drop-user/','','https://mariadb.com/kb/en/drop-user/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (107,10,'GRANT','Syntax\n------\n\nGRANT\n priv_type [(column_list)]\n [, priv_type [(column_list)]] ...\n ON [object_type] priv_level\n TO user_specification [ user_options ...]\n\nuser_specification:\n username [authentication_option]\n | PUBLIC\nauthentication_option:\n IDENTIFIED BY \'password\'\n | IDENTIFIED BY PASSWORD \'password_hash\'\n | IDENTIFIED {VIA|WITH} authentication_rule [OR authentication_rule ...]\n\nauthentication_rule:\n authentication_plugin\n | authentication_plugin {USING|AS} \'authentication_string\'\n | authentication_plugin {USING|AS} PASSWORD(\'password\')\n\nGRANT PROXY ON username\n TO user_specification [, user_specification ...]\n [WITH GRANT OPTION]\n\nGRANT rolename TO grantee [, grantee ...]\n [WITH ADMIN OPTION]\n\ngrantee:\n rolename\n username [authentication_option]\n\nuser_options:\n [REQUIRE {NONE | tls_option [[AND] tls_option] ...}]\n [WITH with_option [with_option] ...]\n\nobject_type:\n TABLE\n | FUNCTION\n | PROCEDURE\n | PACKAGE\n\npriv_level:\n *\n | *.*\n | db_name.*\n | db_name.tbl_name\n | tbl_name\n | db_name.routine_name\n\nwith_option:\n GRANT OPTION\n | resource_option\n\nresource_option:\n MAX_QUERIES_PER_HOUR count\n | MAX_UPDATES_PER_HOUR count\n | MAX_CONNECTIONS_PER_HOUR count\n | MAX_USER_CONNECTIONS count\n | MAX_STATEMENT_TIME time\n\ntls_option:\n SSL\n | X509\n | CIPHER \'cipher\'\n | ISSUER \'issuer\'\n | SUBJECT \'subject\'\n\nDescription\n-----------\n\nThe GRANT statement allows you to grant privileges or roles to accounts. To\nuse GRANT, you must have the GRANT OPTION privilege, and you must have the\nprivileges that you are granting.\n\nUse the REVOKE statement to revoke privileges granted with the GRANT statement.\n\nUse the SHOW GRANTS statement to determine what privileges an account has.\n\nAccount Names\n-------------\n\nFor GRANT statements, account names are specified as the username argument in\nthe same way as they are for CREATE USER statements. See account names from\nthe CREATE USER page for details on how account names are specified.\n\nImplicit Account Creation\n-------------------------\n\nThe GRANT statement also allows you to implicitly create accounts in some\ncases.\n\nIf the account does not yet exist, then GRANT can implicitly create it. To\nimplicitly create an account with GRANT, a user is required to have the same\nprivileges that would be required to explicitly create the account with the\nCREATE USER statement.\n\nIf the NO_AUTO_CREATE_USER SQL_MODE is set, then accounts can only be created\nif authentication information is specified, or with a CREATE USER statement.\nIf no authentication information is provided, GRANT will produce an error when\nthe specified account does not exist, for example:\n\nshow variables like \'%sql_mode%\' ;\n+---------------+--------------------------------------------+\n| Variable_name | Value |\n+---------------+--------------------------------------------+\n| sql_mode | NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |\n+---------------+--------------------------------------------+\n\nGRANT USAGE ON *.* TO \'user123\'@\'%\' IDENTIFIED BY \'\';\nERROR 1133 (28000): Can\'t find any matching row in the user table\n\nGRANT USAGE ON *.* TO \'user123\'@\'%\' \n IDENTIFIED VIA PAM using \'mariadb\' require ssl ;\nQuery OK, 0 rows affected (0.00 sec)\n\nselect host, user from mysql.user where user=\'user123\' ;\n\n+------+----------+\n| host | user |\n+------+----------+\n| % | user123 |\n+------+----------+\n\nPrivilege Levels\n----------------\n\nPrivileges can be set globally, for an entire database, for a table or\nroutine, or for individual columns in a table. Certain privileges can only be\nset at certain levels.\n\n* Global privileges priv_type are granted using *.* for\npriv_level. Global privileges include privileges to administer the database\nand manage user accounts, as well as privileges for all tables, functions, and\nprocedures. Global privileges are stored in the mysql.user table prior to\nMariaDB 10.4, and in mysql.global_priv table afterwards.\n* Database privileges priv_type are granted using db_name.*\nfor priv_level, or using just * to use default database. Database\nprivileges include privileges to create tables and functions, as well as\nprivileges for all tables, functions, and procedures in the database. Database\nprivileges are stored in the mysql.db table.\n* Table privileges priv_type are granted using db_name.tbl_name\nfor priv_level, or using just tbl_name to specify a table in the default\ndatabase. The TABLE keyword is optional. Table privileges include the\nability to select and change data in the table. Certain table privileges can\nbe granted for individual columns.\n* Column privileges priv_type are granted by specifying a table for\npriv_level and providing a column list after the privilege type. They allow\nyou to control exactly which columns in a table users can select and change.\n* Function privileges priv_type are granted using FUNCTION db_name.routine_name\nfor priv_level, or using just FUNCTION routine_name to specify a function\nin the default database.\n* Procedure privileges priv_type are granted using PROCEDURE\ndb_name.routine_name\nfor priv_level, or using just PROCEDURE routine_name to specify a procedure\nin the default database.\n\nThe USAGE Privilege\n-------------------\n\nThe USAGE privilege grants no real privileges. The SHOW GRANTS statement will\nshow a global USAGE privilege for a newly-created user. You can use USAGE with\nthe GRANT statement to change options like GRANT OPTION and\nMAX_USER_CONNECTIONS without changing any account privileges.\n\nThe ALL PRIVILEGES Privilege\n----------------------------\n\nThe ALL PRIVILEGES privilege grants all available privileges. Granting all\nprivileges only affects the given privilege level. For example, granting all\nprivileges on a table does not grant any privileges on the database or\nglobally.\n\nUsing ALL PRIVILEGES does not grant the special GRANT OPTION privilege.\n\nYou can use ALL instead of ALL PRIVILEGES.\n\nThe GRANT OPTION Privilege\n--------------------------\n\nUse the WITH GRANT OPTION clause to give users the ability to grant privileges\nto other users at the given privilege level. Users with the GRANT OPTION\nprivilege can only grant privileges they have. They cannot grant privileges at\na higher privilege level than they have the GRANT OPTION privilege.\n\nThe GRANT OPTION privilege cannot be set for individual columns. If you use\nWITH GRANT OPTION when specifying column privileges, the GRANT OPTION\nprivilege will be granted for the entire table.\n\nUsing the WITH GRANT OPTION clause is equivalent to listing GRANT OPTION as a\nprivilege.\n\nGlobal Privileges\n-----------------\n\nThe following table lists the privileges that can be granted globally. You can\nalso grant all database, table, and function privileges globally. When granted\nglobally, these privileges apply to all databases, tables, or functions,\nincluding those created later.\n\nTo set a global privilege, use *.* for priv_level.\n\nBINLOG ADMIN\n------------\n\nEnables administration of the binary log, including the PURGE BINARY LOGS\nstatement and setting the system variables:\n\n* binlog_annotate_row_events\n* binlog_cache_size\n* binlog_commit_wait_count\n* binlog_commit_wait_usec\n* binlog_direct_non_transactional_updates\n* binlog_expire_logs_seconds\n* binlog_file_cache_size\n* binlog_format\n* binlog_row_image\n* binlog_row_metadata\n* binlog_stmt_cache_size\n* expire_logs_days\n* log_bin_compress\n* log_bin_compress_min_len\n* log_bin_trust_function_creators\n* max_binlog_cache_size\n* max_binlog_size\n* max_binlog_stmt_cache_size\n* sql_log_bin and\n* sync_binlog.\n\nAdded in MariaDB 10.5.2.\n\nBINLOG MONITOR\n--------------\n\nNew name for REPLICATION CLIENT from MariaDB 10.5.2, (REPLICATION CLIENT still\nsupported as an alias for compatibility purposes). Permits running SHOW\ncommands related to the binary log, in particular the SHOW BINLOG STATUS and\nSHOW BINARY LOGS statements. Unlike REPLICATION CLIENT prior to MariaDB 10.5,\nSHOW REPLICA STATUS isn\'t included in this privilege, and REPLICA MONITOR is\nrequired.\n\nBINLOG REPLAY\n-------------\n\nEnables replaying the binary log with the BINLOG statement (generated by\nmariadb-binlog), executing SET timestamp when secure_timestamp is set to\nreplication, and setting the session values of system variables usually\nincluded in BINLOG output, in particular:\n\n* gtid_domain_id\n* gtid_seq_no\n* pseudo_thread_id\n* server_id.\n\nAdded in MariaDB 10.5.2\n\nCONNECTION ADMIN\n----------------\n\nEnables administering connection resource limit options. This includes\nignoring the limits specified by:\n\n* max_connections\n* max_user_connections and\n* max_password_errors.\n\nThe statements specified in init_connect are not executed, killing connections\nand queries owned by other users is permitted. The following\nconnection-related system variables can be changed:\n\n* connect_timeout\n* disconnect_on_expired_password\n* extra_max_connections\n* init_connect\n* max_connections\n* max_connect_errors\n* max_password_errors\n* proxy_protocol_networks\n* secure_auth\n* slow_launch_time\n* thread_pool_exact_stats\n* thread_pool_dedicated_listener\n* thread_pool_idle_timeout\n* thread_pool_max_threads\n* thread_pool_min_threads\n* thread_pool_oversubscribe\n* thread_pool_prio_kickup_timer\n* thread_pool_priority\n* thread_pool_size, and\n* thread_pool_stall_limit.\n\nAdded in MariaDB 10.5.2.\n\nCREATE USER\n-----------\n\nCreate a user using the CREATE USER statement, or implicitly create a user\nwith the GRANT statement.\n\nFEDERATED ADMIN\n---------------\n\nExecute CREATE SERVER, ALTER SERVER, and DROP SERVER statements. Added in\nMariaDB 10.5.2.\n\nFILE\n----\n\nRead and write files on the server, using statements like LOAD DATA INFILE or\nfunctions like LOAD_FILE(). Also needed to create CONNECT outward tables.\nMariaDB server must have the permissions to access those files.\n\nGRANT OPTION\n------------\n\nGrant global privileges. You can only grant privileges that you have.\n\nPROCESS\n-------\n\nShow information about the active processes, for example via SHOW PROCESSLIST\nor mysqladmin processlist. If you have the PROCESS privilege, you can see all\nthreads. Otherwise, you can see only your own threads (that is, threads\nassociated with the MariaDB account that you are using).\n\nREAD_ONLY ADMIN\n---------------\n\nUser can set the read_only system variable and allows the user to perform\nwrite operations, even when the read_only option is active. Added in MariaDB\n10.5.2.\n\nFrom MariaDB 10.11.0, the READ_ONLY ADMIN privilege has been removed from\nSUPER. The benefit of this is that one can remove the READ_ONLY ADMIN\nprivilege from all users and ensure that no one can make any changes on any\nnon-temporary tables. This is useful on replicas when one wants to ensure that\nthe replica is kept identical to the primary.\n\nRELOAD\n------\n\nExecute FLUSH statements or equivalent mariadb-admin/mysqladmin commands.\n\nREPLICATION CLIENT\n------------------\n\nExecute SHOW MASTER STATUS and SHOW BINARY LOGS informative statements.\nRenamed to BINLOG MONITOR in MariaDB 10.5.2 (but still supported as an alias\nfor compatibility reasons). SHOW SLAVE STATUS was part of REPLICATION CLIENT\nprior to MariaDB 10.5.\n\nREPLICATION MASTER ADMIN\n------------------------\n\nPermits administration of primary servers, including the SHOW REPLICA HOSTS\nstatement, and setting the gtid_binlog_state, gtid_domain_id,\nmaster_verify_checksum and server_id system variables. Added in MariaDB 10.5.2.\n\nREPLICA MONITOR\n---------------\n\nPermit SHOW REPLICA STATUS and SHOW RELAYLOG EVENTS. From MariaDB 10.5.9.\n\nWhen a user would upgrade from an older major release to a MariaDB 10.5 minor\nrelease prior to MariaDB 10.5.9, certain user accounts would lose\ncapabilities. For example, a user account that had the REPLICATION CLIENT\nprivilege in older major releases could run SHOW REPLICA STATUS, but after\nupgrading to a MariaDB 10.5 minor release prior to MariaDB 10.5.9, they could\nno longer run SHOW REPLICA STATUS, because that statement was changed to\nrequire the REPLICATION REPLICA ADMIN privilege.\n\nThis issue is fixed in MariaDB 10.5.9 with this new privilege, which now\ngrants the user the ability to execute SHOW [ALL] (SLAVE | REPLICA) STATUS.\n\nWhen a database is upgraded from an older major release to MariaDB Server\n10.5.9 or later, any user accounts with the REPLICATION CLIENT or REPLICATION\nSLAVE privileges will automatically be granted the new REPLICA MONITOR\nprivilege. The privilege fix occurs when the server is started up, not when\nmariadb-upgrade is performed.\n\nHowever, when a database is upgraded from an early 10.5 minor release to\n10.5.9 and later, the user will have to fix any user account privileges\nmanually.\n\nREPLICATION REPLICA\n-------------------\n\nSynonym for REPLICATION SLAVE. From MariaDB 10.5.1.\n\nREPLICATION SLAVE\n-----------------\n\nAccounts used by replica servers on the primary need this privilege. This is\nneeded to get the updates made on the master. From MariaDB 10.5.1, REPLICATION\nREPLICA is an alias for REPLICATION SLAVE.\n\nREPLICATION SLAVE ADMIN\n-----------------------\n\nPermits administering replica servers, including START REPLICA/SLAVE, STOP\nREPLICA/SLAVE, CHANGE MASTER, SHOW REPLICA/SLAVE STATUS, SHOW RELAYLOG EVENTS\nstatements, replaying the binary log with the BINLOG statement (generated by\nmariadb-binlog), and setting the system variables:\n\n* gtid_cleanup_batch_size\n* gtid_ignore_duplicates\n* gtid_pos_auto_engines\n* gtid_slave_pos\n* gtid_strict_mode\n* init_slave\n* read_binlog_speed_limit\n* relay_log_purge\n* relay_log_recovery\n* replicate_do_db\n* replicate_do_table\n* replicate_events_marked_for_skip\n* replicate_ignore_db\n* replicate_ignore_table\n* replicate_wild_do_table\n* replicate_wild_ignore_table\n* slave_compressed_protocol\n* slave_ddl_exec_mode\n* slave_domain_parallel_threads\n* slave_exec_mode\n* slave_max_allowed_packet\n* slave_net_timeout\n* slave_parallel_max_queued\n* slave_parallel_mode\n* slave_parallel_threads\n* slave_parallel_workers\n* slave_run_triggers_for_rbr\n* slave_sql_verify_checksum\n* slave_transaction_retry_interval\n* slave_type_conversions\n* sync_master_info\n* sync_relay_log, and\n* sync_relay_log_info.\n\nAdded in MariaDB 10.5.2.\n\nSET USER\n--------\n','','https://mariadb.com/kb/en/grant/'); update help_topic set description = CONCAT(description, '\nEnables setting the DEFINER when creating triggers, views, stored functions\nand stored procedures. Added in MariaDB 10.5.2.\n\nSHOW DATABASES\n--------------\n\nList all databases using the SHOW DATABASES statement. Without the SHOW\nDATABASES privilege, you can still issue the SHOW DATABASES statement, but it\nwill only list databases containing tables on which you have privileges.\n\nSHUTDOWN\n--------\n\nShut down the server using SHUTDOWN or the mysqladmin shutdown command.\n\nSUPER\n-----\n\nExecute superuser statements: CHANGE MASTER TO, KILL (users who do not have\nthis privilege can only KILL their own threads), PURGE LOGS, SET global system\nvariables, or the mysqladmin debug command. Also, this permission allows the\nuser to write data even if the read_only startup option is set, enable or\ndisable logging, enable or disable replication on replica, specify a DEFINER\nfor statements that support that clause, connect once reaching the\nMAX_CONNECTIONS. If a statement has been specified for the init-connect mysqld\noption, that command will not be executed when a user with SUPER privileges\nconnects to the server.\n\nThe SUPER privilege has been split into multiple smaller privileges from\nMariaDB 10.5.2 to allow for more fine-grained privileges, although it remains\nan alias for these smaller privileges.\n\nFrom MariaDB 10.11.0, the READ_ONLY ADMIN privilege has been removed from\nSUPER. The benefit of this is that one can remove the READ_ONLY ADMIN\nprivilege from all users and ensure that no one can make any changes on any\nnon-temporary tables. This is useful on replicas when one wants to ensure that\nthe replica is kept identical to the primary.\n\nDatabase Privileges\n-------------------\n\nThe following table lists the privileges that can be granted at the database\nlevel. You can also grant all table and function privileges at the database\nlevel. Table and function privileges on a database apply to all tables or\nfunctions in that database, including those created later.\n\nTo set a privilege for a database, specify the database using db_name.* for\npriv_level, or just use * to specify the default database.\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |\n+----------------------------------+-----------------------------------------+\n| CREATE | Create a database using the CREATE |\n| | DATABASE statement, when the privilege |\n| | is granted for a database. You can |\n| | grant the CREATE privilege on |\n| | databases that do not yet exist. This |\n| | also grants the CREATE privilege on |\n| | all tables in the database. |\n+----------------------------------+-----------------------------------------+\n| CREATE ROUTINE | Create Stored Programs using the |\n| | CREATE PROCEDURE and CREATE FUNCTION |\n| | statements. |\n+----------------------------------+-----------------------------------------+\n| CREATE TEMPORARY TABLES | Create temporary tables with the |\n| | CREATE TEMPORARY TABLE statement. This |\n| | privilege enable writing and dropping |\n| | those temporary tables |\n+----------------------------------+-----------------------------------------+\n| DROP | Drop a database using the DROP |\n| | DATABASE statement, when the privilege |\n| | is granted for a database. This also |\n| | grants the DROP privilege on all |\n| | tables in the database. |\n+----------------------------------+-----------------------------------------+\n| EVENT | Create, drop and alter EVENTs. |\n+----------------------------------+-----------------------------------------+\n| GRANT OPTION | Grant database privileges. You can |\n| | only grant privileges that you have. |\n+----------------------------------+-----------------------------------------+\n| LOCK TABLES | Acquire explicit locks using the LOCK |\n| | TABLES statement; you also need to |\n| | have the SELECT privilege on a table, |\n| | in order to lock it. |\n+----------------------------------+-----------------------------------------+\n\nTable Privileges\n----------------\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |\n+----------------------------------+-----------------------------------------+\n| ALTER | Change the structure of an existing |\n| | table using the ALTER TABLE statement. |\n+----------------------------------+-----------------------------------------+\n| CREATE | Create a table using the CREATE TABLE |\n| | statement. You can grant the CREATE |\n| | privilege on tables that do not yet |\n| | exist. |\n+----------------------------------+-----------------------------------------+\n| CREATE VIEW | Create a view using the CREATE_VIEW |\n| | statement. |\n+----------------------------------+-----------------------------------------+\n| DELETE | Remove rows from a table using the |\n| | DELETE statement. |\n+----------------------------------+-----------------------------------------+\n| DELETE HISTORY | Remove historical rows from a table |\n| | using the DELETE HISTORY statement. |\n| | Displays as DELETE VERSIONING ROWS |\n| | when running SHOW GRANTS until MariaDB |\n| | 10.3.15 and until MariaDB 10.4.5 |\n| | (MDEV-17655), or when running SHOW |\n| | PRIVILEGES until MariaDB 10.5.2, |\n| | MariaDB 10.4.13 and MariaDB 10.3.23 |\n| | (MDEV-20382). From MariaDB 10.3.4. |\n| | From MariaDB 10.3.5, if a user has the |\n| | SUPER privilege but not this |\n| | privilege, running mysql_upgrade will |\n| | grant this privilege as well. |\n+----------------------------------+-----------------------------------------+\n| DROP | Drop a table using the DROP TABLE |\n| | statement or a view using the DROP |\n| | VIEW statement. Also required to |\n| | execute the TRUNCATE TABLE statement. |\n+----------------------------------+-----------------------------------------+\n| GRANT OPTION | Grant table privileges. You can only |\n| | grant privileges that you have. |\n+----------------------------------+-----------------------------------------+\n| INDEX | Create an index on a table using the |\n| | CREATE INDEX statement. Without the |\n| | INDEX privilege, you can still create |\n| | indexes when creating a table using |\n| | the CREATE TABLE statement if the you |\n| | have the CREATE privilege, and you can |\n| | create indexes using the ALTER TABLE |\n| | statement if you have the ALTER |\n| | privilege. |\n+----------------------------------+-----------------------------------------+\n| INSERT | Add rows to a table using the INSERT |\n| | statement. The INSERT privilege can |\n| | also be set on individual columns; see |\n| | Column Privileges below for details. |\n+----------------------------------+-----------------------------------------+\n| REFERENCES | Unused. |\n+----------------------------------+-----------------------------------------+\n| SELECT | Read data from a table using the |\n| | SELECT statement. The SELECT privilege |\n| | can also be set on individual columns; |\n| | see Column Privileges below for |\n| | details. |\n+----------------------------------+-----------------------------------------+\n| SHOW VIEW | Show the CREATE VIEW statement to |\n| | create a view using the SHOW CREATE |\n| | VIEW statement. |\n+----------------------------------+-----------------------------------------+\n| TRIGGER | Execute triggers associated to tables |\n| | you update, execute the CREATE TRIGGER |\n| | and DROP TRIGGER statements. You will |\n| | still be able to see triggers. |\n+----------------------------------+-----------------------------------------+\n| UPDATE | Update existing rows in a table using |\n| | the UPDATE statement. UPDATE |\n| | statements usually include a WHERE |\n| | clause to update only certain rows. |\n| | You must have SELECT privileges on the |\n| | table or the appropriate columns for |\n| | the WHERE clause. The UPDATE privilege |\n| | can also be set on individual columns; |\n| | see Column Privileges below for |\n| | details. |\n+----------------------------------+-----------------------------------------+\n\nColumn Privileges\n-----------------\n\nSome table privileges can be set for individual columns of a table. To use\ncolumn privileges, specify the table explicitly and provide a list of column\nnames after the privilege type. For example, the following statement would\nallow the user to read the names and positions of employees, but not other\ninformation from the same table, such as salaries.\n\nGRANT SELECT (name, position) on Employee to \'jeffrey\'@\'localhost\';\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |\n+----------------------------------+-----------------------------------------+\n| INSERT (column_list) | Add rows specifying values in columns |\n| | using the INSERT statement. If you |\n| | only have column-level INSERT |\n| | privileges, you must specify the |\n| | columns you are setting in the INSERT |\n| | statement. All other columns will be |\n| | set to their default values, or NULL. |\n+----------------------------------+-----------------------------------------+\n| REFERENCES (column_list) | Unused. |\n+----------------------------------+-----------------------------------------+\n| SELECT (column_list) | Read values in columns using the |\n| | SELECT statement. You cannot access or |\n| | query any columns for which you do not |\n| | have SELECT privileges, including in |\n| | WHERE, ON, GROUP BY, and ORDER BY |\n| | clauses. |\n+----------------------------------+-----------------------------------------+\n| UPDATE (column_list) | Update values in columns of existing |\n| | rows using the UPDATE statement. |\n| | UPDATE statements usually include a |\n| | WHERE clause to update only certain |\n| | rows. You must have SELECT privileges |\n| | on the table or the appropriate |\n| | columns for the WHERE clause. |\n+----------------------------------+-----------------------------------------+\n\nFunction Privileges\n-------------------\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |') WHERE help_topic_id = 107; update help_topic set description = CONCAT(description, '\n+----------------------------------+-----------------------------------------+\n| ALTER ROUTINE | Change the characteristics of a stored |\n| | function using the ALTER FUNCTION |\n| | statement. |\n+----------------------------------+-----------------------------------------+\n| EXECUTE | Use a stored function. You need SELECT |\n| | privileges for any tables or columns |\n| | accessed by the function. |\n+----------------------------------+-----------------------------------------+\n| GRANT OPTION | Grant function privileges. You can |\n| | only grant privileges that you have. |\n+----------------------------------+-----------------------------------------+\n\nProcedure Privileges\n--------------------\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |\n+----------------------------------+-----------------------------------------+\n| ALTER ROUTINE | Change the characteristics of a stored |\n| | procedure using the ALTER PROCEDURE |\n| | statement. |\n+----------------------------------+-----------------------------------------+\n| EXECUTE | Execute a stored procedure using the |\n| | CALL statement. The privilege to call |\n| | a procedure may allow you to perform |\n| | actions you wouldn\'t otherwise be able |\n| | to do, such as insert rows into a |\n| | table. |\n+----------------------------------+-----------------------------------------+\n| GRANT OPTION | Grant procedure privileges. You can |\n| | only grant privileges that you have. |\n+----------------------------------+-----------------------------------------+\n\nGRANT EXECUTE ON PROCEDURE mysql.create_db TO maintainer;\n\nProxy Privileges\n----------------\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |\n+----------------------------------+-----------------------------------------+\n| PROXY | Permits one user to be a proxy for |\n| | another. |\n+----------------------------------+-----------------------------------------+\n\nThe PROXY privilege allows one user to proxy as another user, which means\ntheir privileges change to that of the proxy user, and the CURRENT_USER()\nfunction returns the user name of the proxy user.\n\nThe PROXY privilege only works with authentication plugins that support it.\nThe default mysql_native_password authentication plugin does not support proxy\nusers.\n\nThe pam authentication plugin is the only plugin included with MariaDB that\ncurrently supports proxy users. The PROXY privilege is commonly used with the\npam authentication plugin to enable user and group mapping with PAM.\n\nFor example, to grant the PROXY privilege to an anonymous account that\nauthenticates with the pam authentication plugin, you could execute the\nfollowing:\n\nCREATE USER \'dba\'@\'%\' IDENTIFIED BY \'strongpassword\';\nGRANT ALL PRIVILEGES ON *.* TO \'dba\'@\'%\' ;\n\nCREATE USER \'\'@\'%\' IDENTIFIED VIA pam USING \'mariadb\';\nGRANT PROXY ON \'dba\'@\'%\' TO \'\'@\'%\';\n\nA user account can only grant the PROXY privilege for a specific user account\nif the granter also has the PROXY privilege for that specific user account,\nand if that privilege is defined WITH GRANT OPTION. For example, the following\nexample fails because the granter does not have the PROXY privilege for that\nspecific user account at all:\n\nSELECT USER(), CURRENT_USER();\n+-----------------+-----------------+\n| USER() | CURRENT_USER() |\n+-----------------+-----------------+\n| alice@localhost | alice@localhost |\n+-----------------+-----------------+\n\nSHOW GRANTS;\n+------------------------------------------------------------------------------\n----------------------------------------+\n| Grants for alice@localhost \n |\n+------------------------------------------------------------------------------\n----------------------------------------+\n| GRANT ALL PRIVILEGES ON *.* TO \'alice\'@\'localhost\' IDENTIFIED BY PASSWORD\n\'*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19\' |\n+------------------------------------------------------------------------------\n----------------------------------------+\n\nGRANT PROXY ON \'dba\'@\'localhost\' TO \'bob\'@\'localhost\';\nERROR 1698 (28000): Access denied for user \'alice\'@\'localhost\'\n\nAnd the following example fails because the granter does have the PROXY\nprivilege for that specific user account, but it is not defined WITH GRANT\nOPTION:\n\nSELECT USER(), CURRENT_USER();\n+-----------------+-----------------+\n| USER() | CURRENT_USER() |\n+-----------------+-----------------+\n| alice@localhost | alice@localhost |\n+-----------------+-----------------+\n\nSHOW GRANTS;\n+------------------------------------------------------------------------------\n----------------------------------------+\n| Grants for alice@localhost \n |\n+------------------------------------------------------------------------------\n----------------------------------------+\n| GRANT ALL PRIVILEGES ON *.* TO \'alice\'@\'localhost\' IDENTIFIED BY PASSWORD\n\'*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19\' |\n| GRANT PROXY ON \'dba\'@\'localhost\' TO \'alice\'@\'localhost\' \n |\n+------------------------------------------------------------------------------\n----------------------------------------+\n\nGRANT PROXY ON \'dba\'@\'localhost\' TO \'bob\'@\'localhost\';\nERROR 1698 (28000): Access denied for user \'alice\'@\'localhost\'\n\nBut the following example succeeds because the granter does have the PROXY\nprivilege for that specific user account, and it is defined WITH GRANT OPTION:\n\nSELECT USER(), CURRENT_USER();\n+-----------------+-----------------+\n| USER() | CURRENT_USER() |\n+-----------------+-----------------+\n| alice@localhost | alice@localhost |\n+-----------------+-----------------+\n\nSHOW GRANTS;\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n| Grants for alice@localhost \n |\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n| GRANT ALL PRIVILEGES ON *.* TO \'alice\'@\'localhost\' IDENTIFIED BY PASSWORD\n\'*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19\' WITH GRANT OPTION |\n| GRANT PROXY ON \'dba\'@\'localhost\' TO \'alice\'@\'localhost\' WITH GRANT OPTION \n |\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n\nGRANT PROXY ON \'dba\'@\'localhost\' TO \'bob\'@\'localhost\';\n\nA user account can grant the PROXY privilege for any other user account if the\ngranter has the PROXY privilege for the \'\'@\'%\' anonymous user account, like\nthis:\n\nGRANT PROXY ON \'\'@\'%\' TO \'dba\'@\'localhost\' WITH GRANT OPTION;\n\nFor example, the following example succeeds because the user can grant the\nPROXY privilege for any other user account:\n\nSELECT USER(), CURRENT_USER();\n+-----------------+-----------------+\n| USER() | CURRENT_USER() |\n+-----------------+-----------------+\n| alice@localhost | alice@localhost |\n+-----------------+-----------------+\n\nSHOW GRANTS;\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n| Grants for alice@localhost \n |\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n| GRANT ALL PRIVILEGES ON *.* TO \'alice\'@\'localhost\' IDENTIFIED BY PASSWORD\n\'*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19\' WITH GRANT OPTION |\n| GRANT PROXY ON \'\'@\'%\' TO \'alice\'@\'localhost\' WITH GRANT OPTION \n |\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n\nGRANT PROXY ON \'app1_dba\'@\'localhost\' TO \'bob\'@\'localhost\';\nQuery OK, 0 rows affected (0.004 sec)\n\nGRANT PROXY ON \'app2_dba\'@\'localhost\' TO \'carol\'@\'localhost\';\nQuery OK, 0 rows affected (0.004 sec)\n\nThe default root user accounts created by mysql_install_db have this\nprivilege. For example:\n\nGRANT ALL PRIVILEGES ON *.* TO \'root\'@\'localhost\' WITH GRANT OPTION;\nGRANT PROXY ON \'\'@\'%\' TO \'root\'@\'localhost\' WITH GRANT OPTION;\n\nThis allows the default root user accounts to grant the PROXY privilege for\nany other user account, and it also allows the default root user accounts to\ngrant others the privilege to do the same.\n\nAuthentication Options\n----------------------\n\nThe authentication options for the GRANT statement are the same as those for\nthe CREATE USER statement.\n\nIDENTIFIED BY \'password\'\n------------------------\n\nThe optional IDENTIFIED BY clause can be used to provide an account with a\npassword. The password should be specified in plain text. It will be hashed by\nthe PASSWORD function prior to being stored.\n\nFor example, if our password is mariadb, then we can create the user with:\n\nGRANT USAGE ON *.* TO foo2@test IDENTIFIED BY \'mariadb\';\n\nIf you do not specify a password with the IDENTIFIED BY clause, the user will\nbe able to connect without a password. A blank password is not a wildcard to\nmatch any password. The user must connect without providing a password if no\npassword is set.\n\nIf the user account already exists and if you provide the IDENTIFIED BY\nclause, then the user\'s password will be changed. You must have the privileges\nneeded for the SET PASSWORD statement to change a user\'s password with GRANT.\n\nThe only authentication plugins that this clause supports are\nmysql_native_password and mysql_old_password.\n\nIDENTIFIED BY PASSWORD \'password_hash\'\n--------------------------------------\n\nThe optional IDENTIFIED BY PASSWORD clause can be used to provide an account\nwith a password that has already been hashed. The password should be specified\nas a hash that was provided by the PASSWORD function. It will be stored as-is.\n\nFor example, if our password is mariadb, then we can find the hash with:\n\nSELECT PASSWORD(\'mariadb\');\n+-------------------------------------------+\n| PASSWORD(\'mariadb\') |\n+-------------------------------------------+\n| *54958E764CE10E50764C2EECBB71D01F08549980 |\n+-------------------------------------------+\n1 row in set (0.00 sec)\n\nAnd then we can create a user with the hash:\n\nGRANT USAGE ON *.* TO foo2@test IDENTIFIED BY \n PASSWORD \'*54958E764CE10E50764C2EECBB71D01F08549980\';\n\nIf you do not specify a password with the IDENTIFIED BY clause, the user will\nbe able to connect without a password. A blank password is not a wildcard to\nmatch any password. The user must connect without providing a password if no\npassword is set.\n\nIf the user account already exists and if you provide the IDENTIFIED BY\nclause, then the user\'s password will be changed. You must have the privileges\nneeded for the SET PASSWORD statement to change a user\'s password with GRANT.\n\nThe only authentication plugins that this clause supports are\nmysql_native_password and mysql_old_password.\n\nIDENTIFIED {VIA|WITH} authentication_plugin\n-------------------------------------------\n\nThe optional IDENTIFIED VIA authentication_plugin allows you to specify that\nthe account should be authenticated by a specific authentication plugin. The\nplugin name must be an active authentication plugin as per SHOW PLUGINS. If it\ndoesn\'t show up in that output, then you will need to install it with INSTALL\nPLUGIN or INSTALL SONAME.\n\nFor example, this could be used with the PAM authentication plugin:\n\nGRANT USAGE ON *.* TO foo2@test IDENTIFIED VIA pam;\n\nSome authentication plugins allow additional arguments to be specified after a\nUSING or AS keyword. For example, the PAM authentication plugin accepts a\nservice name:\n\nGRANT USAGE ON *.* TO foo2@test IDENTIFIED VIA pam USING \'mariadb\';\n\nThe exact meaning of the additional argument would depend on the specific\nauthentication plugin.\n\nMariaDB starting with 10.4.0\n----------------------------\nThe USING or AS keyword can also be used to provide a plain-text password to a\nplugin if it\'s provided as an argument to the PASSWORD() function. This is\nonly valid for authentication plugins that have implemented a hook for the\nPASSWORD() function. For example, the ed25519 authentication plugin supports\nthis:\n\nCREATE USER safe@\'%\' IDENTIFIED VIA ed25519 \n USING PASSWORD(\'secret\');\n\nMariaDB starting with 10.4.3\n----------------------------\nOne can specify many authentication plugins, they all work as alternatives\nways of authenticating a user:\n\nCREATE USER safe@\'%\' IDENTIFIED VIA ed25519 \n USING PASSWORD(\'secret\') OR unix_socket;\n\nBy default, when you create a user without specifying an authentication\nplugin, MariaDB uses the mysql_native_password plugin.\n\nResource Limit Options\n----------------------\n\nIt is possible to set per-account limits for certain server resources. The\nfollowing table shows the values that can be set per account:\n\n+--------------------------------------+--------------------------------------+') WHERE help_topic_id = 107; -update help_topic set description = CONCAT(description, '\n| Limit Type | Decription |\n+--------------------------------------+--------------------------------------+\n| MAX_QUERIES_PER_HOUR | Number of statements that the |\n| | account can issue per hour |\n| | (including updates) |\n+--------------------------------------+--------------------------------------+\n| MAX_UPDATES_PER_HOUR | Number of updates (not queries) |\n| | that the account can issue per hour |\n+--------------------------------------+--------------------------------------+\n| MAX_CONNECTIONS_PER_HOUR | Number of connections that the |\n| | account can start per hour |\n+--------------------------------------+--------------------------------------+\n| MAX_USER_CONNECTIONS | Number of simultaneous connections |\n| | that can be accepted from the same |\n| | account; if it is 0, |\n| | max_connections will be used |\n| | instead; if max_connections is 0, |\n| | there is no limit for this |\n| | account\'s simultaneous connections. |\n+--------------------------------------+--------------------------------------+\n| MAX_STATEMENT_TIME | Timeout, in seconds, for statements |\n| | executed by the user. See also |\n| | Aborting Statements that Exceed a |\n| | Certain Time to Execute. |\n+--------------------------------------+--------------------------------------+\n\nIf any of these limits are set to 0, then there is no limit for that resource\nfor that user.\n\nTo set resource limits for an account, if you do not want to change that\naccount\'s privileges, you can issue a GRANT statement with the USAGE\nprivilege, which has no meaning. The statement can name some or all limit\ntypes, in any order.\n\nHere is an example showing how to set resource limits:\n\nGRANT USAGE ON *.* TO \'someone\'@\'localhost\' WITH\n MAX_USER_CONNECTIONS 0\n MAX_QUERIES_PER_HOUR 200;\n\nThe resources are tracked per account, which means \'user\'@\'server\'; not per\nuser name or per connection.\n\nThe count can be reset for all users using FLUSH USER_RESOURCES, FLUSH\nPRIVILEGES or mysqladmin reload.\n\nUsers with the CONNECTION ADMIN privilege (in MariaDB 10.5.2 and later) or the\nSUPER privilege are not restricted by max_user_connections, max_connections,\nor max_password_errors.\n\nPer account resource limits are stored in the user table, in the mysql\ndatabase. Columns used for resources limits are named max_questions,\nmax_updates, max_connections (for MAX_CONNECTIONS_PER_HOUR), and\nmax_user_connections (for MAX_USER_CONNECTIONS).\n\nTLS Options\n-----------\n\nBy default, MariaDB transmits data between the server and clients without\nencrypting it. This is generally acceptable when the server and client run on\nthe same host or in networks where security is guaranteed through other means.\nHowever, in cases where the server and client exist on separate networks or\nthey are in a high-risk network, the lack of encryption does introduce\nsecurity concerns as a malicious actor could potentially eavesdrop on the\ntraffic as it is sent over the network between them.\n\nTo mitigate this concern, MariaDB allows you to encrypt data in transit\nbetween the server and clients using the Transport Layer Security (TLS)\nprotocol. TLS was formerly known as Secure Socket Layer (SSL), but strictly\nspeaking the SSL protocol is a predecessor to TLS and, that version of the\nprotocol is now considered insecure. The documentation still uses the term SSL\noften and for compatibility reasons TLS-related server system and status\nvariables still use the prefix ssl_, but internally, MariaDB only supports its\nsecure successors.\n\nSee Secure Connections Overview for more information about how to determine\nwhether your MariaDB server has TLS support.\n\nYou can set certain TLS-related restrictions for specific user accounts. For\ninstance, you might use this with user accounts that require access to\nsensitive data while sending it across networks that you do not control. These\nrestrictions can be enabled for a user account with the CREATE USER, ALTER\nUSER, or GRANT statements. The following options are available:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| REQUIRE NONE | TLS is not required for this account, but can |\n| | still be used. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SSL | The account must use TLS, but no valid X509 |\n| | certificate is required. This option cannot |\n| | be combined with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE X509 | The account must use TLS and must have a |\n| | valid X509 certificate. This option implies |\n| | REQUIRE SSL. This option cannot be combined |\n| | with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE ISSUER \'issuer\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the Certificate |\n| | Authority must be the one specified via the |\n| | string issuer. This option implies REQUIRE |\n| | X509. This option can be combined with the |\n| | SUBJECT, and CIPHER options in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SUBJECT \'subject\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the |\n| | certificate\'s Subject must be the one |\n| | specified via the string subject. This option |\n| | implies REQUIRE X509. This option can be |\n| | combined with the ISSUER, and CIPHER options |\n| | in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE CIPHER \'cipher\' | The account must use TLS, but no valid X509 |\n| | certificate is required. Also, the encryption |\n| | used for the connection must use a specific |\n| | cipher method specified in the string cipher. |\n| | This option implies REQUIRE SSL. This option |\n| | can be combined with the ISSUER, and SUBJECT |\n| | options in any order. |\n+---------------------------+------------------------------------------------+\n\nThe REQUIRE keyword must be used only once for all specified options, and the\nAND keyword can be used to separate individual options, but it is not required.\n\nFor example, you can create a user account that requires these TLS options\nwith the following:\n\nGRANT USAGE ON *.* TO \'alice\'@\'%\'\n REQUIRE SUBJECT \'/CN=alice/O=My Dom, Inc./C=US/ST=Oregon/L=Portland\'\n AND ISSUER \'/C=FI/ST=Somewhere/L=City/ O=Some Company/CN=Peter\nParker/emailAddress=p.parker@marvel.com\'\n AND CIPHER \'SHA-DES-CBC3-EDH-RSA\';\n\nIf any of these options are set for a specific user account, then any client\nwho tries to connect with that user account will have to be configured to\nconnect with TLS.\n\nSee Securing Connections for Client and Server for information on how to\nenable TLS on the client and server.\n\nRoles\n-----\n\nSyntax\n------\n\nGRANT role TO grantee [, grantee ... ]\n[ WITH ADMIN OPTION ]\n\ngrantee:\n rolename\n username [authentication_option]\n\nThe GRANT statement is also used to grant the use a role to one or more users\nor other roles. In order to be able to grant a role, the grantor doing so must\nhave permission to do so (see WITH ADMIN in the CREATE ROLE article).\n\nSpecifying the WITH ADMIN OPTION permits the grantee to in turn grant the role\nto another.\n\nFor example, the following commands show how to grant the same role to a\ncouple different users.\n\nGRANT journalist TO hulda;\n\nGRANT journalist TO berengar WITH ADMIN OPTION;\n\nIf a user has been granted a role, they do not automatically obtain all\npermissions associated with that role. These permissions are only in use when\nthe user activates the role with the SET ROLE statement.\n\nTO PUBLIC\n---------\n\nMariaDB starting with 10.11\n---------------------------\n\nSyntax\n------\n\nGRANT ON . TO PUBLIC;\nREVOKE ON . FROM PUBLIC;\n\nGRANT ... TO PUBLIC grants privileges to all users with access to the server.\nThe privileges also apply to users created after the privileges are granted.\nThis can be useful when one only wants to state once that all users need to\nhave a certain set of privileges.\n\nWhen running SHOW GRANTS, a user will also see all privileges inherited from\nPUBLIC. SHOW GRANTS FOR PUBLIC will only show TO PUBLIC grants.\n\nGrant Examples\n--------------\n\nGranting Root-like Privileges\n-----------------------------\n\nYou can create a user that has privileges similar to the default root accounts\nby executing the following:\n\nCREATE USER \'alexander\'@\'localhost\';\nGRANT ALL PRIVILEGES ON *.* to \'alexander\'@\'localhost\' WITH GRANT OPTION;\n\nURL: https://mariadb.com/kb/en/grant/') WHERE help_topic_id = 107; +update help_topic set description = CONCAT(description, '\n| Limit Type | Decription |\n+--------------------------------------+--------------------------------------+\n| MAX_QUERIES_PER_HOUR | Number of statements that the |\n| | account can issue per hour |\n| | (including updates) |\n+--------------------------------------+--------------------------------------+\n| MAX_UPDATES_PER_HOUR | Number of updates (not queries) |\n| | that the account can issue per hour |\n+--------------------------------------+--------------------------------------+\n| MAX_CONNECTIONS_PER_HOUR | Number of connections that the |\n| | account can start per hour |\n+--------------------------------------+--------------------------------------+\n| MAX_USER_CONNECTIONS | Number of simultaneous connections |\n| | that can be accepted from the same |\n| | account; if it is 0, |\n| | max_connections will be used |\n| | instead; if max_connections is 0, |\n| | there is no limit for this |\n| | account\'s simultaneous connections. |\n+--------------------------------------+--------------------------------------+\n| MAX_STATEMENT_TIME | Timeout, in seconds, for statements |\n| | executed by the user. See also |\n| | Aborting Statements that Exceed a |\n| | Certain Time to Execute. |\n+--------------------------------------+--------------------------------------+\n\nIf any of these limits are set to 0, then there is no limit for that resource\nfor that user.\n\nTo set resource limits for an account, if you do not want to change that\naccount\'s privileges, you can issue a GRANT statement with the USAGE\nprivilege, which has no meaning. The statement can name some or all limit\ntypes, in any order.\n\nHere is an example showing how to set resource limits:\n\nGRANT USAGE ON *.* TO \'someone\'@\'localhost\' WITH\n MAX_USER_CONNECTIONS 0\n MAX_QUERIES_PER_HOUR 200;\n\nThe resources are tracked per account, which means \'user\'@\'server\'; not per\nuser name or per connection.\n\nThe count can be reset for all users using FLUSH USER_RESOURCES, FLUSH\nPRIVILEGES or mysqladmin reload.\n\nUsers with the CONNECTION ADMIN privilege (in MariaDB 10.5.2 and later) or the\nSUPER privilege are not restricted by max_user_connections, max_connections,\nor max_password_errors.\n\nPer account resource limits are stored in the user table, in the mysql\ndatabase. Columns used for resources limits are named max_questions,\nmax_updates, max_connections (for MAX_CONNECTIONS_PER_HOUR), and\nmax_user_connections (for MAX_USER_CONNECTIONS).\n\nTLS Options\n-----------\n\nBy default, MariaDB transmits data between the server and clients without\nencrypting it. This is generally acceptable when the server and client run on\nthe same host or in networks where security is guaranteed through other means.\nHowever, in cases where the server and client exist on separate networks or\nthey are in a high-risk network, the lack of encryption does introduce\nsecurity concerns as a malicious actor could potentially eavesdrop on the\ntraffic as it is sent over the network between them.\n\nTo mitigate this concern, MariaDB allows you to encrypt data in transit\nbetween the server and clients using the Transport Layer Security (TLS)\nprotocol. TLS was formerly known as Secure Socket Layer (SSL), but strictly\nspeaking the SSL protocol is a predecessor to TLS and, that version of the\nprotocol is now considered insecure. The documentation still uses the term SSL\noften and for compatibility reasons TLS-related server system and status\nvariables still use the prefix ssl_, but internally, MariaDB only supports its\nsecure successors.\n\nSee Secure Connections Overview for more information about how to determine\nwhether your MariaDB server has TLS support.\n\nYou can set certain TLS-related restrictions for specific user accounts. For\ninstance, you might use this with user accounts that require access to\nsensitive data while sending it across networks that you do not control. These\nrestrictions can be enabled for a user account with the CREATE USER, ALTER\nUSER, or GRANT statements. The following options are available:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| REQUIRE NONE | TLS is not required for this account, but can |\n| | still be used. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SSL | The account must use TLS, but no valid X509 |\n| | certificate is required. This option cannot |\n| | be combined with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE X509 | The account must use TLS and must have a |\n| | valid X509 certificate. This option implies |\n| | REQUIRE SSL. This option cannot be combined |\n| | with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE ISSUER \'issuer\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the Certificate |\n| | Authority must be the one specified via the |\n| | string issuer. This option implies REQUIRE |\n| | X509. This option can be combined with the |\n| | SUBJECT, and CIPHER options in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SUBJECT \'subject\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the |\n| | certificate\'s Subject must be the one |\n| | specified via the string subject. This option |\n| | implies REQUIRE X509. This option can be |\n| | combined with the ISSUER, and CIPHER options |\n| | in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE CIPHER \'cipher\' | The account must use TLS, but no valid X509 |\n| | certificate is required. Also, the encryption |\n| | used for the connection must use a specific |\n| | cipher method specified in the string cipher. |\n| | This option implies REQUIRE SSL. This option |\n| | can be combined with the ISSUER, and SUBJECT |\n| | options in any order. |\n+---------------------------+------------------------------------------------+\n\nThe REQUIRE keyword must be used only once for all specified options, and the\nAND keyword can be used to separate individual options, but it is not required.\n\nFor example, you can create a user account that requires these TLS options\nwith the following:\n\nGRANT USAGE ON *.* TO \'alice\'@\'%\'\n REQUIRE SUBJECT \'/CN=alice/O=My Dom, Inc./C=US/ST=Oregon/L=Portland\'\n AND ISSUER \'/C=FI/ST=Somewhere/L=City/ O=Some Company/CN=Peter\nParker/emailAddress=p.parker@marvel.com\'\n AND CIPHER \'SHA-DES-CBC3-EDH-RSA\';\n\nIf any of these options are set for a specific user account, then any client\nwho tries to connect with that user account will have to be configured to\nconnect with TLS.\n\nSee Securing Connections for Client and Server for information on how to\nenable TLS on the client and server.\n\nRoles\n-----\n\nSyntax\n------\n\nGRANT role TO grantee [, grantee ... ]\n[ WITH ADMIN OPTION ]\n\ngrantee:\n rolename\n username [authentication_option]\n\nThe GRANT statement is also used to grant the use of a role to one or more\nusers or other roles. In order to be able to grant a role, the grantor doing\nso must have permission to do so (see WITH ADMIN in the CREATE ROLE article).\n\nSpecifying the WITH ADMIN OPTION permits the grantee to in turn grant the role\nto another.\n\nFor example, the following commands show how to grant the same role to a\ncouple different users.\n\nGRANT journalist TO hulda;\n\nGRANT journalist TO berengar WITH ADMIN OPTION;\n\nIf a user has been granted a role, they do not automatically obtain all\npermissions associated with that role. These permissions are only in use when\nthe user activates the role with the SET ROLE statement.\n\nTO PUBLIC\n---------\n\nMariaDB starting with 10.11\n---------------------------\n\nSyntax\n------\n\nGRANT ON . TO PUBLIC;\nREVOKE ON . FROM PUBLIC;\n\nGRANT ... TO PUBLIC grants privileges to all users with access to the server.\nThe privileges also apply to users created after the privileges are granted.\nThis can be useful when one only wants to state once that all users need to\nhave a certain set of privileges.\n\nWhen running SHOW GRANTS, a user will also see all privileges inherited from\nPUBLIC. SHOW GRANTS FOR PUBLIC will only show TO PUBLIC grants.\n\nGrant Examples\n--------------\n\nGranting Root-like Privileges\n-----------------------------\n\nYou can create a user that has privileges similar to the default root accounts\nby executing the following:\n\nCREATE USER \'alexander\'@\'localhost\';\nGRANT ALL PRIVILEGES ON *.* to \'alexander\'@\'localhost\' WITH GRANT OPTION;\n\nURL: https://mariadb.com/kb/en/grant/') WHERE help_topic_id = 107; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (108,10,'RENAME USER','Syntax\n------\n\nRENAME USER old_user TO new_user\n [, old_user TO new_user] ...\n\nDescription\n-----------\n\nThe RENAME USER statement renames existing MariaDB accounts. To use it, you\nmust have the global CREATE USER privilege or the UPDATE privilege for the\nmysql database. Each account is named using the same format as for the CREATE\nUSER statement; for example, \'jeffrey\'@\'localhost\'. If you specify only the\nuser name part of the account name, a host name part of \'%\' is used.\n\nIf any of the old user accounts do not exist or any of the new user accounts\nalready exist, ERROR 1396 (HY000) results. If an error occurs, RENAME USER\nwill still rename the accounts that do not result in an error.\n\nExamples\n--------\n\nCREATE USER \'donald\', \'mickey\';\nRENAME USER \'donald\' TO \'duck\'@\'localhost\', \'mickey\' TO \'mouse\'@\'localhost\';\n\nURL: https://mariadb.com/kb/en/rename-user/','','https://mariadb.com/kb/en/rename-user/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (109,10,'REVOKE','Privileges\n----------\n\nSyntax\n------\n\nREVOKE \n priv_type [(column_list)]\n [, priv_type [(column_list)]] ...\n ON [object_type] priv_level\n FROM user [, user] ...\n\nREVOKE ALL PRIVILEGES, GRANT OPTION\n FROM user [, user] ...\n\nDescription\n-----------\n\nThe REVOKE statement enables system administrators to revoke privileges (or\nroles - see section below) from MariaDB accounts. Each account is named using\nthe same format as for the GRANT statement; for example,\n\'jeffrey\'@\'localhost\'. If you specify only the user name part of the account\nname, a host name part of \'%\' is used. For details on the levels at which\nprivileges exist, the allowable priv_type and priv_level values, and the\nsyntax for specifying users and passwords, see GRANT.\n\nTo use the first REVOKE syntax, you must have the GRANT OPTION privilege, and\nyou must have the privileges that you are revoking.\n\nTo revoke all privileges, use the second syntax, which drops all global,\ndatabase, table, column, and routine privileges for the named user or users:\n\nREVOKE ALL PRIVILEGES, GRANT OPTION FROM user [, user] ...\n\nTo use this REVOKE syntax, you must have the global CREATE USER privilege or\nthe UPDATE privilege for the mysql database. See GRANT.\n\nExamples\n--------\n\nREVOKE SUPER ON *.* FROM \'alexander\'@\'localhost\';\n\nRoles\n-----\n\nSyntax\n------\n\nREVOKE role [, role ...]\n FROM grantee [, grantee2 ... ]\n\nREVOKE ADMIN OPTION FOR role FROM grantee [, grantee2]\n\nDescription\n-----------\n\nREVOKE is also used to remove a role from a user or another role that it\'s\npreviously been assigned to. If a role has previously been set as a default\nrole, REVOKE does not remove the record of the default role from the\nmysql.user table. If the role is subsequently granted again, it will again be\nthe user\'s default. Use SET DEFAULT ROLE NONE to explicitly remove this.\n\nBefore MariaDB 10.1.13, the REVOKE role statement was not permitted in\nprepared statements.\n\nExample\n-------\n\nREVOKE journalist FROM hulda\n\nURL: https://mariadb.com/kb/en/revoke/','','https://mariadb.com/kb/en/revoke/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (110,10,'SET PASSWORD','Syntax\n------\n\nSET PASSWORD [FOR user] =\n {\n PASSWORD(\'some password\')\n | OLD_PASSWORD(\'some password\')\n | \'encrypted password\'\n }\n\nDescription\n-----------\n\nThe SET PASSWORD statement assigns a password to an existing MariaDB user\naccount.\n\nIf the password is specified using the PASSWORD() or OLD_PASSWORD() function,\nthe literal text of the password should be given. If the password is specified\nwithout using either function, the password should be the already-encrypted\npassword value as returned by PASSWORD().\n\nOLD_PASSWORD() should only be used if your MariaDB/MySQL clients are very old\n(< 4.0.0).\n\nWith no FOR clause, this statement sets the password for the current user. Any\nclient that has connected to the server using a non-anonymous account can\nchange the password for that account.\n\nWith a FOR clause, this statement sets the password for a specific account on\nthe current server host. Only clients that have the UPDATE privilege for the\nmysql database can do this. The user value should be given in\nuser_name@host_name format, where user_name and host_name are exactly as they\nare listed in the User and Host columns of the mysql.user table (or view in\nMariaDB-10.4 onwards) entry.\n\nThe argument to PASSWORD() and the password given to MariaDB clients can be of\narbitrary length.\n\nAuthentication Plugin Support\n-----------------------------\n\nMariaDB starting with 10.4\n--------------------------\nIn MariaDB 10.4 and later, SET PASSWORD (with or without PASSWORD()) works for\naccounts authenticated via any authentication plugin that supports passwords\nstored in the mysql.global_priv table.\n\nThe ed25519, mysql_native_password, and mysql_old_password authentication\nplugins store passwords in the mysql.global_priv table.\n\nIf you run SET PASSWORD on an account that authenticates with one of these\nauthentication plugins that stores passwords in the mysql.global_priv table,\nthen the PASSWORD() function is evaluated by the specific authentication\nplugin used by the account. The authentication plugin hashes the password with\na method that is compatible with that specific authentication plugin.\n\nThe unix_socket, named_pipe, gssapi, and pam authentication plugins do not\nstore passwords in the mysql.global_priv table. These authentication plugins\nrely on other methods to authenticate the user.\n\nIf you attempt to run SET PASSWORD on an account that authenticates with one\nof these authentication plugins that doesn\'t store a password in the\nmysql.global_priv table, then MariaDB Server will raise a warning like the\nfollowing:\n\nSET PASSWORD is ignored for users authenticating via unix_socket plugin\n\nSee Authentication from MariaDB 10.4 for an overview of authentication changes\nin MariaDB 10.4.\n\nMariaDB until 10.3\n------------------\nIn MariaDB 10.3 and before, SET PASSWORD (with or without PASSWORD()) only\nworks for accounts authenticated via mysql_native_password or\nmysql_old_password authentication plugins\n\nPasswordless User Accounts\n--------------------------\n\nUser accounts do not always require passwords to login.\n\nThe unix_socket , named_pipe and gssapi authentication plugins do not require\na password to authenticate the user.\n\nThe pam authentication plugin may or may not require a password to\nauthenticate the user, depending on the specific configuration.\n\nThe mysql_native_password and mysql_old_password authentication plugins\nrequire passwords for authentication, but the password can be blank. In that\ncase, no password is required.\n\nIf you provide a password while attempting to log into the server as an\naccount that doesn\'t require a password, then MariaDB server will simply\nignore the password.\n\nMariaDB starting with 10.4\n--------------------------\nIn MariaDB 10.4 and later, a user account can be defined to use multiple\nauthentication plugins in a specific order of preference. This specific\nscenario may be more noticeable in these versions, since an account could be\nassociated with some authentication plugins that require a password, and some\nthat do not.\n\nExample\n-------\n\nFor example, if you had an entry with User and Host column values of \'bob\' and\n\'%.loc.gov\', you would write the statement like this:\n\nSET PASSWORD FOR \'bob\'@\'%.loc.gov\' = PASSWORD(\'newpass\');\n\nIf you want to delete a password for a user, you would do:\n\nSET PASSWORD FOR \'bob\'@localhost = PASSWORD(\"\");\n\nURL: https://mariadb.com/kb/en/set-password/','','https://mariadb.com/kb/en/set-password/'); @@ -332,10 +332,10 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (240,20,'~','Syntax\n------\n\n~\n\nDescription\n-----------\n\nBitwise NOT. Converts the value to 4 bytes binary and inverts all bits.\n\nExamples\n--------\n\nSELECT 3 & ~1;\n+--------+\n| 3 & ~1 |\n+--------+\n| 2 |\n+--------+\n\nSELECT 5 & ~1;\n+--------+\n| 5 & ~1 |\n+--------+\n| 4 |\n+--------+\n\nURL: https://mariadb.com/kb/en/bitwise-not/','','https://mariadb.com/kb/en/bitwise-not/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (241,20,'Parentheses','Parentheses are sometimes called precedence operators - this means that they\ncan be used to change the other operator\'s precedence in an expression. The\nexpressions that are written between parentheses are computed before the\nexpressions that are written outside. Parentheses must always contain an\nexpression (that is, they cannot be empty), and can be nested.\n\nFor example, the following expressions could return different results:\n\n* NOT a OR b\n* NOT (a OR b)\n\nIn the first case, NOT applies to a, so if a is FALSE or b is TRUE, the\nexpression returns TRUE. In the second case, NOT applies to the result of a OR\nb, so if at least one of a or b is TRUE, the expression is TRUE.\n\nWhen the precedence of operators is not intuitive, you can use parentheses to\nmake it immediately clear for whoever reads the statement.\n\nThe precedence of the NOT operator can also be affected by the\nHIGH_NOT_PRECEDENCE SQL_MODE flag.\n\nOther uses\n----------\n\nParentheses must always be used to enclose subqueries.\n\nParentheses can also be used in a JOIN statement between multiple tables to\ndetermine which tables must be joined first.\n\nAlso, parentheses are used to enclose the list of parameters to be passed to\nbuilt-in functions, user-defined functions and stored routines. However, when\nno parameter is passed to a stored procedure, parentheses are optional. For\nbuiltin functions and user-defined functions, spaces are not allowed between\nthe function name and the open parenthesis, unless the IGNORE_SPACE SQL_MODE\nis set. For stored routines (and for functions if IGNORE_SPACE is set) spaces\nare allowed before the open parenthesis, including tab characters and new line\ncharacters.\n\nSyntax errors\n-------------\n\nIf there are more open parentheses than closed parentheses, the error usually\nlooks like this:\n\nERROR 1064 (42000): You have an error in your SQL syntax; check the manual that\ncorresponds to your MariaDB server version for the right syntax to use near \'\'\na\nt line 1\n\nNote the empty string.\n\nIf there are more closed parentheses than open parentheses, the error usually\nlooks like this:\n\nERROR 1064 (42000): You have an error in your SQL syntax; check the manual that\ncorresponds to your MariaDB server version for the right syntax to use near \')\'\nat line 1\n\nNote the quoted closed parenthesis.\n\nURL: https://mariadb.com/kb/en/parentheses/','','https://mariadb.com/kb/en/parentheses/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (242,20,'TRUE FALSE','Description\n-----------\n\nThe constants TRUE and FALSE evaluate to 1 and 0, respectively. The constant\nnames can be written in any lettercase.\n\nExamples\n--------\n\nSELECT TRUE, true, FALSE, false;\n+------+------+-------+-------+\n| TRUE | TRUE | FALSE | FALSE |\n+------+------+-------+-------+\n| 1 | 1 | 0 | 0 |\n+------+------+-------+-------+\n\nURL: https://mariadb.com/kb/en/true-false/','','https://mariadb.com/kb/en/true-false/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (243,21,'ANALYZE TABLE','Syntax\n------\n\nANALYZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name [,tbl_name ...] \n [PERSISTENT FOR [ALL|COLUMNS ([col_name [,col_name ...]])]\n [INDEXES ([index_name [,index_name ...]])]]\n\nDescription\n-----------\n\nANALYZE TABLE analyzes and stores the key distribution for a table (index\nstatistics). This statement works with MyISAM, Aria and InnoDB tables. During\nthe analysis, InnoDB will allow reads/writes, and MyISAM/Aria reads/inserts.\nFor MyISAM tables, this statement is equivalent to using myisamchk --analyze.\n\nFor more information on how the analysis works within InnoDB, see InnoDB\nLimitations.\n\nMariaDB uses the stored key distribution to decide the order in which tables\nshould be joined when you perform a join on something other than a constant.\nIn addition, key distributions can be used when deciding which indexes to use\nfor a specific table within a query.\n\nThis statement requires SELECT and INSERT privileges for the table.\n\nBy default, ANALYZE TABLE statements are written to the binary log and will be\nreplicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure the\nstatement is not written to the binary log.\n\nFrom MariaDB 10.3.19, ANALYZE TABLE statements are not logged to the binary\nlog if read_only is set. See also Read-Only Replicas.\n\nANALYZE TABLE is also supported for partitioned tables. You can use ALTER\nTABLE ... ANALYZE PARTITION to analyze one or more partitions.\n\nThe Aria storage engine supports progress reporting for the ANALYZE TABLE\nstatement.\n\nEngine-Independent Statistics\n-----------------------------\n\nANALYZE TABLE supports engine-independent statistics. See Engine-Independent\nTable Statistics: Collecting Statistics with the ANALYZE TABLE Statement for\nmore information.\n\nURL: https://mariadb.com/kb/en/analyze-table/','','https://mariadb.com/kb/en/analyze-table/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (244,21,'CHECK TABLE','Syntax\n------\n\nCHECK TABLE tbl_name [, tbl_name] ... [option] ...\n\noption = {FOR UPGRADE | QUICK | FAST | MEDIUM | EXTENDED | CHANGED}\n\nDescription\n-----------\n\nCHECK TABLE checks a table or tables for errors. CHECK TABLE works for\nArchive, Aria, CSV, InnoDB, and MyISAM tables. For Aria and MyISAM tables, the\nkey statistics are updated as well. For CSV, see also Checking and Repairing\nCSV Tables.\n\nAs an alternative, myisamchk is a commandline tool for checking MyISAM tables\nwhen the tables are not being accessed.\n\nFor checking dynamic columns integrity, COLUMN_CHECK() can be used.\n\nCHECK TABLE can also check views for problems, such as tables that are\nreferenced in the view definition that no longer exist.\n\nCHECK TABLE is also supported for partitioned tables. You can use ALTER TABLE\n... CHECK PARTITION to check one or more partitions.\n\nThe meaning of the different options are as follows - note that this can vary\na bit between storage engines:\n\n+-----+----------------------------------------------------------------------+\n| FOR | Do a very quick check if the storage format for the table has |\n| UPG | changed so that one needs to do a REPAIR. This is only needed when |\n| ADE | one upgrades between major versions of MariaDB or MySQL. This is |\n| | usually done by running mysql_upgrade. |\n+-----+----------------------------------------------------------------------+\n| FAS | Only check tables that has not been closed properly or are marked |\n| | as corrupt. Only supported by the MyISAM and Aria engines. For |\n| | other engines the table is checked normally |\n+-----+----------------------------------------------------------------------+\n| CHA | Check only tables that has changed since last REPAIR / CHECK. Only |\n| GED | supported by the MyISAM and Aria engines. For other engines the |\n| | table is checked normally. |\n+-----+----------------------------------------------------------------------+\n| QUI | Do a fast check. For MyISAM and Aria engine this means we skip |\n| K | checking the delete link chain which may take some time. |\n+-----+----------------------------------------------------------------------+\n| MED | Scan also the data files. Checks integrity between data and index |\n| UM | files with checksums. In most cases this should find all possible |\n| | errors. |\n+-----+----------------------------------------------------------------------+\n| EXT | Does a full check to verify every possible error. For MyISAM and |\n| NDE | Aria we verify for each row that all it keys exists and points to |\n| | the row. This may take a long time on big tables! |\n+-----+----------------------------------------------------------------------+\n\nFor most cases running CHECK TABLE without options or MEDIUM should be good\nenough.\n\nThe Aria storage engine supports progress reporting for this statement.\n\nIf you want to know if two tables are identical, take a look at CHECKSUM TABLE.\n\nInnoDB\n------\n\nIf CHECK TABLE finds an error in an InnoDB table, MariaDB might shutdown to\nprevent the error propagation. In this case, the problem will be reported in\nthe error log. Otherwise the table or an index might be marked as corrupted,\nto prevent use. This does not happen with some minor problems, like a wrong\nnumber of entries in a secondary index. Those problems are reported in the\noutput of CHECK TABLE.\n\nEach tablespace contains a header with metadata. This header is not checked by\nthis statement.\n\nDuring the execution of CHECK TABLE, other threads may be blocked.\n\nURL: https://mariadb.com/kb/en/check-table/','','https://mariadb.com/kb/en/check-table/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (243,21,'ANALYZE TABLE','Syntax\n------\n\nANALYZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name [,tbl_name ...] \n [PERSISTENT FOR [ALL|COLUMNS ([col_name [,col_name ...]])]\n [INDEXES ([index_name [,index_name ...]])]]\n\nDescription\n-----------\n\nANALYZE TABLE analyzes and stores the key distribution for a table (index\nstatistics). This statement works with MyISAM, Aria and InnoDB tables. During\nthe analysis, InnoDB will allow reads/writes, and MyISAM/Aria reads/inserts.\nFor MyISAM tables, this statement is equivalent to using myisamchk --analyze.\n\nFor more information on how the analysis works within InnoDB, see InnoDB\nLimitations.\n\nMariaDB uses the stored key distribution to decide the order in which tables\nshould be joined when you perform a join on something other than a constant.\nIn addition, key distributions can be used when deciding which indexes to use\nfor a specific table within a query.\n\nThis statement requires SELECT and INSERT privileges for the table.\n\nBy default, ANALYZE TABLE statements are written to the binary log and will be\nreplicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure the\nstatement is not written to the binary log.\n\nFrom MariaDB 10.3.19, ANALYZE TABLE statements are not logged to the binary\nlog if read_only is set. See also Read-Only Replicas.\n\nANALYZE TABLE is also supported for partitioned tables. You can use ALTER\nTABLE ... ANALYZE PARTITION to analyze one or more partitions.\n\nThe Aria storage engine supports progress reporting for the ANALYZE TABLE\nstatement.\n\nEngine-Independent Statistics\n-----------------------------\n\nANALYZE TABLE supports engine-independent statistics. See Engine-Independent\nTable Statistics: Collecting Statistics with the ANALYZE TABLE Statement for\nmore information.\n\nUseful Variables\n----------------\n\nFor calculating the number of duplicates, ANALYZE TABLE uses a buffer of\nsort_buffer_size bytes per column. You can slightly increase the speed of\nANALYZE TABLE by increasing this variable.\n\nURL: https://mariadb.com/kb/en/analyze-table/','','https://mariadb.com/kb/en/analyze-table/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (244,21,'CHECK TABLE','Syntax\n------\n\nCHECK TABLE tbl_name [, tbl_name] ... [option] ...\n\noption = {FOR UPGRADE | QUICK | FAST | MEDIUM | EXTENDED | CHANGED}\n\nDescription\n-----------\n\nCHECK TABLE checks a table or tables for errors. CHECK TABLE works for\nArchive, Aria, CSV, InnoDB and MyISAM tables. For Aria and MyISAM tables, the\nkey statistics are updated as well. For CSV, see also Checking and Repairing\nCSV Tables.\n\nAs an alternative, myisamchk is a commandline tool for checking MyISAM tables\nwhen the tables are not being accessed. For Aria tables, there is a similar\ntool: aria_chk.\n\nFor checking dynamic columns integrity, COLUMN_CHECK() can be used.\n\nCHECK TABLE can also check views for problems, such as tables that are\nreferenced in the view definition that no longer exist.\n\nCHECK TABLE is also supported for partitioned tables. You can use ALTER TABLE\n... CHECK PARTITION to check one or more partitions.\n\nThe meaning of the different options are as follows - note that this can vary\na bit between storage engines:\n\n+-----+----------------------------------------------------------------------+\n| FOR | Do a very quick check if the storage format for the table has |\n| UPG | changed so that one needs to do a REPAIR. This is only needed when |\n| ADE | one upgrades between major versions of MariaDB or MySQL. This is |\n| | usually done by running mysql_upgrade. |\n+-----+----------------------------------------------------------------------+\n| FAS | Only check tables that has not been closed properly or are marked |\n| | as corrupt. Only supported by the MyISAM and Aria engines. For |\n| | other engines the table is checked normally |\n+-----+----------------------------------------------------------------------+\n| CHA | Check only tables that has changed since last REPAIR / CHECK. Only |\n| GED | supported by the MyISAM and Aria engines. For other engines the |\n| | table is checked normally. |\n+-----+----------------------------------------------------------------------+\n| QUI | Do a fast check. For MyISAM and Aria, this means skipping the check |\n| K | of the delete link chain, which may take some time. |\n+-----+----------------------------------------------------------------------+\n| MED | Scan also the data files. Checks integrity between data and index |\n| UM | files with checksums. In most cases this should find all possible |\n| | errors. |\n+-----+----------------------------------------------------------------------+\n| EXT | Does a full check to verify every possible error. For MyISAM and |\n| NDE | Aria, verify for each row that all it keys exists and points to the |\n| | row. This may take a long time on large tables. Ignored by InnoDB |\n| | before MariaDB 10.6.11, MariaDB 10.7.7, MariaDB 10.8.6 and MariaDB |\n| | 10.9.4. |\n+-----+----------------------------------------------------------------------+\n\nFor most cases running CHECK TABLE without options or MEDIUM should be good\nenough.\n\nThe Aria storage engine supports progress reporting for this statement.\n\nIf you want to know if two tables are identical, take a look at CHECKSUM TABLE.\n\nInnoDB\n------\n\nIf CHECK TABLE finds an error in an InnoDB table, MariaDB might shutdown to\nprevent the error propagation. In this case, the problem will be reported in\nthe error log. Otherwise the table or an index might be marked as corrupted,\nto prevent use. This does not happen with some minor problems, like a wrong\nnumber of entries in a secondary index. Those problems are reported in the\noutput of CHECK TABLE.\n\nEach tablespace contains a header with metadata. This header is not checked by\nthis statement.\n\nDuring the execution of CHECK TABLE, other threads may be blocked.\n\nURL: https://mariadb.com/kb/en/check-table/','','https://mariadb.com/kb/en/check-table/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (245,21,'CHECK VIEW','Syntax\n------\n\nCHECK VIEW view_name\n\nDescription\n-----------\n\nThe CHECK VIEW statement was introduced in MariaDB 10.0.18 to assist with\nfixing MDEV-6916, an issue introduced in MariaDB 5.2 where the view algorithms\nwere swapped. It checks whether the view algorithm is correct. It is run as\npart of mysql_upgrade, and should not normally be required in regular use.\n\nURL: https://mariadb.com/kb/en/check-view/','','https://mariadb.com/kb/en/check-view/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (246,21,'CHECKSUM TABLE','Syntax\n------\n\nCHECKSUM TABLE tbl_name [, tbl_name] ... [ QUICK | EXTENDED ]\n\nDescription\n-----------\n\nCHECKSUM TABLE reports a table checksum. This is very useful if you want to\nknow if two tables are the same (for example on a master and slave).\n\nWith QUICK, the live table checksum is reported if it is available, or NULL\notherwise. This is very fast. A live checksum is enabled by specifying the\nCHECKSUM=1 table option when you create the table; currently, this is\nsupported only for Aria and MyISAM tables.\n\nWith EXTENDED, the entire table is read row by row and the checksum is\ncalculated. This can be very slow for large tables.\n\nIf neither QUICK nor EXTENDED is specified, MariaDB returns a live checksum if\nthe table storage engine supports it and scans the table otherwise.\n\nCHECKSUM TABLE requires the SELECT privilege for the table.\n\nFor a nonexistent table, CHECKSUM TABLE returns NULL and generates a warning.\n\nThe table row format affects the checksum value. If the row format changes,\nthe checksum will change. This means that when a table created with a\nMariaDB/MySQL version is upgraded to another version, the checksum value will\nprobably change.\n\nTwo identical tables should always match to the same checksum value; however,\nalso for non-identical tables there is a very slight chance that they will\nreturn the same value as the hashing algorithm is not completely\ncollision-free.\n\nDifferences Between MariaDB and MySQL\n-------------------------------------\n\nCHECKSUM TABLE may give a different result as MariaDB doesn\'t ignore NULLs in\nthe columns as MySQL 5.1 does (Later MySQL versions should calculate checksums\nthe same way as MariaDB). You can get the \'old style\' checksum in MariaDB by\nstarting mysqld with the --old option. Note however that that the MyISAM and\nAria storage engines in MariaDB are using the new checksum internally, so if\nyou are using --old, the CHECKSUM command will be slower as it needs to\ncalculate the checksum row by row. Starting from MariaDB Server 10.9, --old is\ndeprecated and will be removed in a future release. Set --old-mode or OLD_MODE\nto COMPAT_5_1_CHECKSUM to get \'old style\' checksum.\n\nURL: https://mariadb.com/kb/en/checksum-table/','','https://mariadb.com/kb/en/checksum-table/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (246,21,'CHECKSUM TABLE','Syntax\n------\n\nCHECKSUM TABLE tbl_name [, tbl_name] ... [ QUICK | EXTENDED ]\n\nDescription\n-----------\n\nCHECKSUM TABLE reports a table checksum. This is very useful if you want to\nknow if two tables are the same (for example on a master and slave).\n\nWith QUICK, the live table checksum is reported if it is available, or NULL\notherwise. This is very fast. A live checksum is enabled by specifying the\nCHECKSUM=1 table option when you create the table; currently, this is\nsupported only for Aria and MyISAM tables.\n\nWith EXTENDED, the entire table is read row by row and the checksum is\ncalculated. This can be very slow for large tables.\n\nIf neither QUICK nor EXTENDED is specified, MariaDB returns a live checksum if\nthe table storage engine supports it and scans the table otherwise.\n\nCHECKSUM TABLE requires the SELECT privilege for the table.\n\nFor a nonexistent table, CHECKSUM TABLE returns NULL and generates a warning.\n\nThe table row format affects the checksum value. If the row format changes,\nthe checksum will change. This means that when a table created with a\nMariaDB/MySQL version is upgraded to another version, the checksum value will\nprobably change.\n\nTwo identical tables should always match to the same checksum value; however,\nalso for non-identical tables there is a very slight chance that they will\nreturn the same value as the hashing algorithm is not completely\ncollision-free.\n\nIdentical Tables\n----------------\n\nIdentical tables mean that the CREATE statement is identical and that the\nfollowing variable, which affects the storage formats, was the same when the\ntables were created:\n\n* mysql56-temporal-format\n\nDifferences Between MariaDB and MySQL\n-------------------------------------\n\nCHECKSUM TABLE may give a different result as MariaDB doesn\'t ignore NULLs in\nthe columns as MySQL 5.1 does (Later MySQL versions should calculate checksums\nthe same way as MariaDB). You can get the \'old style\' checksum in MariaDB by\nstarting mysqld with the --old option. Note however that that the MyISAM and\nAria storage engines in MariaDB are using the new checksum internally, so if\nyou are using --old, the CHECKSUM command will be slower as it needs to\ncalculate the checksum row by row. Starting from MariaDB Server 10.9, --old is\ndeprecated and will be removed in a future release. Set --old-mode or OLD_MODE\nto COMPAT_5_1_CHECKSUM to get \'old style\' checksum.\n\nURL: https://mariadb.com/kb/en/checksum-table/','','https://mariadb.com/kb/en/checksum-table/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (247,21,'OPTIMIZE TABLE','Syntax\n------\n\nOPTIMIZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE\n tbl_name [, tbl_name] ...\n [WAIT n | NOWAIT]\n\nDescription\n-----------\n\nOPTIMIZE TABLE has two main functions. It can either be used to defragment\ntables, or to update the InnoDB fulltext index.\n\nMariaDB starting with 10.3.0\n----------------------------\n\nWAIT/NOWAIT\n-----------\n\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nDefragmenting\n-------------\n\nOPTIMIZE TABLE works for InnoDB (before MariaDB 10.1.1, only if the\ninnodb_file_per_table server system variable is set), Aria, MyISAM and ARCHIVE\ntables, and should be used if you have deleted a large part of a table or if\nyou have made many changes to a table with variable-length rows (tables that\nhave VARCHAR, VARBINARY, BLOB, or TEXT columns). Deleted rows are maintained\nin a linked list and subsequent INSERT operations reuse old row positions.\n\nThis statement requires SELECT and INSERT privileges for the table.\n\nBy default, OPTIMIZE TABLE statements are written to the binary log and will\nbe replicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure\nthe statement is not written to the binary log.\n\nFrom MariaDB 10.3.19, OPTIMIZE TABLE statements are not logged to the binary\nlog if read_only is set. See also Read-Only Replicas.\n\nOPTIMIZE TABLE is also supported for partitioned tables. You can use ALTER\nTABLE ... OPTIMIZE PARTITION to optimize one or more partitions.\n\nYou can use OPTIMIZE TABLE to reclaim the unused space and to defragment the\ndata file. With other storage engines, OPTIMIZE TABLE does nothing by default,\nand returns this message: \" The storage engine for the table doesn\'t support\noptimize\". However, if the server has been started with the --skip-new option,\nOPTIMIZE TABLE is linked to ALTER TABLE, and recreates the table. This\noperation frees the unused space and updates index statistics.\n\nThe Aria storage engine supports progress reporting for this statement.\n\nIf a MyISAM table is fragmented, concurrent inserts will not be performed\nuntil an OPTIMIZE TABLE statement is executed on that table, unless the\nconcurrent_insert server system variable is set to ALWAYS.\n\nUpdating an InnoDB fulltext index\n---------------------------------\n\nWhen rows are added or deleted to an InnoDB fulltext index, the index is not\nimmediately re-organized, as this can be an expensive operation. Change\nstatistics are stored in a separate location . The fulltext index is only\nfully re-organized when an OPTIMIZE TABLE statement is run.\n\nBy default, an OPTIMIZE TABLE will defragment a table. In order to use it to\nupdate fulltext index statistics, the innodb_optimize_fulltext_only system\nvariable must be set to 1. This is intended to be a temporary setting, and\nshould be reset to 0 once the fulltext index has been re-organized.\n\nSince fulltext re-organization can take a long time, the\ninnodb_ft_num_word_optimize variable limits the re-organization to a number of\nwords (2000 by default). You can run multiple OPTIMIZE statements to fully\nre-organize the index.\n\nDefragmenting InnoDB tablespaces\n--------------------------------\n\nMariaDB 10.1.1 merged the Facebook/Kakao defragmentation patch, allowing one\nto use OPTIMIZE TABLE to defragment InnoDB tablespaces. For this functionality\nto be enabled, the innodb_defragment system variable must be enabled. No new\ntables are created and there is no need to copy data from old tables to new\ntables. Instead, this feature loads n pages (determined by\ninnodb-defragment-n-pages) and tries to move records so that pages would be\nfull of records and then frees pages that are fully empty after the operation.\nNote that tablespace files (including ibdata1) will not shrink as the result\nof defragmentation, but one will get better memory utilization in the InnoDB\nbuffer pool as there are fewer data pages in use.\n\nSee Defragmenting InnoDB Tablespaces for more details.\n\nURL: https://mariadb.com/kb/en/optimize-table/','','https://mariadb.com/kb/en/optimize-table/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (248,21,'REPAIR TABLE','Syntax\n------\n\nREPAIR [NO_WRITE_TO_BINLOG | LOCAL] TABLE\n tbl_name [, tbl_name] ...\n [QUICK] [EXTENDED] [USE_FRM]\n\nDescription\n-----------\n\nREPAIR TABLE repairs a possibly corrupted table. By default, it has the same\neffect as\n\nmyisamchk --recover tbl_name\n\nor\n\naria_chk --recover tbl_name\n\nSee aria_chk and myisamchk for more.\n\nREPAIR TABLE works for Archive, Aria, CSV and MyISAM tables. For InnoDB, see\nrecovery modes. For CSV, see also Checking and Repairing CSV Tables. For\nArchive, this statement also improves compression. If the storage engine does\nnot support this statement, a warning is issued.\n\nThis statement requires SELECT and INSERT privileges for the table.\n\nBy default, REPAIR TABLE statements are written to the binary log and will be\nreplicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure the\nstatement is not written to the binary log.\n\nFrom MariaDB 10.3.19, REPAIR TABLE statements are not logged to the binary log\nif read_only is set. See also Read-Only Replicas.\n\nWhen an index is recreated, the storage engine may use a configurable buffer\nin the process. Incrementing the buffer speeds up the index creation. Aria and\nMyISAM allocate a buffer whose size is defined by aria_sort_buffer_size or\nmyisam_sort_buffer_size, also used for ALTER TABLE.\n\nREPAIR TABLE is also supported for partitioned tables. However, the USE_FRM\noption cannot be used with this statement on a partitioned table.\n\nALTER TABLE ... REPAIR PARTITION can be used to repair one or more partitions.\n\nThe Aria storage engine supports progress reporting for this statement.\n\nURL: https://mariadb.com/kb/en/repair-table/','','https://mariadb.com/kb/en/repair-table/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (249,21,'REPAIR VIEW','Syntax\n------\n\nREPAIR [NO_WRITE_TO_BINLOG | LOCAL] VIEW view_name[, view_name] ... [FROM\nMYSQL]\n\nDescription\n-----------\n\nThe REPAIR VIEW statement was introduced to assist with fixing MDEV-6916, an\nissue introduced in MariaDB 5.2 where the view algorithms were swapped\ncompared to their MySQL on disk representation. It checks whether the view\nalgorithm is correct. It is run as part of mysql_upgrade, and should not\nnormally be required in regular use.\n\nBy default it corrects the checksum and if necessary adds the mariadb-version\nfield. If the optional FROM MYSQL clause is used, and no mariadb-version field\nis present, the MERGE and TEMPTABLE algorithms are toggled.\n\nBy default, REPAIR VIEW statements are written to the binary log and will be\nreplicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure the\nstatement is not written to the binary log.\n\nURL: https://mariadb.com/kb/en/repair-view/','','https://mariadb.com/kb/en/repair-view/'); @@ -399,7 +399,7 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (306,24,'REPEAT LOOP','Syntax\n------\n\n[begin_label:] REPEAT\n statement_list\nUNTIL search_condition\nEND REPEAT [end_label]\n\nThe statement list within a REPEAT statement is repeated until the\nsearch_condition is true. Thus, a REPEAT always enters the loop at least once.\nstatement_list consists of one or more statements, each terminated by a\nsemicolon (i.e., ;) statement delimiter.\n\nA REPEAT statement can be labeled. end_label cannot be given unless\nbegin_label also is present. If both are present, they must be the same.\n\nSee Delimiters in the mysql client for more on client delimiter usage.\n\nDELIMITER //\n\nCREATE PROCEDURE dorepeat(p1 INT)\n BEGIN\n SET @x = 0;\n REPEAT SET @x = @x + 1; UNTIL @x > p1 END REPEAT;\n END\n//\n\nCALL dorepeat(1000)//\n\nSELECT @x//\n+------+\n| @x |\n+------+\n| 1001 |\n+------+\n\nURL: https://mariadb.com/kb/en/repeat-loop/','','https://mariadb.com/kb/en/repeat-loop/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (307,24,'RESIGNAL','Syntax\n------\n\nRESIGNAL [error_condition]\n [SET error_property\n [, error_property] ...]\n\nerror_condition:\n SQLSTATE [VALUE] \'sqlstate_value\'\n | condition_name\n\nerror_property:\n error_property_name = \n\nerror_property_name:\n CLASS_ORIGIN\n | SUBCLASS_ORIGIN\n | MESSAGE_TEXT\n | MYSQL_ERRNO\n | CONSTRAINT_CATALOG\n | CONSTRAINT_SCHEMA\n | CONSTRAINT_NAME\n | CATALOG_NAME\n | SCHEMA_NAME\n | TABLE_NAME\n | COLUMN_NAME\n | CURSOR_NAME\n\nDescription\n-----------\n\nThe syntax of RESIGNAL and its semantics are very similar to SIGNAL. This\nstatement can only be used within an error HANDLER. It produces an error, like\nSIGNAL. RESIGNAL clauses are the same as SIGNAL, except that they all are\noptional, even SQLSTATE. All the properties which are not specified in\nRESIGNAL, will be identical to the properties of the error that was received\nby the error HANDLER. For a description of the clauses, see diagnostics area.\n\nNote that RESIGNAL does not empty the diagnostics area: it just appends\nanother error condition.\n\nRESIGNAL, without any clauses, produces an error which is identical to the\nerror that was received by HANDLER.\n\nIf used out of a HANDLER construct, RESIGNAL produces the following error:\n\nERROR 1645 (0K000): RESIGNAL when handler not active\n\nIn MariaDB 5.5, if a HANDLER contained a CALL to another procedure, that\nprocedure could use RESIGNAL. Since MariaDB 10.0, trying to do this raises the\nabove error.\n\nFor a list of SQLSTATE values and MariaDB error codes, see MariaDB Error Codes.\n\nThe following procedure tries to query two tables which don\'t exist, producing\na 1146 error in both cases. Those errors will trigger the HANDLER. The first\ntime the error will be ignored and the client will not receive it, but the\nsecond time, the error is re-signaled, so the client will receive it.\n\nCREATE PROCEDURE test_error( )\nBEGIN\n DECLARE CONTINUE HANDLER\n FOR 1146\n BEGIN\n IF @hide_errors IS FALSE THEN\n RESIGNAL;\n END IF;\n END;\n SET @hide_errors = TRUE;\n SELECT \'Next error will be ignored\' AS msg;\n SELECT `c` FROM `temptab_one`;\n SELECT \'Next error won\'\'t be ignored\' AS msg;\n SET @hide_errors = FALSE;\n SELECT `c` FROM `temptab_two`;\nEND;\n\nCALL test_error( );\n\n+----------------------------+\n| msg |\n+----------------------------+\n| Next error will be ignored |\n+----------------------------+\n\n+-----------------------------+\n| msg |\n+-----------------------------+\n| Next error won\'t be ignored |\n+-----------------------------+\n\nERROR 1146 (42S02): Table \'test.temptab_two\' doesn\'t exist\n\nThe following procedure re-signals an error, modifying only the error message\nto clarify the cause of the problem.\n\nCREATE PROCEDURE test_error()\nBEGIN\n DECLARE CONTINUE HANDLER\n FOR 1146\n BEGIN\n RESIGNAL SET\n MESSAGE_TEXT = \'`temptab` does not exist\';\n END;\n SELECT `c` FROM `temptab`;\nEND;\n\nCALL test_error( );\nERROR 1146 (42S02): `temptab` does not exist\n\nAs explained above, this works on MariaDB 5.5, but produces a 1645 error since\n10.0.\n\nCREATE PROCEDURE handle_error()\nBEGIN\n RESIGNAL;\nEND;\nCREATE PROCEDURE p()\nBEGIN\n DECLARE EXIT HANDLER FOR SQLEXCEPTION CALL p();\n SIGNAL SQLSTATE \'45000\';\nEND;\n\nURL: https://mariadb.com/kb/en/resignal/','','https://mariadb.com/kb/en/resignal/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (308,24,'RETURN','Syntax\n------\n\nRETURN expr\n\nThe RETURN statement terminates execution of a stored function and returns the\nvalue expr to the function caller. There must be at least one RETURN statement\nin a stored function. If the function has multiple exit points, all exit\npoints must have a RETURN.\n\nThis statement is not used in stored procedures, triggers, or events. LEAVE\ncan be used instead.\n\nThe following example shows that RETURN can return the result of a scalar\nsubquery:\n\nCREATE FUNCTION users_count() RETURNS BOOL\n READS SQL DATA\nBEGIN\n RETURN (SELECT COUNT(DISTINCT User) FROM mysql.user);\nEND;\n\nURL: https://mariadb.com/kb/en/return/','','https://mariadb.com/kb/en/return/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (309,24,'SELECT INTO','Syntax\n------\n\nSELECT col_name [, col_name] ...\n INTO var_name [, var_name] ...\n table_expr\n\nDescription\n-----------\n\nSELECT ... INTO enables selected columns to be stored directly into variables.\nNo resultset is produced. The query should return a single row. If the query\nreturns no rows, a warning with error code 1329 occurs (No data), and the\nvariable values remain unchanged. If the query returns multiple rows, error\n1172 occurs (Result consisted of more than one row). If it is possible that\nthe statement may retrieve multiple rows, you can use LIMIT 1 to limit the\nresult set to a single row.\n\nThe INTO clause can also be specified at the end of the statement.\n\nIn the context of such statements that occur as part of events executed by the\nEvent Scheduler, diagnostics messages (not only errors, but also warnings) are\nwritten to the error log, and, on Windows, to the application event log.\n\nThis statement can be used with both local variables and user-defined\nvariables.\n\nFor the complete syntax, see SELECT.\n\nAnother way to set a variable\'s value is the SET statement.\n\nSELECT ... INTO results are not stored in the query cache even if SQL_CACHE is\nspecified.\n\nExamples\n--------\n\nSELECT id, data INTO @x,@y \nFROM test.t1 LIMIT 1;\n\nURL: https://mariadb.com/kb/en/selectinto/','','https://mariadb.com/kb/en/selectinto/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (309,24,'SELECT INTO','Syntax\n------\n\nSELECT col_name [, col_name] ...\n INTO var_name [, var_name] ...\n table_expr\n\nDescription\n-----------\n\nSELECT ... INTO enables selected columns to be stored directly into variables.\nNo resultset is produced. The query should return a single row. If the query\nreturns no rows, a warning with error code 1329 occurs (No data), and the\nvariable values remain unchanged. If the query returns multiple rows, error\n1172 occurs (Result consisted of more than one row). If it is possible that\nthe statement may retrieve multiple rows, you can use LIMIT 1 to limit the\nresult set to a single row.\n\nThe INTO clause can also be specified at the end of the statement.\n\nIn the context of such statements that occur as part of events executed by the\nEvent Scheduler, diagnostics messages (not only errors, but also warnings) are\nwritten to the error log, and, on Windows, to the application event log.\n\nThis statement can be used with both local variables and user-defined\nvariables.\n\nFor the complete syntax, see SELECT.\n\nAnother way to set a variable\'s value is the SET statement.\n\nSELECT ... INTO results are not stored in the query cache even if SQL_CACHE is\nspecified.\n\nExamples\n--------\n\nSELECT id, data INTO @x,@y \nFROM test.t1 LIMIT 1;\nSELECT * from t1 where t1.a=@x and t1.b=@y\n\nIf you want to use this construct with UNION you have to use the syntax:\n\nSELECT * INTO @x FROM (SELECT t1.a FROM t1 UNION SELECT t2.a FROM t2);\n\nURL: https://mariadb.com/kb/en/selectinto/','','https://mariadb.com/kb/en/selectinto/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (310,24,'SIGNAL','Syntax\n------\n\nSIGNAL error_condition\n [SET error_property\n [, error_property] ...]\n\nerror_condition:\n SQLSTATE [VALUE] \'sqlstate_value\'\n | condition_name\n\nerror_property:\n error_property_name = \n\nerror_property_name:\n CLASS_ORIGIN\n | SUBCLASS_ORIGIN\n | MESSAGE_TEXT\n | MYSQL_ERRNO\n | CONSTRAINT_CATALOG\n | CONSTRAINT_SCHEMA\n | CONSTRAINT_NAME\n | CATALOG_NAME\n | SCHEMA_NAME\n | TABLE_NAME\n | COLUMN_NAME\n | CURSOR_NAME\n\nSIGNAL empties the diagnostics area and produces a custom error. This\nstatement can be used anywhere, but is generally useful when used inside a\nstored program. When the error is produced, it can be caught by a HANDLER. If\nnot, the current stored program, or the current statement, will terminate with\nthe specified error.\n\nSometimes an error HANDLER just needs to SIGNAL the same error it received,\noptionally with some changes. Usually the RESIGNAL statement is the most\nconvenient way to do this.\n\nerror_condition can be an SQLSTATE value or a named error condition defined\nvia DECLARE CONDITION. SQLSTATE must be a constant string consisting of five\ncharacters. These codes are standard to ODBC and ANSI SQL. For customized\nerrors, the recommended SQLSTATE is \'45000\'. For a list of SQLSTATE values\nused by MariaDB, see the MariaDB Error Codes page. The SQLSTATE can be read\nvia the API method mysql_sqlstate( ).\n\nTo specify error properties user-defined variables and local variables can be\nused, as well as character set conversions (but you can\'t set a collation).\n\nThe error properties, their type and their default values are explained in the\ndiagnostics area page.\n\nErrors\n------\n\nIf the SQLSTATE is not valid, the following error like this will be produced:\n\nERROR 1407 (42000): Bad SQLSTATE: \'123456\'\n\nIf a property is specified more than once, an error like this will be produced:\n\nERROR 1641 (42000): Duplicate condition information item \'MESSAGE_TEXT\'\n\nIf you specify a condition name which is not declared, an error like this will\nbe produced:\n\nERROR 1319 (42000): Undefined CONDITION: cond_name\n\nIf MYSQL_ERRNO is out of range, you will get an error like this:\n\nERROR 1231 (42000): Variable \'MYSQL_ERRNO\' can\'t be set to the value of \'0\'\n\nExamples\n--------\n\nHere\'s what happens if SIGNAL is used in the client to generate errors:\n\nSIGNAL SQLSTATE \'01000\';\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n\n+---------+------+------------------------------------------+\n| Level | Code | Message |\n+---------+------+------------------------------------------+\n| Warning | 1642 | Unhandled user-defined warning condition |\n+---------+------+------------------------------------------+\n1 row in set (0.06 sec)\n\nSIGNAL SQLSTATE \'02000\';\nERROR 1643 (02000): Unhandled user-defined not found condition\n\nHow to specify MYSQL_ERRNO and MESSAGE_TEXT properties:\n\nSIGNAL SQLSTATE \'45000\' SET MYSQL_ERRNO=30001, MESSAGE_TEXT=\'H\nello, world!\';\n\nERROR 30001 (45000): Hello, world!\n\nThe following code shows how to use user variables, local variables and\ncharacter set conversion with SIGNAL:\n\nCREATE PROCEDURE test_error(x INT)\nBEGIN\n DECLARE errno SMALLINT UNSIGNED DEFAULT 31001;\n SET @errmsg = \'Hello, world!\';\n IF x = 1 THEN\n SIGNAL SQLSTATE \'45000\' SET\n MYSQL_ERRNO = errno,\n MESSAGE_TEXT = @errmsg;\n ELSE\n SIGNAL SQLSTATE \'45000\' SET\n MYSQL_ERRNO = errno,\n MESSAGE_TEXT = _utf8\'Hello, world!\';\n END IF;\nEND;\n\nHow to use named error conditions:\n\nCREATE PROCEDURE test_error(n INT)\nBEGIN\n DECLARE `too_big` CONDITION FOR SQLSTATE \'45000\';\n IF n > 10 THEN\n SIGNAL `too_big`;\n END IF;\nEND;\n\nIn this example, we\'ll define a HANDLER for an error code. When the error\noccurs, we SIGNAL a more informative error which makes sense for our procedure:\n\nCREATE PROCEDURE test_error()\nBEGIN\n DECLARE EXIT HANDLER\n FOR 1146\n BEGIN\n SIGNAL SQLSTATE \'45000\' SET\n MESSAGE_TEXT = \'Temporary tables not found; did you call init()\nprocedure?\';\n END;\n -- this will produce a 1146 error\n SELECT `c` FROM `temptab`;\nEND;\n\nURL: https://mariadb.com/kb/en/signal/','','https://mariadb.com/kb/en/signal/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (311,24,'WHILE','Syntax\n------\n\n[begin_label:] WHILE search_condition DO\n statement_list\nEND WHILE [end_label]\n\nDescription\n-----------\n\nThe statement list within a WHILE statement is repeated as long as the\nsearch_condition is true. statement_list consists of one or more statements.\nIf the loop must be executed at least once, REPEAT ... LOOP can be used\ninstead.\n\nA WHILE statement can be labeled. end_label cannot be given unless begin_label\nalso is present. If both are present, they must be the same.\n\nExamples\n--------\n\nCREATE PROCEDURE dowhile()\nBEGIN\n DECLARE v1 INT DEFAULT 5;\n\nWHILE v1 > 0 DO\n ...\n SET v1 = v1 - 1;\n END WHILE;\nEND\n\nURL: https://mariadb.com/kb/en/while/','','https://mariadb.com/kb/en/while/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (312,24,'Cursor Overview','Description\n-----------\n\nA cursor is a structure that allows you to go over records sequentially, and\nperform processing based on the result.\n\nMariaDB permits cursors inside stored programs, and MariaDB cursors are\nnon-scrollable, read-only and asensitive.\n\n* Non-scrollable means that the rows can only be fetched in the order\nspecified by the SELECT statement. Rows cannot be skipped, you cannot jump to\na specific row, and you cannot fetch rows in reverse order.\n* Read-only means that data cannot be updated through the cursor.\n* Asensitive means that the cursor points to the actual underlying data. This\nkind of cursor is quicker than the alternative, an insensitive cursor, as no\ndata is copied to a temporary table. However, changes to the data being used\nby the cursor will affect the cursor data.\n\nCursors are created with a DECLARE CURSOR statement and opened with an OPEN\nstatement. Rows are read with a FETCH statement before the cursor is finally\nclosed with a CLOSE statement.\n\nWhen FETCH is issued and there are no more rows to extract, the following\nerror is produced:\n\nERROR 1329 (02000): No data - zero rows fetched, selected, or processed\n\nTo avoid problems, a DECLARE HANDLER statement is generally used. The HANDLER\nshould handler the 1329 error, or the \'02000\' SQLSTATE, or the NOT FOUND error\nclass.\n\nOnly SELECT statements are allowed for cursors, and they cannot be contained\nin a variable - so, they cannot be composed dynamically. However, it is\npossible to SELECT from a view. Since the CREATE VIEW statement can be\nexecuted as a prepared statement, it is possible to dynamically create the\nview that is queried by the cursor.\n\nFrom MariaDB 10.3.0, cursors can have parameters. Cursor parameters can appear\nin any part of the DECLARE CURSOR select_statement where a stored procedure\nvariable is allowed (select list, WHERE, HAVING, LIMIT etc). See DECLARE\nCURSOR and OPEN for syntax, and below for an example:\n\nExamples\n--------\n\nCREATE TABLE c1(i INT);\n\nCREATE TABLE c2(i INT);\n\nCREATE TABLE c3(i INT);\n\nDELIMITER //\n\nCREATE PROCEDURE p1()\nBEGIN\n DECLARE done INT DEFAULT FALSE;\n DECLARE x, y INT;\n DECLARE cur1 CURSOR FOR SELECT i FROM test.c1;\n DECLARE cur2 CURSOR FOR SELECT i FROM test.c2;\n DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;\n\nOPEN cur1;\n OPEN cur2;\n\nread_loop: LOOP\n FETCH cur1 INTO x;\n FETCH cur2 INTO y;\n IF done THEN\n LEAVE read_loop;\n END IF;\n IF x < y THEN\n INSERT INTO test.c3 VALUES (x);\n ELSE\n INSERT INTO test.c3 VALUES (y);\n END IF;\n END LOOP;\n\nCLOSE cur1;\n CLOSE cur2;\nEND; //\n\nDELIMITER ;\n\nINSERT INTO c1 VALUES(5),(50),(500);\n\nINSERT INTO c2 VALUES(10),(20),(30);\n\nCALL p1;\n\nSELECT * FROM c3;\n+------+\n| i |\n+------+\n| 5 |\n| 20 |\n| 30 |\n+------+\n\nFrom MariaDB 10.3.0\n\nDROP PROCEDURE IF EXISTS p1;\nDROP TABLE IF EXISTS t1;\nCREATE TABLE t1 (a INT, b VARCHAR(10));\n\nINSERT INTO t1 VALUES (1,\'old\'),(2,\'old\'),(3,\'old\'),(4,\'old\'),(5,\'old\');\n\nDELIMITER //\n\nCREATE PROCEDURE p1(min INT,max INT)\nBEGIN\n DECLARE done INT DEFAULT FALSE;\n DECLARE va INT;\n DECLARE cur CURSOR(pmin INT, pmax INT) FOR SELECT a FROM t1 WHERE a BETWEEN\npmin AND pmax;\n DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=TRUE;\n OPEN cur(min,max);\n read_loop: LOOP\n FETCH cur INTO va;\n IF done THEN\n LEAVE read_loop;\n END IF;\n INSERT INTO t1 VALUES (va,\'new\');\n END LOOP;\n CLOSE cur;\nEND;\n//\n\nDELIMITER ;\n\nCALL p1(2,4);\n\nSELECT * FROM t1;\n+------+------+\n| a | b |\n+------+------+\n| 1 | old |\n| 2 | old |\n| 3 | old |\n| 4 | old |\n| 5 | old |\n| 2 | new |\n| 3 | new |\n| 4 | new |\n+------+------+\n\nURL: https://mariadb.com/kb/en/cursor-overview/','','https://mariadb.com/kb/en/cursor-overview/'); @@ -431,20 +431,20 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (338,26,'SHOW EXPLAIN','Syntax\n------\n\nSHOW EXPLAIN [FORMAT=JSON] FOR ;\nEXPLAIN [FORMAT=JSON] FOR CONNECTION ;\n\nDescription\n-----------\n\nThe SHOW EXPLAIN command allows one to get an EXPLAIN (that is, a description\nof a query plan) of a query running in a certain connection.\n\nSHOW EXPLAIN FOR ;\n\nwill produce an EXPLAIN output for the query that connection number\nconnection_id is running. The connection id can be obtained with SHOW\nPROCESSLIST.\n\nSHOW EXPLAIN FOR 1;\n+------+-------------+-------+-------+---------------+------+---------+------+-\n-------+-------------+\n| id | select_type | table | type | possible_keys | key | key_len | ref |\nrows | Extra |\n+------+-------------+-------+-------+---------------+------+---------+------+-\n-------+-------------+\n| 1 | SIMPLE | tbl | index | NULL | a | 5 | NULL |\n1000107 | Using index |\n+------+-------------+-------+-------+---------------+------+---------+------+-\n-------+-------------+\n1 row in set, 1 warning (0.00 sec)\n\nThe output is always accompanied with a warning which shows the query the\ntarget connection is running (this shows what the EXPLAIN is for):\n\nSHOW WARNINGS;\n+-------+------+------------------------+\n| Level | Code | Message |\n+-------+------+------------------------+\n| Note | 1003 | select sum(a) from tbl |\n+-------+------+------------------------+\n1 row in set (0.00 sec)\n\nEXPLAIN FOR CONNECTION\n----------------------\n\nMariaDB starting with 10.9\n--------------------------\nThe EXPLAIN FOR CONNECTION syntax was added for MySQL compatibility.\n\nFORMAT=JSON\n-----------\n\nMariaDB starting with 10.9\n--------------------------\nSHOW EXPLAIN [FORMAT=JSON] FOR extends SHOW EXPLAIN to return\nmore detailed JSON output.\n\nPossible Errors\n---------------\n\nThe output can be only produced if the target connection is currently running\na query, which has a ready query plan. If this is not the case, the output\nwill be:\n\nSHOW EXPLAIN FOR 2;\nERROR 1932 (HY000): Target is not running an EXPLAINable command\n\nYou will get this error when:\n\n* the target connection is not running a command for which one can run EXPLAIN\n* the target connection is running a command for which one can run EXPLAIN, but\nthere is no query plan yet (for example, tables are open and locks are\n acquired before the query plan is produced)\n\nDifferences Between SHOW EXPLAIN and EXPLAIN Outputs\n----------------------------------------------------\n\nBackground\n----------\n\nIn MySQL, EXPLAIN execution takes a slightly different route from the way the\nreal query (typically the SELECT) is optimized. This is unfortunate, and has\ncaused a number of bugs in EXPLAIN. (For example, see MDEV-326, MDEV-410, and\nlp:1013343. lp:992942 is not directly about EXPLAIN, but it also would not\nhave existed if MySQL didn\'t try to delete parts of a query plan in the middle\nof the query)\n\nSHOW EXPLAIN examines a running SELECT, and hence its output may be slightly\ndifferent from what EXPLAIN SELECT would produce. We did our best to make sure\nthat either the difference is negligible, or SHOW EXPLAIN\'s output is closer\nto reality than EXPLAIN\'s output.\n\nList of Recorded Differences\n----------------------------\n\n* SHOW EXPLAIN may have Extra=\'no matching row in const table\', where EXPLAIN\nwould produce Extra=\'Impossible WHERE ...\'\n* For queries with subqueries, SHOW EXPLAIN may print select_type==PRIMARY\nwhere regular EXPLAIN used to print select_type==SIMPLE, or vice versa.\n\nRequired Permissions\n--------------------\n\nRunning SHOW EXPLAIN requires the same permissions as running SHOW PROCESSLIST\nwould.\n\nURL: https://mariadb.com/kb/en/show-explain/','','https://mariadb.com/kb/en/show-explain/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (339,26,'BACKUP STAGE','MariaDB starting with 10.4.1\n----------------------------\nThe BACKUP STAGE commands were introduced in MariaDB 10.4.1.\n\nThe BACKUP STAGE commands are a set of commands to make it possible to make an\nefficient external backup tool.\n\nSyntax\n------\n\nBACKUP STAGE [START | FLUSH | BLOCK_DDL | BLOCK_COMMIT | END ]\n\nIn the following text, a transactional table means InnoDB or \"InnoDB-like\nengine with redo log that can lock redo purges and can be copied without locks\nby an outside process\".\n\nGoals with BACKUP STAGE Commands\n--------------------------------\n\n* To be able to do a majority of the backup with the minimum possible server\nlocks. Especially for transactional tables (InnoDB, MyRocks etc) there is only\nneed for a very short block of new commits while copying statistics and log\ntables.\n* DDL are only needed to be blocked for a very short duration of the backup\nwhile mariabackup is copying the tables affected by DDL during the initial\npart of the backup.\n* Most non transactional tables (those that are not in use) will be copied\nduring BACKUP STAGE START. The exceptions are system statistic and log tables\nthat are not blocked during the backup until BLOCK_COMMIT.\n* Should work efficiently with backup tools that use disk snapshots.\n* Should work as efficiently as possible for all table types that store data\non the local disks.\n* As little copying as possible under higher level stages/locks. For example,\n.frm (dictionary) and .trn (trigger) files should be copying while copying the\ntable data.\n\nBACKUP STAGE Commands\n---------------------\n\nBACKUP STAGE START\n------------------\n\nThe START stage is designed for the following tasks:\n\n* Blocks purge of redo files for storage engines that needs this (Aria)\n* Start logging of DDL commands into \'datadir\'/ddl.log. This may take a short\ntime as the command has to wait until there are no active DDL commands.\n\nBACKUP STAGE FLUSH\n------------------\n\nThe FLUSH stage is designed for the following tasks:\n\n* FLUSH all changes for inactive non-transactional tables, except for\nstatistics and log tables.\n* Close all tables that are not in use, to ensure they are marked as closed\nfor the backup.\n* BLOCK all new write locks for all non transactional tables (except\nstatistics and log tables). The command will not wait for tables that are in\nuse by read-only transactions.\n\nDDLs don\'t have to be blocked at this stage as they can\'t cause the table to\nbe in an inconsistent state. This is true also for non-transactional tables.\n\nBACKUP STAGE BLOCK_DDL\n----------------------\n\nThe BLOCK_DDL stage is designed for the following tasks:\n\n* Wait for all statements using write locked non-transactional tables to end.\n* Blocks CREATE TABLE, DROP TABLE, TRUNCATE TABLE, and RENAME TABLE.\n* Blocks also start off a new ALTER TABLE and the final rename phase of ALTER\nTABLE. Running ALTER TABLES are not blocked.\n\nBACKUP STAGE BLOCK_COMMIT\n-------------------------\n\nThe BLOCK_COMMIT stage is designed for the following tasks:\n\n* Lock the binary log and commit/rollback to ensure that no changes are\ncommitted to any tables. If there are active commits or data to be copied to\nthe binary log this will be allowed to finish. Active transactions will not\naffect BLOCK_COMMIT.\n* This doesn\'t lock temporary tables that are not used by replication. However\nthese will be blocked when it\'s time to write to the binary log.\n* Lock system log tables and statistics tables, flush them and mark them\nclosed.\n\nWhen the BLOCK_COMMIT\'s stages return, this is the \'backup time\'. Everything\ncommitted will be in the backup and everything not committed will roll back.\n\nTransactional engines will continue to do changes to the redo log during the\nBLOCK COMMIT stage, but this is not important as all of these will roll back\nlater as the changes will not be committed.\n\nBACKUP STAGE END\n----------------\n\nThe END stage is designed for the following tasks:\n\n* End DDL logging\n* Free resources\n\nUsing BACKUP STAGE Commands with Backup Tools\n---------------------------------------------\n\nUsing BACKUP STAGE Commands with Mariabackup\n--------------------------------------------\n\nThe BACKUP STAGE commands are a set of commands to make it possible to make an\nefficient external backup tool. How Mariabackup uses these commands depends on\nwhether you are using the version that is bundled with MariaDB Community\nServer or the version that is bundled with MariaDB Enterprise Server. See\nMariabackup and BACKUP STAGE Commands for some examples on how Mariabackup\nuses these commands.\n\nIf you would like to use a version of Mariabackup that uses the BACKUP STAGE\ncommands in an efficient way, then one option is to use MariaDB Enterprise\nBackup that is bundled with MariaDB Enterprise Server.\n\nUsing BACKUP STAGE Commands with Storage Snapshots\n--------------------------------------------------\n\nThe BACKUP STAGE commands are a set of commands to make it possible to make an\nefficient external backup tool. These commands could even be used by tools\nthat perform backups by taking a snapshot of a file system, SAN, or some other\nkind of storage device. See Storage Snapshots and BACKUP STAGE Commands for\nsome examples on how to use each BACKUP STAGE command in an efficient way.\n\nPrivileges\n----------\n\nBACKUP STAGE requires the RELOAD privilege.\n\nNotes\n-----\n\n* Only one connection can run BACKUP STAGE START. If a second connection\ntries, it will wait until the first one has executed BACKUP STAGE END.\n* If the user skips a BACKUP STAGE, then all intermediate backup stages will\nautomatically be run. This will allow us to add new stages within the BACKUP\nSTAGE hierarchy in the future with even more precise locks without causing\nproblems for tools using an earlier version of the BACKUP STAGE implementation.\n* One can use the max_statement_time or lock_wait_timeout system variables to\nensure that a BACKUP STAGE command doesn\'t block the server too long.\n* DDL logging will only be available in MariaDB Enterprise Server 10.2 and\nlater.\n* A disconnect will automatically release backup stages.\n* There is no easy way to see which is the current stage.\n\nURL: https://mariadb.com/kb/en/backup-stage/','','https://mariadb.com/kb/en/backup-stage/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (340,26,'BACKUP LOCK','MariaDB starting with 10.4.2\n----------------------------\nThe BACKUP LOCK command was introduced in MariaDB 10.4.2.\n\nBACKUP LOCK blocks a table from DDL statements. This is mainly intended to be\nused by tools like mariabackup that need to ensure there are no DDLs on a\ntable while the table files are opened. For example, for an Aria table that\nstores data in 3 files with extensions .frm, .MAI and .MAD. Normal read/write\noperations can continue as normal.\n\nSyntax\n------\n\nTo lock a table:\n\nBACKUP LOCK table_name\n\nTo unlock a table:\n\nBACKUP UNLOCK\n\nUsage in a Backup Tool\n----------------------\n\nBACKUP LOCK [database.]table_name;\n - Open all files related to a table (for example, t.frm, t.MAI and t.MYD)\nBACKUP UNLOCK;\n- Copy data\n- Close files\n\nThis ensures that all files are from the same generation, that is created at\nthe same time by the MariaDB server. This works, because the open files will\npoint to the original table files which will not be affected if there is any\nALTER TABLE while copying the files.\n\nPrivileges\n----------\n\nBACKUP LOCK requires the RELOAD privilege.\n\nNotes\n-----\n\n* The idea is that the BACKUP LOCK should be held for as short a time as\npossible by the backup tool. The time to take an uncontested lock is very\nshort! One can easily do 50,000 locks/unlocks per second on low end hardware.\n* One should use different connections for BACKUP STAGE commands and BACKUP\nLOCK.\n\nImplementation\n--------------\n\n* Internally, BACKUP LOCK is implemented by taking an MDLSHARED_HIGH_PRIO MDL\nlock on the table object, which protects the table from any DDL operations.\n\nURL: https://mariadb.com/kb/en/backup-lock/','','https://mariadb.com/kb/en/backup-lock/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (341,26,'FLUSH','Syntax\n------\n\nFLUSH [NO_WRITE_TO_BINLOG | LOCAL]\n flush_option [, flush_option] ...\n\nor when flushing tables:\n\nFLUSH [NO_WRITE_TO_BINLOG | LOCAL] TABLES [table_list] [table_flush_option]\n\nwhere table_list is a list of tables separated by , (comma).\n\nDescription\n-----------\n\nThe FLUSH statement clears or reloads various internal caches used by MariaDB.\nTo execute FLUSH, you must have the RELOAD privilege. See GRANT.\n\nThe RESET statement is similar to FLUSH. See RESET.\n\nYou cannot issue a FLUSH statement from within a stored function or a trigger.\nDoing so within a stored procedure is permitted, as long as it is not called\nby a stored function or trigger. See Stored Routine Limitations, Stored\nFunction Limitations and Trigger Limitations.\n\nIf a listed table is a view, an error like the following will be produced:\n\nERROR 1347 (HY000): \'test.v\' is not BASE TABLE\n\nBy default, FLUSH statements are written to the binary log and will be\nreplicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure the\nstatement is not written to the binary log.\n\nThe different flush options are:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| CHANGED_PAGE_BITMAPS | XtraDB only. Internal command used for backup |\n| | purposes. See the Information Schema |\n| | CHANGED_PAGE_BITMAPS Table. |\n+---------------------------+------------------------------------------------+\n| CLIENT_STATISTICS | Reset client statistics (see SHOW |\n| | CLIENT_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| DES_KEY_FILE | Reloads the DES key file (Specified with the |\n| | --des-key-file startup option). |\n+---------------------------+------------------------------------------------+\n| HOSTS | Flush the hostname cache (used for converting |\n| | ip to host names and for unblocking blocked |\n| | hosts. See max_connect_errors) |\n+---------------------------+------------------------------------------------+\n| INDEX_STATISTICS | Reset index statistics (see SHOW |\n| | INDEX_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| [ERROR | ENGINE | | Close and reopen the specified log type, or |\n| GENERAL | SLOW | BINARY | all log types if none are specified. FLUSH |\n| | RELAY] LOGS | RELAY LOGS [connection-name] can be used to |\n| | flush the relay logs for a specific |\n| | connection. Only one connection can be |\n| | specified per FLUSH command. See Multi-source |\n| | replication. FLUSH ENGINE LOGS will delete |\n| | all unneeded Aria redo logs. Since MariaDB |\n| | 10.1.30 and MariaDB 10.2.11, FLUSH BINARY |\n| | LOGS DELETE_DOMAIN_ID=(list-of-domains) can |\n| | be used to discard obsolete GTID domains from |\n| | the server\'s binary log state. In order for |\n| | this to be successful, no event group from |\n| | the listed GTID domains can be present in |\n| | existing binary log files. If some still |\n| | exist, then they must be purged prior to |\n| | executing this command. If the command |\n| | completes successfully, then it also rotates |\n| | the binary log. |\n+---------------------------+------------------------------------------------+\n| MASTER | Deprecated option, use RESET MASTER instead. |\n+---------------------------+------------------------------------------------+\n| PRIVILEGES | Reload all privileges from the privilege |\n| | tables in the mysql database. If the server |\n| | is started with --skip-grant-table option, |\n| | this will activate the privilege tables again. |\n+---------------------------+------------------------------------------------+\n| QUERY CACHE | Defragment the query cache to better utilize |\n| | its memory. If you want to reset the query |\n| | cache, you can do it with RESET QUERY CACHE. |\n+---------------------------+------------------------------------------------+\n| QUERY_RESPONSE_TIME | See the QUERY_RESPONSE_TIME plugin. |\n+---------------------------+------------------------------------------------+\n| SLAVE | Deprecated option, use RESET REPLICA or RESET |\n| | SLAVE instead. |\n+---------------------------+------------------------------------------------+\n| SSL | Used to dynamically reinitialize the server\'s |\n| | TLS context by reloading the files defined by |\n| | several TLS system variables. See FLUSH SSL |\n| | for more information. This command was first |\n| | added in MariaDB 10.4.1. |\n+---------------------------+------------------------------------------------+\n| STATUS | Resets all server status variables that can |\n| | be reset to 0. Not all global status |\n| | variables support this, so not all global |\n| | values are reset. See FLUSH STATUS for more |\n| | information. |\n+---------------------------+------------------------------------------------+\n| TABLE | Close tables given as options or all open |\n| | tables if no table list was used. From |\n| | MariaDB 10.4.1, using without any table list |\n| | will only close tables not in use, and tables |\n| | not locked by the FLUSH TABLES connection. If |\n| | there are no locked tables, FLUSH TABLES will |\n| | be instant and will not cause any waits, as |\n| | it no longer waits for tables in use. When a |\n| | table list is provided, from MariaDB 10.4.1, |\n| | the server will wait for the end of any |\n| | transactions that are using the tables. |\n| | Previously, FLUSH TABLES only waited for the |\n| | statements to complete. |\n+---------------------------+------------------------------------------------+\n| TABLES | Same as FLUSH TABLE. |\n+---------------------------+------------------------------------------------+\n| TABLES ... FOR EXPORT | For InnoDB tables, flushes table changes to |\n| | disk to permit binary table copies while the |\n| | server is running. See FLUSH TABLES ... FOR |\n| | EXPORT for more. |\n+---------------------------+------------------------------------------------+\n| TABLES WITH READ LOCK | Closes all open tables. New tables are only |\n| | allowed to be opened with read locks until an |\n| | UNLOCK TABLES is given. |\n+---------------------------+------------------------------------------------+\n| TABLES WITH READ LOCK | As TABLES WITH READ LOCK but also disable all |\n| AND DISABLE CHECKPOINT | checkpoint writes by transactional table |\n| | engines. This is useful when doing a disk |\n| | snapshot of all tables. |\n+---------------------------+------------------------------------------------+\n| TABLE_STATISTICS | Reset table statistics (see SHOW |\n| | TABLE_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| USER_RESOURCES | Resets all per hour user resources. This |\n| | enables clients that have exhausted their |\n| | resources to connect again. |\n+---------------------------+------------------------------------------------+\n| USER_STATISTICS | Reset user statistics (see SHOW |\n| | USER_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| USER_VARIABLES | Reset user variables (see User-defined |\n| | variables). |\n+---------------------------+------------------------------------------------+\n\nYou can also use the mysqladmin client to flush things. Use mysqladmin --help\nto examine what flush commands it supports.\n\nFLUSH RELAY LOGS\n----------------\n\nFLUSH RELAY LOGS \'connection_name\';\n\nCompatibility with MySQL\n------------------------\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nas using the channel_name directly after the FLUSH command.\n\nFor example, one can now use:\n\nFLUSH RELAY LOGS FOR CHANNEL \'connection_name\';\n\nFLUSH STATUS\n------------\n\nServer status variables can be reset by executing the following:\n\nFLUSH STATUS;\n\nGlobal Status Variables that Support FLUSH STATUS\n-------------------------------------------------\n\nNot all global status variables support being reset by FLUSH STATUS.\nCurrently, the following status variables are reset by FLUSH STATUS:\n\n* Aborted_clients\n* Aborted_connects\n* Binlog_cache_disk_use\n* Binlog_cache_use\n* Binlog_stmt_cache_disk_use\n* Binlog_stmt_cache_use\n* Connection_errors_accept\n* Connection_errors_internal\n* Connection_errors_max_connections\n* Connection_errors_peer_address\n* Connection_errors_select\n* Connection_errors_tcpwrap\n* Created_tmp_files\n* Delayed_errors\n* Delayed_writes\n* Feature_check_constraint\n* Feature_delay_key_write\n* Max_used_connections\n* Opened_plugin_libraries\n* Performance_schema_accounts_lost\n* Performance_schema_cond_instances_lost\n* Performance_schema_digest_lost\n* Performance_schema_file_handles_lost\n* Performance_schema_file_instances_lost\n* Performance_schema_hosts_lost\n* Performance_schema_locker_lost\n* Performance_schema_mutex_instances_lost\n* Performance_schema_rwlock_instances_lost\n* Performance_schema_session_connect_attrs_lost\n* Performance_schema_socket_instances_lost\n* Performance_schema_stage_classes_lost\n* Performance_schema_statement_classes_lost\n* Performance_schema_table_handles_lost\n* Performance_schema_table_instances_lost\n* Performance_schema_thread_instances_lost\n* Performance_schema_users_lost\n* Qcache_hits\n* Qcache_inserts\n* Qcache_lowmem_prunes\n* Qcache_not_cached\n* Rpl_semi_sync_master_no_times\n* Rpl_semi_sync_master_no_tx\n* Rpl_semi_sync_master_timefunc_failures\n* Rpl_semi_sync_master_wait_pos_backtraverse\n* Rpl_semi_sync_master_yes_tx\n* Rpl_transactions_multi_engine\n* Server_audit_writes_failed\n* Slave_retried_transactions\n* Slow_launch_threads\n* Ssl_accept_renegotiates\n* Ssl_accepts\n* Ssl_callback_cache_hits\n* Ssl_client_connects\n* Ssl_connect_renegotiates\n* Ssl_ctx_verify_depth\n* Ssl_ctx_verify_mode\n* Ssl_finished_accepts\n* Ssl_finished_connects\n* Ssl_session_cache_hits\n* Ssl_session_cache_misses\n* Ssl_session_cache_overflows\n* Ssl_session_cache_size\n* Ssl_session_cache_timeouts\n* Ssl_sessions_reused\n* Ssl_used_session_cache_entries\n* Subquery_cache_hit\n* Subquery_cache_miss\n* Table_locks_immediate\n* Table_locks_waited\n* Tc_log_max_pages_used\n* Tc_log_page_waits\n* Transactions_gtid_foreign_engine\n* Transactions_multi_engine\n\nThe different usage of FLUSH TABLES\n-----------------------------------\n\nThe purpose of FLUSH TABLES\n---------------------------\n\nThe purpose of FLUSH TABLES is to clean up the open table cache and table\ndefinition cache from not in use tables. This frees up memory and file\ndescriptors. Normally this is not needed as the caches works on a FIFO bases,\nbut can be useful if the server seams to use up to much memory for some reason.\n\nThe purpose of FLUSH TABLES WITH READ LOCK \n-------------------------------------------\n\nFLUSH TABLES WITH READ LOCK is useful if you want to take a backup of some\ntables. When FLUSH TABLES WITH READ LOCK returns, all write access to tables\nare blocked and all tables are marked as \'properly closed\' on disk. The tables\ncan still be used for read operations.\n\nThe purpose of FLUSH TABLES table_list\n--------------------------------------\n\nFLUSH TABLES table_list is useful if you want to copy a table object/files to\nor from the server. This command puts a lock that stops new users of the table\nand will wait until everyone has stopped using the table. The table is then\nremoved from the table definition and table cache.\n\nNote that it\'s up to the user to ensure that no one is accessing the table\nbetween FLUSH TABLES and the table is copied to or from the server. This can\nbe secured by using LOCK TABLES.\n\nIf there are any tables locked by the connection that is using FLUSH TABLES','','https://mariadb.com/kb/en/flush/'); -update help_topic set description = CONCAT(description, '\nall the locked tables will be closed as part of the flush and reopened and\nrelocked before FLUSH TABLES returns. This allows one to copy the table after\nFLUSH TABLES returns without having any writes on the table. For now this\nworks works with most tables, except InnoDB as InnoDB may do background purges\non the table even while it\'s write locked.\n\nThe purpose of FLUSH TABLES table_list WITH READ LOCK\n-----------------------------------------------------\n\nFLUSH TABLES table_list WITH READ LOCK should work as FLUSH TABLES WITH READ\nLOCK, but only those tables that are listed will be properly closed. However\nin practice this works exactly like FLUSH TABLES WITH READ LOCK as the FLUSH\ncommand has anyway to wait for all WRITE operations to end because we are\ndepending on a global read lock for this code. In the future we should\nconsider fixing this to instead use meta data locks.\n\nImplementation of FLUSH TABLES commands in MariaDB 10.4.8 and above\n-------------------------------------------------------------------\n\nImplementation of FLUSH TABLES\n------------------------------\n\n* Free memory and file descriptors not in use\n\nImplementation of FLUSH TABLES WITH READ LOCK\n---------------------------------------------\n\n* Lock all tables read only for simple old style backup.\n* All background writes are suspended and tables are marked as closed.\n* No statement requiring table changes are allowed for any user until UNLOCK\nTABLES.\n\nInstead of using FLUSH TABLE WITH READ LOCK one should in most cases instead\nuse BACKUP STAGE BLOCK_COMMIT.\n\nImplementation of FLUSH TABLES table_list\n-----------------------------------------\n\n* Free memory and file descriptors for tables not in use from table list.\n* Lock given tables as read only.\n* Wait until all translations has ended that uses any of the given tables.\n* Wait until all background writes are suspended and tables are marked as\nclosed.\n\nImplementation of FLUSH TABLES table_list FOR EXPORT\n----------------------------------------------------\n\n* Free memory and file descriptors for tables not in use from table list\n* Lock given tables as read.\n* Wait until all background writes are suspended and tables are marked as\nclosed.\n* Check that all tables supports FOR EXPORT\n* No changes to these tables allowed until UNLOCK TABLES\n\nThis is basically the same behavior as in old MariaDB version if one first\nlock the tables, then do FLUSH TABLES. The tables will be copyable until\nUNLOCK TABLES.\n\nFLUSH SSL\n---------\n\nMariaDB starting with 10.4\n--------------------------\nThe FLUSH SSL command was first added in MariaDB 10.4.\n\nIn MariaDB 10.4 and later, the FLUSH SSL command can be used to dynamically\nreinitialize the server\'s TLS context. This is most useful if you need to\nreplace a certificate that is about to expire without restarting the server.\n\nThis operation is performed by reloading the files defined by the following\nTLS system variables:\n\n* ssl_cert\n* ssl_key\n* ssl_ca\n* ssl_capath\n* ssl_crl\n* ssl_crlpath\n\nThese TLS system variables are not dynamic, so their values can not be changed\nwithout restarting the server.\n\nIf you want to dynamically reinitialize the server\'s TLS context, then you\nneed to change the certificate and key files at the relevant paths defined by\nthese TLS system variables, without actually changing the values of the\nvariables. See MDEV-19341 for more information.\n\nReducing Memory Usage\n---------------------\n\nTo flush some of the global caches that take up memory, you could execute the\nfollowing command:\n\nFLUSH LOCAL HOSTS,\n QUERY CACHE,\n TABLE_STATISTICS,\n INDEX_STATISTICS,\n USER_STATISTICS;\n\nURL: https://mariadb.com/kb/en/flush/') WHERE help_topic_id = 341; +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (341,26,'FLUSH','Syntax\n------\n\nFLUSH [NO_WRITE_TO_BINLOG | LOCAL]\n flush_option [, flush_option] ...\n\nor when flushing tables:\n\nFLUSH [NO_WRITE_TO_BINLOG | LOCAL] TABLES [table_list] [table_flush_option]\n\nwhere table_list is a list of tables separated by , (comma).\n\nDescription\n-----------\n\nThe FLUSH statement clears or reloads various internal caches used by MariaDB.\nTo execute FLUSH, you must have the RELOAD privilege. See GRANT.\n\nThe RESET statement is similar to FLUSH. See RESET.\n\nYou cannot issue a FLUSH statement from within a stored function or a trigger.\nDoing so within a stored procedure is permitted, as long as it is not called\nby a stored function or trigger. See Stored Routine Limitations, Stored\nFunction Limitations and Trigger Limitations.\n\nIf a listed table is a view, an error like the following will be produced:\n\nERROR 1347 (HY000): \'test.v\' is not BASE TABLE\n\nBy default, FLUSH statements are written to the binary log and will be\nreplicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure the\nstatement is not written to the binary log.\n\nThe different flush options are:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| CHANGED_PAGE_BITMAPS | XtraDB only. Internal command used for backup |\n| | purposes. See the Information Schema |\n| | CHANGED_PAGE_BITMAPS Table. |\n+---------------------------+------------------------------------------------+\n| CLIENT_STATISTICS | Reset client statistics (see SHOW |\n| | CLIENT_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| DES_KEY_FILE | Reloads the DES key file (Specified with the |\n| | --des-key-file startup option). |\n+---------------------------+------------------------------------------------+\n| HOSTS | Flush the hostname cache (used for converting |\n| | ip to host names and for unblocking blocked |\n| | hosts. See max_connect_errors and |\n| | performance_schema.host_cache |\n+---------------------------+------------------------------------------------+\n| INDEX_STATISTICS | Reset index statistics (see SHOW |\n| | INDEX_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| [ERROR | ENGINE | | Close and reopen the specified log type, or |\n| GENERAL | SLOW | BINARY | all log types if none are specified. FLUSH |\n| | RELAY] LOGS | RELAY LOGS [connection-name] can be used to |\n| | flush the relay logs for a specific |\n| | connection. Only one connection can be |\n| | specified per FLUSH command. See Multi-source |\n| | replication. FLUSH ENGINE LOGS will delete |\n| | all unneeded Aria redo logs. Since MariaDB |\n| | 10.1.30 and MariaDB 10.2.11, FLUSH BINARY |\n| | LOGS DELETE_DOMAIN_ID=(list-of-domains) can |\n| | be used to discard obsolete GTID domains from |\n| | the server\'s binary log state. In order for |\n| | this to be successful, no event group from |\n| | the listed GTID domains can be present in |\n| | existing binary log files. If some still |\n| | exist, then they must be purged prior to |\n| | executing this command. If the command |\n| | completes successfully, then it also rotates |\n| | the binary log. |\n+---------------------------+------------------------------------------------+\n| MASTER | Deprecated option, use RESET MASTER instead. |\n+---------------------------+------------------------------------------------+\n| PRIVILEGES | Reload all privileges from the privilege |\n| | tables in the mysql database. If the server |\n| | is started with --skip-grant-table option, |\n| | this will activate the privilege tables again. |\n+---------------------------+------------------------------------------------+\n| QUERY CACHE | Defragment the query cache to better utilize |\n| | its memory. If you want to reset the query |\n| | cache, you can do it with RESET QUERY CACHE. |\n+---------------------------+------------------------------------------------+\n| QUERY_RESPONSE_TIME | See the QUERY_RESPONSE_TIME plugin. |\n+---------------------------+------------------------------------------------+\n| SLAVE | Deprecated option, use RESET REPLICA or RESET |\n| | SLAVE instead. |\n+---------------------------+------------------------------------------------+\n| SSL | Used to dynamically reinitialize the server\'s |\n| | TLS context by reloading the files defined by |\n| | several TLS system variables. See FLUSH SSL |\n| | for more information. This command was first |\n| | added in MariaDB 10.4.1. |\n+---------------------------+------------------------------------------------+\n| STATUS | Resets all server status variables that can |\n| | be reset to 0. Not all global status |\n| | variables support this, so not all global |\n| | values are reset. See FLUSH STATUS for more |\n| | information. |\n+---------------------------+------------------------------------------------+\n| TABLE | Close tables given as options or all open |\n| | tables if no table list was used. From |\n| | MariaDB 10.4.1, using without any table list |\n| | will only close tables not in use, and tables |\n| | not locked by the FLUSH TABLES connection. If |\n| | there are no locked tables, FLUSH TABLES will |\n| | be instant and will not cause any waits, as |\n| | it no longer waits for tables in use. When a |\n| | table list is provided, from MariaDB 10.4.1, |\n| | the server will wait for the end of any |\n| | transactions that are using the tables. |\n| | Previously, FLUSH TABLES only waited for the |\n| | statements to complete. |\n+---------------------------+------------------------------------------------+\n| TABLES | Same as FLUSH TABLE. |\n+---------------------------+------------------------------------------------+\n| TABLES ... FOR EXPORT | For InnoDB tables, flushes table changes to |\n| | disk to permit binary table copies while the |\n| | server is running. See FLUSH TABLES ... FOR |\n| | EXPORT for more. |\n+---------------------------+------------------------------------------------+\n| TABLES WITH READ LOCK | Closes all open tables. New tables are only |\n| | allowed to be opened with read locks until an |\n| | UNLOCK TABLES is given. |\n+---------------------------+------------------------------------------------+\n| TABLES WITH READ LOCK | As TABLES WITH READ LOCK but also disable all |\n| AND DISABLE CHECKPOINT | checkpoint writes by transactional table |\n| | engines. This is useful when doing a disk |\n| | snapshot of all tables. |\n+---------------------------+------------------------------------------------+\n| TABLE_STATISTICS | Reset table statistics (see SHOW |\n| | TABLE_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| USER_RESOURCES | Resets all per hour user resources. This |\n| | enables clients that have exhausted their |\n| | resources to connect again. |\n+---------------------------+------------------------------------------------+\n| USER_STATISTICS | Reset user statistics (see SHOW |\n| | USER_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| USER_VARIABLES | Reset user variables (see User-defined |\n| | variables). |\n+---------------------------+------------------------------------------------+\n\nYou can also use the mysqladmin client to flush things. Use mysqladmin --help\nto examine what flush commands it supports.\n\nFLUSH RELAY LOGS\n----------------\n\nFLUSH RELAY LOGS \'connection_name\';\n\nCompatibility with MySQL\n------------------------\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nas using the channel_name directly after the FLUSH command.\n\nFor example, one can now use:\n\nFLUSH RELAY LOGS FOR CHANNEL \'connection_name\';\n\nFLUSH STATUS\n------------\n\nServer status variables can be reset by executing the following:\n\nFLUSH STATUS;\n\nGlobal Status Variables that Support FLUSH STATUS\n-------------------------------------------------\n\nNot all global status variables support being reset by FLUSH STATUS.\nCurrently, the following status variables are reset by FLUSH STATUS:\n\n* Aborted_clients\n* Aborted_connects\n* Binlog_cache_disk_use\n* Binlog_cache_use\n* Binlog_stmt_cache_disk_use\n* Binlog_stmt_cache_use\n* Connection_errors_accept\n* Connection_errors_internal\n* Connection_errors_max_connections\n* Connection_errors_peer_address\n* Connection_errors_select\n* Connection_errors_tcpwrap\n* Created_tmp_files\n* Delayed_errors\n* Delayed_writes\n* Feature_check_constraint\n* Feature_delay_key_write\n* Max_used_connections\n* Opened_plugin_libraries\n* Performance_schema_accounts_lost\n* Performance_schema_cond_instances_lost\n* Performance_schema_digest_lost\n* Performance_schema_file_handles_lost\n* Performance_schema_file_instances_lost\n* Performance_schema_hosts_lost\n* Performance_schema_locker_lost\n* Performance_schema_mutex_instances_lost\n* Performance_schema_rwlock_instances_lost\n* Performance_schema_session_connect_attrs_lost\n* Performance_schema_socket_instances_lost\n* Performance_schema_stage_classes_lost\n* Performance_schema_statement_classes_lost\n* Performance_schema_table_handles_lost\n* Performance_schema_table_instances_lost\n* Performance_schema_thread_instances_lost\n* Performance_schema_users_lost\n* Qcache_hits\n* Qcache_inserts\n* Qcache_lowmem_prunes\n* Qcache_not_cached\n* Rpl_semi_sync_master_no_times\n* Rpl_semi_sync_master_no_tx\n* Rpl_semi_sync_master_timefunc_failures\n* Rpl_semi_sync_master_wait_pos_backtraverse\n* Rpl_semi_sync_master_yes_tx\n* Rpl_transactions_multi_engine\n* Server_audit_writes_failed\n* Slave_retried_transactions\n* Slow_launch_threads\n* Ssl_accept_renegotiates\n* Ssl_accepts\n* Ssl_callback_cache_hits\n* Ssl_client_connects\n* Ssl_connect_renegotiates\n* Ssl_ctx_verify_depth\n* Ssl_ctx_verify_mode\n* Ssl_finished_accepts\n* Ssl_finished_connects\n* Ssl_session_cache_hits\n* Ssl_session_cache_misses\n* Ssl_session_cache_overflows\n* Ssl_session_cache_size\n* Ssl_session_cache_timeouts\n* Ssl_sessions_reused\n* Ssl_used_session_cache_entries\n* Subquery_cache_hit\n* Subquery_cache_miss\n* Table_locks_immediate\n* Table_locks_waited\n* Tc_log_max_pages_used\n* Tc_log_page_waits\n* Transactions_gtid_foreign_engine\n* Transactions_multi_engine\n\nThe different usage of FLUSH TABLES\n-----------------------------------\n\nThe purpose of FLUSH TABLES\n---------------------------\n\nThe purpose of FLUSH TABLES is to clean up the open table cache and table\ndefinition cache from not in use tables. This frees up memory and file\ndescriptors. Normally this is not needed as the caches works on a FIFO bases,\nbut can be useful if the server seams to use up to much memory for some reason.\n\nThe purpose of FLUSH TABLES WITH READ LOCK \n-------------------------------------------\n\nFLUSH TABLES WITH READ LOCK is useful if you want to take a backup of some\ntables. When FLUSH TABLES WITH READ LOCK returns, all write access to tables\nare blocked and all tables are marked as \'properly closed\' on disk. The tables\ncan still be used for read operations.\n\nThe purpose of FLUSH TABLES table_list\n--------------------------------------\n\nFLUSH TABLES table_list is useful if you want to copy a table object/files to\nor from the server. This command puts a lock that stops new users of the table\nand will wait until everyone has stopped using the table. The table is then\nremoved from the table definition and table cache.\n\nNote that it\'s up to the user to ensure that no one is accessing the table\nbetween FLUSH TABLES and the table is copied to or from the server. This can\nbe secured by using LOCK TABLES.\n','','https://mariadb.com/kb/en/flush/'); +update help_topic set description = CONCAT(description, '\nIf there are any tables locked by the connection that is using FLUSH TABLES\nall the locked tables will be closed as part of the flush and reopened and\nrelocked before FLUSH TABLES returns. This allows one to copy the table after\nFLUSH TABLES returns without having any writes on the table. For now this\nworks works with most tables, except InnoDB as InnoDB may do background purges\non the table even while it\'s write locked.\n\nThe purpose of FLUSH TABLES table_list WITH READ LOCK\n-----------------------------------------------------\n\nFLUSH TABLES table_list WITH READ LOCK should work as FLUSH TABLES WITH READ\nLOCK, but only those tables that are listed will be properly closed. However\nin practice this works exactly like FLUSH TABLES WITH READ LOCK as the FLUSH\ncommand has anyway to wait for all WRITE operations to end because we are\ndepending on a global read lock for this code. In the future we should\nconsider fixing this to instead use meta data locks.\n\nImplementation of FLUSH TABLES commands in MariaDB 10.4.8 and above\n-------------------------------------------------------------------\n\nImplementation of FLUSH TABLES\n------------------------------\n\n* Free memory and file descriptors not in use\n\nImplementation of FLUSH TABLES WITH READ LOCK\n---------------------------------------------\n\n* Lock all tables read only for simple old style backup.\n* All background writes are suspended and tables are marked as closed.\n* No statement requiring table changes are allowed for any user until UNLOCK\nTABLES.\n\nInstead of using FLUSH TABLE WITH READ LOCK one should in most cases instead\nuse BACKUP STAGE BLOCK_COMMIT.\n\nImplementation of FLUSH TABLES table_list\n-----------------------------------------\n\n* Free memory and file descriptors for tables not in use from table list.\n* Lock given tables as read only.\n* Wait until all translations has ended that uses any of the given tables.\n* Wait until all background writes are suspended and tables are marked as\nclosed.\n\nImplementation of FLUSH TABLES table_list FOR EXPORT\n----------------------------------------------------\n\n* Free memory and file descriptors for tables not in use from table list\n* Lock given tables as read.\n* Wait until all background writes are suspended and tables are marked as\nclosed.\n* Check that all tables supports FOR EXPORT\n* No changes to these tables allowed until UNLOCK TABLES\n\nThis is basically the same behavior as in old MariaDB version if one first\nlock the tables, then do FLUSH TABLES. The tables will be copyable until\nUNLOCK TABLES.\n\nFLUSH SSL\n---------\n\nMariaDB starting with 10.4\n--------------------------\nThe FLUSH SSL command was first added in MariaDB 10.4.\n\nIn MariaDB 10.4 and later, the FLUSH SSL command can be used to dynamically\nreinitialize the server\'s TLS context. This is most useful if you need to\nreplace a certificate that is about to expire without restarting the server.\n\nThis operation is performed by reloading the files defined by the following\nTLS system variables:\n\n* ssl_cert\n* ssl_key\n* ssl_ca\n* ssl_capath\n* ssl_crl\n* ssl_crlpath\n\nThese TLS system variables are not dynamic, so their values can not be changed\nwithout restarting the server.\n\nIf you want to dynamically reinitialize the server\'s TLS context, then you\nneed to change the certificate and key files at the relevant paths defined by\nthese TLS system variables, without actually changing the values of the\nvariables. See MDEV-19341 for more information.\n\nReducing Memory Usage\n---------------------\n\nTo flush some of the global caches that take up memory, you could execute the\nfollowing command:\n\nFLUSH LOCAL HOSTS,\n QUERY CACHE,\n TABLE_STATISTICS,\n INDEX_STATISTICS,\n USER_STATISTICS;\n\nURL: https://mariadb.com/kb/en/flush/') WHERE help_topic_id = 341; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (342,26,'FLUSH QUERY CACHE','Description\n-----------\n\nYou can defragment the query cache to better utilize its memory with the FLUSH\nQUERY CACHE statement. The statement does not remove any queries from the\ncache.\n\nThe RESET QUERY CACHE statement removes all query results from the query\ncache. The FLUSH TABLES statement also does this.\n\nURL: https://mariadb.com/kb/en/flush-query-cache/','','https://mariadb.com/kb/en/flush-query-cache/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (343,26,'FLUSH TABLES FOR EXPORT','Syntax\n------\n\nFLUSH TABLES table_name [, table_name] FOR EXPORT\n\nDescription\n-----------\n\nFLUSH TABLES ... FOR EXPORT flushes changes to the specified tables to disk so\nthat binary copies can be made while the server is still running. This works\nfor Archive, Aria, CSV, InnoDB, MyISAM, MERGE, and XtraDB tables.\n\nThe table is read locked until one has issued UNLOCK TABLES.\n\nIf a storage engine does not support FLUSH TABLES FOR EXPORT, a 1031 error\n(SQLSTATE \'HY000\') is produced.\n\nIf FLUSH TABLES ... FOR EXPORT is in effect in the session, the following\nstatements will produce an error if attempted:\n\n* FLUSH TABLES WITH READ LOCK\n* FLUSH TABLES ... WITH READ LOCK\n* FLUSH TABLES ... FOR EXPORT\n* Any statement trying to update any table\n\nIf any of the following statements is in effect in the session, attempting\nFLUSH TABLES ... FOR EXPORT will produce an error.\n\n* FLUSH TABLES ... WITH READ LOCK\n* FLUSH TABLES ... FOR EXPORT\n* LOCK TABLES ... READ\n* LOCK TABLES ... WRITE\n\nFLUSH FOR EXPORT is not written to the binary log.\n\nThis statement requires the RELOAD and the LOCK TABLES privileges.\n\nIf one of the specified tables cannot be locked, none of the tables will be\nlocked.\n\nIf a table does not exist, an error like the following will be produced:\n\nERROR 1146 (42S02): Table \'test.xxx\' doesn\'t exist\n\nIf a table is a view, an error like the following will be produced:\n\nERROR 1347 (HY000): \'test.v\' is not BASE TABLE\n\nExample\n-------\n\nFLUSH TABLES test.t1 FOR EXPORT;\n# Copy files related to the table (see below)\nUNLOCK TABLES;\n\nFor a full description, please see copying MariaDB tables.\n\nURL: https://mariadb.com/kb/en/flush-tables-for-export/','','https://mariadb.com/kb/en/flush-tables-for-export/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (344,26,'SHOW RELAYLOG EVENTS','The terms master and slave have historically been used in replication, but the\nterms terms primary and replica are now preferred. The old terms are used\nstill used in parts of the documentation, and in MariaDB commands, although\nMariaDB 10.5 has begun the process of renaming. The documentation process is\nongoing. See MDEV-18777 to follow progress on this effort.\n\nSyntax\n------\n\nSHOW RELAYLOG [\'connection_name\'] EVENTS\n [IN \'log_name\'] [FROM pos] [LIMIT [offset,] row_count]\n [ FOR CHANNEL \'channel_name\']\n\nDescription\n-----------\n\nOn replicas, this command shows the events in the relay log. If \'log_name\' is\nnot specified, the first relay log is shown.\n\nSyntax for the LIMIT clause is the same as for SELECT ... LIMIT.\n\nUsing the LIMIT clause is highly recommended because the SHOW RELAYLOG EVENTS\ncommand returns the complete contents of the relay log, which can be quite\nlarge.\n\nThis command does not return events related to setting user and system\nvariables. If you need those, use mariadb-binlog/mysqlbinlog.\n\nOn the primary, this command does nothing.\n\nRequires the REPLICA MONITOR privilege (>= MariaDB 10.5.9), the REPLICATION\nSLAVE ADMIN privilege (>= MariaDB 10.5.2) or the REPLICATION SLAVE privilege\n(<= MariaDB 10.5.1).\n\nconnection_name\n---------------\n\nIf there is only one nameless primary, or the default primary (as specified by\nthe default_master_connection system variable) is intended, connection_name\ncan be omitted. If provided, the SHOW RELAYLOG statement will apply to the\nspecified primary. connection_name is case-insensitive.\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nas using the channel_name directly after SHOW RELAYLOG.\n\nURL: https://mariadb.com/kb/en/show-relaylog-events/','','https://mariadb.com/kb/en/show-relaylog-events/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (345,26,'SHOW SLAVE STATUS','Syntax\n------\n\nSHOW SLAVE [\"connection_name\"] STATUS [FOR CHANNEL \"connection_name\"]\nSHOW REPLICA [\"connection_name\"] STATUS -- From MariaDB 10.5.1\n\nor\n\nSHOW ALL SLAVES STATUS\nSHOW ALL REPLICAS STATUS -- From MariaDB 10.5.1\n\nDescription\n-----------\n\nThis statement is to be run on a replica and provides status information on\nessential parameters of the replica threads.\n\nThis statement requires the SUPER privilege, the REPLICATION_CLIENT privilege,\nor, from MariaDB 10.5.2, the REPLICATION SLAVE ADMIN privilege, or, from\nMariaDB 10.5.9, the REPLICA MONITOR privilege.\n\nMulti-Source\n------------\n\nThe ALL and \"connection_name\" options allow you to connect to many primaries\nat the same time.\n\nALL SLAVES (or ALL REPLICAS from MariaDB 10.5.1) gives you a list of all\nconnections to the primary nodes.\n\nThe rows will be sorted according to Connection_name.\n\nIf you specify a connection_name, you only get the information about that\nconnection. If connection_name is not used, then the name set by\ndefault_master_connection is used. If the connection name doesn\'t exist you\nwill get an error: There is no master connection for \'xxx\'.\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nas using the channel_name directly after SHOW SLAVE.\n\nColumn Descriptions\n-------------------\n\n+---------------+---------------------------------------+-------------------+\n| Name | Description | Added |\n+---------------+---------------------------------------+-------------------+\n| Connection_na | Name of the primary connection. | |\n| e | Returned with SHOW ALL SLAVES STATUS | |\n| | (or SHOW ALL REPLICAS STATUS from | |\n| | MariaDB 10.5.1) only. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_SQL_Sta | State of SQL thread. Returned with | |\n| e | SHOW ALL SLAVES STATUS (or SHOW ALL | |\n| | REPLICAS STATUS from MariaDB 10.5.1) | |\n| | only. See Slave SQL Thread States. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_IO_Stat | State of I/O thread. See Slave I/O | |\n| | Thread States. | |\n+---------------+---------------------------------------+-------------------+\n| Master_host | Master host that the replica is | |\n| | connected to. | |\n+---------------+---------------------------------------+-------------------+\n| Master_user | Account user name being used to | |\n| | connect to the primary. | |\n+---------------+---------------------------------------+-------------------+\n| Master_port | The port being used to connect to | |\n| | the primary. | |\n+---------------+---------------------------------------+-------------------+\n| Connect_Retry | Time in seconds between retries to | |\n| | connect. The default is 60. The | |\n| | CHANGE MASTER TO statement can set | |\n| | this. The master-retry-count option | |\n| | determines the maximum number of | |\n| | reconnection attempts. | |\n+---------------+---------------------------------------+-------------------+\n| Master_Log_Fi | Name of the primary binary log file | |\n| e | that the I/O thread is currently | |\n| | reading from. | |\n+---------------+---------------------------------------+-------------------+\n| Read_Master_L | Position up to which the I/O thread | |\n| g_Pos | has read in the current primary | |\n| | binary log file. | |\n+---------------+---------------------------------------+-------------------+\n| Relay_Log_Fil | Name of the relay log file that the | |\n| | SQL thread is currently processing. | |\n+---------------+---------------------------------------+-------------------+\n| Relay_Log_Pos | Position up to which the SQL thread | |\n| | has finished processing in the | |\n| | current relay log file. | |\n+---------------+---------------------------------------+-------------------+\n| Relay_Master_ | Name of the primary binary log file | |\n| og_File | that contains the most recent event | |\n| | executed by the SQL thread. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_IO_Runn | Whether the replica I/O thread is | |\n| ng | running and connected (Yes), running | |\n| | but not connected to a primary | |\n| | (Connecting) or not running (No). | |\n+---------------+---------------------------------------+-------------------+\n| Slave_SQL_Run | Whether or not the SQL thread is | |\n| ing | running. | |\n+---------------+---------------------------------------+-------------------+\n| Replicate_Do_ | Databases specified for replicating | |\n| B | with the replicate_do_db option. | |\n+---------------+---------------------------------------+-------------------+\n| Replicate_Ign | Databases specified for ignoring | |\n| re_DB | with the replicate_ignore_db option. | |\n+---------------+---------------------------------------+-------------------+\n| Replicate_Do_ | Tables specified for replicating | |\n| able | with the replicate_do_table option. | |\n+---------------+---------------------------------------+-------------------+\n| Replicate_Ign | Tables specified for ignoring with | |\n| re_Table | the replicate_ignore_table option. | |\n+---------------+---------------------------------------+-------------------+\n| Replicate_Wil | Tables specified for replicating | |\n| _Do_Table | with the replicate_wild_do_table | |\n| | option. | |\n+---------------+---------------------------------------+-------------------+\n| Replicate_Wil | Tables specified for ignoring with | |\n| _Ignore_Table | the replicate_wild_ignore_table | |\n| | option. | |\n+---------------+---------------------------------------+-------------------+\n| Last_Errno | Alias for Last_SQL_Errno (see below) | |\n+---------------+---------------------------------------+-------------------+\n| Last Error | Alias for Last_SQL_Error (see below) | |\n+---------------+---------------------------------------+-------------------+\n| Skip_Counter | Number of events that a replica | |\n| | skips from the master, as recorded | |\n| | in the sql_slave_skip_counter system | |\n| | variable. | |\n+---------------+---------------------------------------+-------------------+\n| Exec_Master_L | Position up to which the SQL thread | |\n| g_Pos | has processed in the current master | |\n| | binary log file. Can be used to | |\n| | start a new replica from a current | |\n| | replica with the CHANGE MASTER TO | |\n| | ... MASTER_LOG_POS option. | |\n+---------------+---------------------------------------+-------------------+\n| Relay_Log_Spa | Total size of all relay log files | |\n| e | combined. | |\n+---------------+---------------------------------------+-------------------+\n| Until_Conditi | | |\n| n | | |\n+---------------+---------------------------------------+-------------------+\n| Until_Log_Fil | The MASTER_LOG_FILE value of the | |\n| | START SLAVE UNTIL condition. | |\n+---------------+---------------------------------------+-------------------+\n| Until_Log_Pos | The MASTER_LOG_POS value of the | |\n| | START SLAVE UNTIL condition. | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_Al | Whether an SSL connection is | |\n| owed | permitted (Yes), not permitted (No) | |\n| | or permitted but without the replica | |\n| | having SSL support enabled (Ignored) | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_CA | The MASTER_SSL_CA option of the | |\n| File | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_CA | The MASTER_SSL_CAPATH option of the | |\n| Path | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_Ce | The MASTER_SSL_CERT option of the | |\n| t | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_Ci | The MASTER_SSL_CIPHER option of the | |\n| her | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_Ke | The MASTER_SSL_KEY option of the | |\n| | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Seconds_Behin | Difference between the timestamp | |\n| _Master | logged on the master for the event | |\n| | that the replica is currently | |\n| | processing, and the current | |\n| | timestamp on the replica. Zero if | |\n| | the replica is not currently | |\n| | processing an event. With parallel | |\n| | replication, seconds_behind_master | |\n| | is updated only after transactions | |\n| | commit. | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_Ve | The MASTER_SSL_VERIFY_SERVER_CERT | |\n| ify_Server_Ce | option of the CHANGE MASTER TO | |\n| t | statement. | |\n+---------------+---------------------------------------+-------------------+\n| Last_IO_Errno | Error code of the most recent error | |\n| | that caused the I/O thread to stop | |\n| | (also recorded in the replica\'s | |\n| | error log). 0 means no error. RESET | |\n| | SLAVE or RESET MASTER will reset | |\n| | this value. | |\n+---------------+---------------------------------------+-------------------+\n| Last_IO_Error | Error message of the most recent | |\n| | error that caused the I/O thread to | |\n| | stop (also recorded in the replica\'s | |\n| | error log). An empty string means no | |\n| | error. RESET SLAVE or RESET MASTER | |\n| | will reset this value. | |\n+---------------+---------------------------------------+-------------------+\n| Last_SQL_Errn | Error code of the most recent error | |\n| | that caused the SQL thread to stop | |\n| | (also recorded in the replica\'s | |\n| | error log). 0 means no error. RESET | |\n| | SLAVE or RESET MASTER will reset | |\n| | this value. | |\n+---------------+---------------------------------------+-------------------+\n| Last_SQL_Erro | Error message of the most recent | |\n| | error that caused the SQL thread to | |\n| | stop (also recorded in the replica\'s | |','','https://mariadb.com/kb/en/show-replica-status/'); update help_topic set description = CONCAT(description, '\n| | error log). An empty string means no | |\n| | error. RESET SLAVE or RESET MASTER | |\n| | will reset this value. | |\n+---------------+---------------------------------------+-------------------+\n| Replicate_Ign | List of server_ids that are | |\n| re_Server_Ids | currently being ignored for | |\n| | replication purposes, or an empty | |\n| | string for none, as specified in the | |\n| | IGNORE_SERVER_IDS option of the | |\n| | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Master_Server | The master\'s server_id value. | |\n| Id | | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_Cr | The MASTER_SSL_CRL option of the | |\n| | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_Cr | The MASTER_SSL_CRLPATH option of the | |\n| path | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Using_Gtid | Whether or not global transaction | |\n| | ID\'s are being used for replication | |\n| | (can be No, Slave_Pos, or | |\n| | Current_Pos). | |\n+---------------+---------------------------------------+-------------------+\n| Gtid_IO_Pos | Current global transaction ID value. | |\n+---------------+---------------------------------------+-------------------+\n| Retried_trans | Number of retried transactions for | |\n| ctions | this connection. Returned with SHOW | |\n| | ALL SLAVES STATUS only. | |\n+---------------+---------------------------------------+-------------------+\n| Max_relay_log | Max relay log size for this | |\n| size | connection. Returned with SHOW ALL | |\n| | SLAVES STATUS only. | |\n+---------------+---------------------------------------+-------------------+\n| Executed_log_ | How many log entries the replica has | |\n| ntries | executed. Returned with SHOW ALL | |\n| | SLAVES STATUS only. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_receive | How many heartbeats we have got from | |\n| _heartbeats | the master. Returned with SHOW ALL | |\n| | SLAVES STATUS only. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_heartbe | How often to request a heartbeat | |\n| t_period | packet from the master (in seconds). | |\n| | Returned with SHOW ALL SLAVES STATUS | |\n| | only. | |\n+---------------+---------------------------------------+-------------------+\n| Gtid_Slave_Po | GTID of the last event group | |\n| | replicated on a replica server, for | |\n| | each replication domain, as stored | |\n| | in the gtid_slave_pos system | |\n| | variable. Returned with SHOW ALL | |\n| | SLAVES STATUS only. | |\n+---------------+---------------------------------------+-------------------+\n| SQL_Delay | Value specified by MASTER_DELAY in | MariaDB 10.2.3 |\n| | CHANGE MASTER (or 0 if none). | |\n+---------------+---------------------------------------+-------------------+\n| SQL_Remaining | When the replica is delaying the | MariaDB 10.2.3 |\n| Delay | execution of an event due to | |\n| | MASTER_DELAY, this is the number of | |\n| | seconds of delay remaining before | |\n| | the event will be applied. | |\n| | Otherwise, the value is NULL. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_SQL_Run | The state of the SQL driver threads, | MariaDB 10.2.3 |\n| ing_State | same as in SHOW PROCESSLIST. When | |\n| | the replica is delaying the | |\n| | execution of an event due to | |\n| | MASTER_DELAY, this field displays: | |\n| | \"Waiting until MASTER_DELAY seconds | |\n| | after master executed event\". | |\n+---------------+---------------------------------------+-------------------+\n| Slave_DDL_Gro | This status variable counts the | MariaDB 10.3.7 |\n| ps | occurrence of DDL statements. This | |\n| | is a replica-side counter for | |\n| | optimistic parallel replication. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_Non_Tra | This status variable counts the | MariaDB 10.3.7 |\n| sactional_Gro | occurrence of non-transactional | |\n| ps | event groups. This is a | |\n| | replica-side counter for optimistic | |\n| | parallel replication. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_Transac | This status variable counts the | MariaDB 10.3.7 |\n| ional_Groups | occurrence of transactional event | |\n| | groups. This is a replica-side | |\n| | counter for optimistic parallel | |\n| | replication. | |\n+---------------+---------------------------------------+-------------------+\n\nSHOW REPLICA STATUS\n-------------------\n\nMariaDB starting with 10.5.1\n----------------------------\nSHOW REPLICA STATUS is an alias for SHOW SLAVE STATUS from MariaDB 10.5.1.\n\nExamples\n--------\n\nIf you issue this statement using the mysql client, you can use a \\G statement\nterminator rather than a semicolon to obtain a more readable vertical layout.\n\nSHOW SLAVE STATUS\\G\n*************************** 1. row ***************************\n Slave_IO_State: Waiting for master to send event\n Master_Host: db01.example.com\n Master_User: replicant\n Master_Port: 3306\n Connect_Retry: 60\n Master_Log_File: mariadb-bin.000010\n Read_Master_Log_Pos: 548\n Relay_Log_File: relay-bin.000004\n Relay_Log_Pos: 837\n Relay_Master_Log_File: mariadb-bin.000010\n Slave_IO_Running: Yes\n Slave_SQL_Running: Yes\n Replicate_Do_DB:\n Replicate_Ignore_DB:\n Replicate_Do_Table:\n Replicate_Ignore_Table:\n Replicate_Wild_Do_Table:\n Replicate_Wild_Ignore_Table:\n Last_Errno: 0\n Last_Error:\n Skip_Counter: 0\n Exec_Master_Log_Pos: 548\n Relay_Log_Space: 1497\n Until_Condition: None\n Until_Log_File:\n Until_Log_Pos: 0\n Master_SSL_Allowed: No\n Master_SSL_CA_File:\n Master_SSL_CA_Path:\n Master_SSL_Cert:\n Master_SSL_Cipher:\n Master_SSL_Key:\n Seconds_Behind_Master: 0\nMaster_SSL_Verify_Server_Cert: No\n Last_IO_Errno: 0\n Last_IO_Error:\n Last_SQL_Errno: 0\n Last_SQL_Error:\n Replicate_Ignore_Server_Ids:\n Master_Server_Id: 101\n Master_SSL_Crl:\n Master_SSL_Crlpath:\n Using_Gtid: No\n Gtid_IO_Pos:\n\nSHOW ALL SLAVES STATUS\\G\n*************************** 1. row ***************************\n Connection_name:\n Slave_SQL_State: Slave has read all relay log; waiting for the\nslave I/O thread to update it\n Slave_IO_State: Waiting for master to send event\n Master_Host: db01.example.com\n Master_User: replicant\n Master_Port: 3306\n Connect_Retry: 60\n Master_Log_File: mariadb-bin.000010\n Read_Master_Log_Pos: 3608\n Relay_Log_File: relay-bin.000004\n Relay_Log_Pos: 3897\n Relay_Master_Log_File: mariadb-bin.000010\n Slave_IO_Running: Yes\n Slave_SQL_Running: Yes\n Replicate_Do_DB:\n Replicate_Ignore_DB:\n Replicate_Do_Table:\n Replicate_Ignore_Table:\n Replicate_Wild_Do_Table:\n Replicate_Wild_Ignore_Table:\n Last_Errno: 0\n Last_Error:\n Skip_Counter: 0\n Exec_Master_Log_Pos: 3608\n Relay_Log_Space: 4557\n Until_Condition: None\n Until_Log_File:\n Until_Log_Pos: 0\n Master_SSL_Allowed: No\n Master_SSL_CA_File:\n Master_SSL_CA_Path:\n Master_SSL_Cert:\n Master_SSL_Cipher:\n Master_SSL_Key:\n Seconds_Behind_Master: 0\nMaster_SSL_Verify_Server_Cert: No\n Last_IO_Errno: 0\n Last_IO_Error:\n Last_SQL_Errno: 0\n Last_SQL_Error:\n Replicate_Ignore_Server_Ids:\n Master_Server_Id: 101\n Master_SSL_Crl:\n Master_SSL_Crlpath:\n Using_Gtid: No\n Gtid_IO_Pos:\n Retried_transactions: 0\n Max_relay_log_size: 104857600\n Executed_log_entries: 40\n Slave_received_heartbeats: 11\n Slave_heartbeat_period: 1800.000\n Gtid_Slave_Pos: 0-101-2320\n\nYou can also access some of the variables directly from status variables:\n\nSET @@default_master_connection=\"test\" ;\nshow status like \"%slave%\"\n\nVariable_name Value\nCom_show_slave_hosts 0\nCom_show_slave_status 0\nCom_start_all_slaves 0\nCom_start_slave 0\nCom_stop_all_slaves 0\nCom_stop_slave 0\nRpl_semi_sync_slave_status OFF\nSlave_connections 0\nSlave_heartbeat_period 1800.000\nSlave_open_temp_tables 0\nSlave_received_heartbeats 0\nSlave_retried_transactions 0\nSlave_running OFF\nSlaves_connected 0\nSlaves_running 1\n\nURL: https://mariadb.com/kb/en/show-replica-status/') WHERE help_topic_id = 345; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (346,26,'SHOW MASTER STATUS','Syntax\n------\n\nSHOW MASTER STATUS\nSHOW BINLOG STATUS -- From MariaDB 10.5.2\n\nDescription\n-----------\n\nProvides status information about the binary log files of the primary.\n\nThis statement requires the SUPER privilege, the REPLICATION_CLIENT privilege,\nor, from MariaDB 10.5.2, the BINLOG MONITOR privilege.\n\nTo see information about the current GTIDs in the binary log, use the\ngtid_binlog_pos variable.\n\nSHOW MASTER STATUS was renamed to SHOW BINLOG STATUS in MariaDB 10.5.2, but\nthe old name remains an alias for compatibility purposes.\n\nExample\n-------\n\nSHOW MASTER STATUS;\n+--------------------+----------+--------------+------------------+\n| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |\n+--------------------+----------+--------------+------------------+\n| mariadb-bin.000016 | 475 | | |\n+--------------------+----------+--------------+------------------+\nSELECT @@global.gtid_binlog_pos;\n+--------------------------+\n| @@global.gtid_binlog_pos |\n+--------------------------+\n| 0-1-2 |\n+--------------------------+\n\nURL: https://mariadb.com/kb/en/show-binlog-status/','','https://mariadb.com/kb/en/show-binlog-status/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (347,26,'SHOW SLAVE HOSTS','Syntax\n------\n\nSHOW SLAVE HOSTS\nSHOW REPLICA HOSTS -- from MariaDB 10.5.1\n\nDescription\n-----------\n\nThis command is run on the primary and displays a list of replicas that are\ncurrently registered with it. Only replicas started with the\n--report-host=host_name option are visible in this list.\n\nThe list is displayed on any server (not just the primary server). The output\nlooks like this:\n\nSHOW SLAVE HOSTS;\n+------------+-----------+------+-----------+\n| Server_id | Host | Port | Master_id |\n+------------+-----------+------+-----------+\n| 192168010 | iconnect2 | 3306 | 192168011 |\n| 1921680101 | athena | 3306 | 192168011 |\n+------------+-----------+------+-----------+\n\n* Server_id: The unique server ID of the replica server, as configured in the\nserver\'s option file, or on the command line with --server-id=value.\n* Host: The host name of the replica server, as configured in the server\'s\noption file, or on the command line with --report-host=host_name. Note that\nthis can differ from the machine name as configured in the operating system.\n* Port: The port the replica server is listening on.\n* Master_id: The unique server ID of the primary server that the replica\nserver is replicating from.\n\nSome MariaDB and MySQL versions report another variable, rpl_recovery_rank.\nThis variable was never used, and was eventually removed in MariaDB 10.1.2 .\n\nRequires the REPLICATION MASTER ADMIN privilege (>= MariaDB 10.5.2) or the\nREPLICATION SLAVE privilege (<= MariaDB 10.5.1).\n\nSHOW REPLICA HOSTS\n------------------\n\nMariaDB starting with 10.5.1\n----------------------------\nSHOW REPLICA HOSTS is an alias for SHOW SLAVE HOSTS from MariaDB 10.5.1.\n\nURL: https://mariadb.com/kb/en/show-replica-hosts/','','https://mariadb.com/kb/en/show-replica-hosts/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (347,26,'SHOW SLAVE HOSTS','Syntax\n------\n\nSHOW SLAVE HOSTS\nSHOW REPLICA HOSTS -- from MariaDB 10.5.1\n\nDescription\n-----------\n\nThis command is run on the primary and displays a list of replicas that are\ncurrently registered with it. Only replicas started with the\n--report-host=host_name option are visible in this list.\n\nThe output looks like this:\n\nSHOW SLAVE HOSTS;\n+------------+-----------+------+-----------+\n| Server_id | Host | Port | Master_id |\n+------------+-----------+------+-----------+\n| 192168010 | iconnect2 | 3306 | 192168011 |\n| 1921680101 | athena | 3306 | 192168011 |\n+------------+-----------+------+-----------+\n\n* Server_id: The unique server ID of the replica server, as configured in the\nserver\'s option file, or on the command line with --server-id=value.\n* Host: The host name of the replica server, as configured in the server\'s\noption file, or on the command line with --report-host=host_name. Note that\nthis can differ from the machine name as configured in the operating system.\n* Port: The port the replica server is listening on.\n* Master_id: The unique server ID of the primary server that the replica\nserver is replicating from.\n\nSome MariaDB and MySQL versions report another variable, rpl_recovery_rank.\nThis variable was never used, and was eventually removed in MariaDB 10.1.2 .\n\nRequires the REPLICATION MASTER ADMIN privilege (>= MariaDB 10.5.2) or the\nREPLICATION SLAVE privilege (<= MariaDB 10.5.1).\n\nSHOW REPLICA HOSTS\n------------------\n\nMariaDB starting with 10.5.1\n----------------------------\nSHOW REPLICA HOSTS is an alias for SHOW SLAVE HOSTS from MariaDB 10.5.1.\n\nURL: https://mariadb.com/kb/en/show-replica-hosts/','','https://mariadb.com/kb/en/show-replica-hosts/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (348,26,'SHOW PLUGINS','Syntax\n------\n\nSHOW PLUGINS;\n\nDescription\n-----------\n\nSHOW PLUGINS displays information about installed plugins. The Library column\nindicates the plugin library - if it is NULL, the plugin is built-in and\ncannot be uninstalled.\n\nThe PLUGINS table in the information_schema database contains more detailed\ninformation.\n\nFor specific information about storage engines (a particular type of plugin),\nsee the information_schema.ENGINES table and the SHOW ENGINES statement.\n\nExamples\n--------\n\nSHOW PLUGINS;\n+----------------------------+----------+--------------------+-------------+---\n-----+\n| Name | Status | Type | Library |\nLicense |\n+----------------------------+----------+--------------------+-------------+---\n-----+\n| binlog | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| mysql_native_password | ACTIVE | AUTHENTICATION | NULL |\nGPL |\n| mysql_old_password | ACTIVE | AUTHENTICATION | NULL |\nGPL |\n| MRG_MyISAM | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| MyISAM | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| CSV | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| MEMORY | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| FEDERATED | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| PERFORMANCE_SCHEMA | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| Aria | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| InnoDB | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| INNODB_TRX | ACTIVE | INFORMATION SCHEMA | NULL |\nGPL |\n...\n| INNODB_SYS_FOREIGN | ACTIVE | INFORMATION SCHEMA | NULL |\nGPL |\n| INNODB_SYS_FOREIGN_COLS | ACTIVE | INFORMATION SCHEMA | NULL |\nGPL |\n| SPHINX | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| ARCHIVE | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| BLACKHOLE | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| FEEDBACK | DISABLED | INFORMATION SCHEMA | NULL |\nGPL |\n| partition | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| pam | ACTIVE | AUTHENTICATION | auth_pam.so |\nGPL |\n+----------------------------+----------+--------------------+-------------+---\n-----+\n\nURL: https://mariadb.com/kb/en/show-plugins/','','https://mariadb.com/kb/en/show-plugins/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (349,26,'SHOW PLUGINS SONAME','Syntax\n------\n\nSHOW PLUGINS SONAME { library | LIKE \'pattern\' | WHERE expr };\n\nDescription\n-----------\n\nSHOW PLUGINS SONAME displays information about compiled-in and all server\nplugins in the plugin_dir directory, including plugins that haven\'t been\ninstalled.\n\nExamples\n--------\n\nSHOW PLUGINS SONAME \'ha_example.so\';\n+----------+---------------+----------------+---------------+---------+\n| Name | Status | Type | Library | License |\n+----------+---------------+----------------+---------------+---------+\n| EXAMPLE | NOT INSTALLED | STORAGE ENGINE | ha_example.so | GPL |\n| UNUSABLE | NOT INSTALLED | DAEMON | ha_example.so | GPL |\n+----------+---------------+----------------+---------------+---------+\n\nThere is also a corresponding information_schema table, called ALL_PLUGINS,\nwhich contains more complete information.\n\nURL: https://mariadb.com/kb/en/show-plugins-soname/','','https://mariadb.com/kb/en/show-plugins-soname/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (350,26,'SET','Syntax\n------\n\nSET variable_assignment [, variable_assignment] ...\n\nvariable_assignment:\n user_var_name = expr\n | [GLOBAL | SESSION] system_var_name = expr\n | [@@global. | @@session. | @@]system_var_name = expr\n\nOne can also set a user variable in any expression with this syntax:\n\nuser_var_name:= expr\n\nDescription\n-----------\n\nThe SET statement assigns values to different types of variables that affect\nthe operation of the server or your client. Older versions of MySQL employed\nSET OPTION, but this syntax was deprecated in favor of SET without OPTION, and\nwas removed in MariaDB 10.0.\n\nChanging a system variable by using the SET statement does not make the change\npermanently. To do so, the change must be made in a configuration file.\n\nFor setting variables on a per-query basis, see SET STATEMENT.\n\nSee SHOW VARIABLES for documentation on viewing server system variables.\n\nSee Server System Variables for a list of all the system variables.\n\nGLOBAL / SESSION\n----------------\n\nWhen setting a system variable, the scope can be specified as either GLOBAL or\nSESSION.\n\nA global variable change affects all new sessions. It does not affect any\ncurrently open sessions, including the one that made the change.\n\nA session variable change affects the current session only.\n\nIf the variable has a session value, not specifying either GLOBAL or SESSION\nwill be the same as specifying SESSION. If the variable only has a global\nvalue, not specifying GLOBAL or SESSION will apply to the change to the global\nvalue.\n\nDEFAULT\n-------\n\nSetting a global variable to DEFAULT will restore it to the server default,\nand setting a session variable to DEFAULT will restore it to the current\nglobal value.\n\nExamples\n--------\n\n* innodb_sync_spin_loops is a global variable.\n* skip_parallel_replication is a session variable.\n* max_error_count is both global and session.\n\nSELECT VARIABLE_NAME, SESSION_VALUE, GLOBAL_VALUE FROM\n INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE \n VARIABLE_NAME IN (\'max_error_count\', \'skip_parallel_replication\',\n\'innodb_sync_spin_loops\');\n+---------------------------+---------------+--------------+\n| VARIABLE_NAME | SESSION_VALUE | GLOBAL_VALUE |\n+---------------------------+---------------+--------------+\n| MAX_ERROR_COUNT | 64 | 64 |\n| SKIP_PARALLEL_REPLICATION | OFF | NULL |\n| INNODB_SYNC_SPIN_LOOPS | NULL | 30 |\n+---------------------------+---------------+--------------+\n\nSetting the session values:\n\nSET max_error_count=128;Query OK, 0 rows affected (0.000 sec)\n\nSET skip_parallel_replication=ON;Query OK, 0 rows affected (0.000 sec)\n\nSET innodb_sync_spin_loops=60;\nERROR 1229 (HY000): Variable \'innodb_sync_spin_loops\' is a GLOBAL variable \n and should be set with SET GLOBAL\n\nSELECT VARIABLE_NAME, SESSION_VALUE, GLOBAL_VALUE FROM\n INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE \n VARIABLE_NAME IN (\'max_error_count\', \'skip_parallel_replication\',\n\'innodb_sync_spin_loops\');\n+---------------------------+---------------+--------------+\n| VARIABLE_NAME | SESSION_VALUE | GLOBAL_VALUE |\n+---------------------------+---------------+--------------+\n| MAX_ERROR_COUNT | 128 | 64 |\n| SKIP_PARALLEL_REPLICATION | ON | NULL |\n| INNODB_SYNC_SPIN_LOOPS | NULL | 30 |\n+---------------------------+---------------+--------------+\n\nSetting the global values:\n\nSET GLOBAL max_error_count=256;\n\nSET GLOBAL skip_parallel_replication=ON;\nERROR 1228 (HY000): Variable \'skip_parallel_replication\' is a SESSION variable \n and can\'t be used with SET GLOBAL\n\nSET GLOBAL innodb_sync_spin_loops=120;\n\nSELECT VARIABLE_NAME, SESSION_VALUE, GLOBAL_VALUE FROM\n INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE \n VARIABLE_NAME IN (\'max_error_count\', \'skip_parallel_replication\',\n\'innodb_sync_spin_loops\');\n+---------------------------+---------------+--------------+\n| VARIABLE_NAME | SESSION_VALUE | GLOBAL_VALUE |\n+---------------------------+---------------+--------------+\n| MAX_ERROR_COUNT | 128 | 256 |\n| SKIP_PARALLEL_REPLICATION | ON | NULL |\n| INNODB_SYNC_SPIN_LOOPS | NULL | 120 |\n+---------------------------+---------------+--------------+\n\nSHOW VARIABLES will by default return the session value unless the variable is\nglobal only.\n\nSHOW VARIABLES LIKE \'max_error_count\';\n+-----------------+-------+\n| Variable_name | Value |\n+-----------------+-------+\n| max_error_count | 128 |\n+-----------------+-------+\n\nSHOW VARIABLES LIKE \'skip_parallel_replication\';\n+---------------------------+-------+\n| Variable_name | Value |\n+---------------------------+-------+\n| skip_parallel_replication | ON |\n+---------------------------+-------+\n\nSHOW VARIABLES LIKE \'innodb_sync_spin_loops\';\n+------------------------+-------+\n| Variable_name | Value |\n+------------------------+-------+\n| innodb_sync_spin_loops | 120 |\n+------------------------+-------+\n\nUsing the inplace syntax:\n\nSELECT (@a:=1);\n+---------+\n| (@a:=1) |\n+---------+\n| 1 |\n+---------+\n\nSELECT @a;\n+------+\n| @a |\n+------+\n| 1 |\n+------+\n\nURL: https://mariadb.com/kb/en/set/','','https://mariadb.com/kb/en/set/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (351,26,'SET CHARACTER SET','Syntax\n------\n\nSET {CHARACTER SET | CHARSET}\n {charset_name | DEFAULT}\n\nDescription\n-----------\n\nSets the character_set_client and character_set_results session system\nvariables to the specified character set and collation_connection to the value\nof collation_database, which implicitly sets character_set_connection to the\nvalue of character_set_database.\n\nThis maps all strings sent between the current client and the server with the\ngiven mapping.\n\nExample\n-------\n\nSHOW VARIABLES LIKE \'character_set\\_%\';\n+--------------------------+--------+\n| Variable_name | Value |\n+--------------------------+--------+\n| character_set_client | utf8 |\n| character_set_connection | utf8 |\n| character_set_database | latin1 |\n| character_set_filesystem | binary |\n| character_set_results | utf8 |\n| character_set_server | latin1 |\n| character_set_system | utf8 |\n+--------------------------+--------+\n\nSHOW VARIABLES LIKE \'collation%\';\n+----------------------+-------------------+\n| Variable_name | Value |\n+----------------------+-------------------+\n| collation_connection | utf8_general_ci |\n| collation_database | latin1_swedish_ci |\n| collation_server | latin1_swedish_ci |\n+----------------------+-------------------+\n\nSET CHARACTER SET utf8mb4;\n\nSHOW VARIABLES LIKE \'character_set\\_%\';\n+--------------------------+---------+\n| Variable_name | Value |\n+--------------------------+---------+\n| character_set_client | utf8mb4 |\n| character_set_connection | latin1 |\n| character_set_database | latin1 |\n| character_set_filesystem | binary |\n| character_set_results | utf8mb4 |\n| character_set_server | latin1 |\n| character_set_system | utf8 |\n+--------------------------+---------+\n\nSHOW VARIABLES LIKE \'collation%\';\n+----------------------+-------------------+\n| Variable_name | Value |\n+----------------------+-------------------+\n| collation_connection | latin1_swedish_ci |\n| collation_database | latin1_swedish_ci |\n| collation_server | latin1_swedish_ci |\n+----------------------+-------------------+\n\nURL: https://mariadb.com/kb/en/set-character-set/','','https://mariadb.com/kb/en/set-character-set/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (352,26,'SET NAMES','Syntax\n------\n\nSET NAMES {\'charset_name\'\n [COLLATE \'collation_name\'] | DEFAULT}\n\nDescription\n-----------\n\nSets the character_set_client, character_set_connection, character_set_results\nand, implicitly, the collation_connection session system variables to the\nspecified character set and collation.\n\nThis determines which character set the client will use to send statements to\nthe server, and the server will use for sending results back to the client.\n\nucs2, utf16, and utf32 are not valid character sets for SET NAMES, as they\ncannot be used as client character sets.\n\nThe collation clause is optional. If not defined (or if DEFAULT is specified),\nthe default collation for the character set will be used.\n\nQuotes are optional for the character set or collation clauses.\n\nExamples\n--------\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+-----------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+-----------------+\n| CHARACTER_SET_RESULTS | utf8 |\n| CHARACTER_SET_CONNECTION | utf8 |\n| CHARACTER_SET_CLIENT | utf8 |\n| COLLATION_CONNECTION | utf8_general_ci |\n+--------------------------+-----------------+\n\nSET NAMES big5;\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+-----------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+-----------------+\n| CHARACTER_SET_RESULTS | big5 |\n| CHARACTER_SET_CONNECTION | big5 |\n| CHARACTER_SET_CLIENT | big5 |\n| COLLATION_CONNECTION | big5_chinese_ci |\n+--------------------------+-----------------+\n\nSET NAMES \'latin1\' COLLATE \'latin1_bin\';\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+---------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+---------------+\n| CHARACTER_SET_RESULTS | latin1 |\n| CHARACTER_SET_CONNECTION | latin1 |\n| CHARACTER_SET_CLIENT | latin1 |\n| COLLATION_CONNECTION | latin1_bin |\n+--------------------------+---------------+\n\nSET NAMES DEFAULT;\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+-------------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+-------------------+\n| CHARACTER_SET_RESULTS | latin1 |\n| CHARACTER_SET_CONNECTION | latin1 |\n| CHARACTER_SET_CLIENT | latin1 |\n| COLLATION_CONNECTION | latin1_swedish_ci |\n+--------------------------+-------------------+\n\nURL: https://mariadb.com/kb/en/set-names/','','https://mariadb.com/kb/en/set-names/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (352,26,'SET NAMES','Syntax\n------\n\nSET NAMES {\'charset_name\'\n [COLLATE \'collation_name\'] | DEFAULT}\n\nDescription\n-----------\n\nSets the character_set_client, character_set_connection, character_set_results\nand, implicitly, the collation_connection session system variables to the\nspecified character set and collation.\n\nThis determines which character set the client will use to send statements to\nthe server, and the server will use for sending results back to the client.\n\nucs2, utf16, utf16le and utf32 are not valid character sets for SET NAMES, as\nthey cannot be used as client character sets.\n\nThe collation clause is optional. If not defined (or if DEFAULT is specified),\nthe default collation for the character set will be used.\n\nQuotes are optional for the character set or collation clauses.\n\nExamples\n--------\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+-----------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+-----------------+\n| CHARACTER_SET_RESULTS | utf8 |\n| CHARACTER_SET_CONNECTION | utf8 |\n| CHARACTER_SET_CLIENT | utf8 |\n| COLLATION_CONNECTION | utf8_general_ci |\n+--------------------------+-----------------+\n\nSET NAMES big5;\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+-----------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+-----------------+\n| CHARACTER_SET_RESULTS | big5 |\n| CHARACTER_SET_CONNECTION | big5 |\n| CHARACTER_SET_CLIENT | big5 |\n| COLLATION_CONNECTION | big5_chinese_ci |\n+--------------------------+-----------------+\n\nSET NAMES \'latin1\' COLLATE \'latin1_bin\';\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+---------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+---------------+\n| CHARACTER_SET_RESULTS | latin1 |\n| CHARACTER_SET_CONNECTION | latin1 |\n| CHARACTER_SET_CLIENT | latin1 |\n| COLLATION_CONNECTION | latin1_bin |\n+--------------------------+---------------+\n\nSET NAMES DEFAULT;\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+-------------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+-------------------+\n| CHARACTER_SET_RESULTS | latin1 |\n| CHARACTER_SET_CONNECTION | latin1 |\n| CHARACTER_SET_CLIENT | latin1 |\n| COLLATION_CONNECTION | latin1_swedish_ci |\n+--------------------------+-------------------+\n\nURL: https://mariadb.com/kb/en/set-names/','','https://mariadb.com/kb/en/set-names/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (353,26,'SET SQL_LOG_BIN','Syntax\n------\n\nSET [SESSION] sql_log_bin = {0|1}\n\nDescription\n-----------\n\nSets the sql_log_bin system variable, which disables or enables binary logging\nfor the current connection, if the client has the SUPER privilege. The\nstatement is refused with an error if the client does not have that privilege.\n\nBefore MariaDB 5.5 and before MySQL 5.6 one could also set sql_log_bin as a\nglobal variable. This was disabled as this was too dangerous as it could\ndamage replication.\n\nURL: https://mariadb.com/kb/en/set-sql_log_bin/','','https://mariadb.com/kb/en/set-sql_log_bin/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (354,26,'SET STATEMENT','MariaDB starting with 10.1.2\n----------------------------\nPer-query variables were introduced in MariaDB 10.1.2\n\nSET STATEMENT can be used to set the value of a system variable for the\nduration of the statement. It is also possible to set multiple variables.\n\nSyntax\n------\n\nSET STATEMENT var1=value1 [, var2=value2, ...] \n FOR \n\nwhere varN is a system variable (list of allowed variables is provided below),\nand valueN is a constant literal.\n\nDescription\n-----------\n\nSET STATEMENT var1=value1 FOR stmt\n\nis roughly equivalent to\n\nSET @save_value=@@var1;\nSET SESSION var1=value1;\nstmt;\nSET SESSION var1=@save_value;\n\nThe server parses the whole statement before executing it, so any variables\nset in this fashion that affect the parser may not have the expected effect.\nExamples include the charset variables, sql_mode=ansi_quotes, etc.\n\nExamples\n--------\n\nOne can limit statement execution time max_statement_time:\n\nSET STATEMENT max_statement_time=1000 FOR SELECT ... ;\n\nOne can switch on/off individual optimizations:\n\nSET STATEMENT optimizer_switch=\'materialization=off\' FOR SELECT ....;\n\nIt is possible to enable MRR/BKA for a query:\n\nSET STATEMENT join_cache_level=6, optimizer_switch=\'mrr=on\' FOR SELECT ...\n\nNote that it makes no sense to try to set a session variable inside a SET\nSTATEMENT:\n\n#USELESS STATEMENT\nSET STATEMENT sort_buffer_size = 100000 for SET SESSION sort_buffer_size =\n200000;\n\nFor the above, after setting sort_buffer_size to 200000 it will be reset to\nits original state (the state before the SET STATEMENT started) after the\nstatement execution.\n\nLimitations\n-----------\n\nThere are a number of variables that cannot be set on per-query basis. These\ninclude:\n\n* autocommit\n* character_set_client\n* character_set_connection\n* character_set_filesystem\n* collation_connection\n* default_master_connection\n* debug_sync\n* interactive_timeout\n* gtid_domain_id\n* last_insert_id\n* log_slow_filter\n* log_slow_rate_limit\n* log_slow_verbosity\n* long_query_time\n* min_examined_row_limit\n* profiling\n* profiling_history_size\n* query_cache_type\n* rand_seed1\n* rand_seed2\n* skip_replication\n* slow_query_log\n* sql_log_off\n* tx_isolation\n* wait_timeout\n\nSource\n------\n\n* The feature was originally implemented as a Google Summer of Code 2009\nproject by Joseph Lukas. \n* Percona Server 5.6 included it as Per-query variable statement\n* MariaDB ported the patch and fixed many bugs. The task in MariaDB Jira is\nMDEV-5231.\n\nURL: https://mariadb.com/kb/en/set-statement/','','https://mariadb.com/kb/en/set-statement/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (355,26,'SET Variable','Syntax\n------\n\nSET var_name = expr [, var_name = expr] ...\n\nDescription\n-----------\n\nThe SET statement in stored programs is an extended version of the general SET\nstatement. Referenced variables may be ones declared inside a stored program,\nglobal system variables, or user-defined variables.\n\nThe SET statement in stored programs is implemented as part of the\npre-existing SET syntax. This allows an extended syntax of SET a=x, b=y, ...\nwhere different variable types (locally declared variables, global and session\nserver variables, user-defined variables) can be mixed. This also allows\ncombinations of local variables and some options that make sense only for\nsystem variables; in that case, the options are recognized but ignored.\n\nSET can be used with both local variables and user-defined variables.\n\nWhen setting several variables using the columns returned by a query, SELECT\nINTO should be preferred.\n\nTo set many variables to the same value, the LAST_VALUE( ) function can be\nused.\n\nBelow is an example of how a user-defined variable may be set:\n\nSET @x = 1;\n\nURL: https://mariadb.com/kb/en/set-variable/','','https://mariadb.com/kb/en/set-variable/'); @@ -522,7 +522,7 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (424,27,'Subqueries and JOINs','A subquery can quite often, but not in all cases, be rewritten as a JOIN.\n\nRewriting Subqueries as JOINS\n-----------------------------\n\nA subquery using IN can be rewritten with the DISTINCT keyword, for example:\n\nSELECT * FROM table1 WHERE col1 IN (SELECT col1 FROM table2);\n\ncan be rewritten as:\n\nSELECT DISTINCT table1.* FROM table1, table2 WHERE table1.col1=table2.col1;\n\nNOT IN or NOT EXISTS queries can also be rewritten. For example, these two\nqueries returns the same result:\n\nSELECT * FROM table1 WHERE col1 NOT IN (SELECT col1 FROM table2);\nSELECT * FROM table1 WHERE NOT EXISTS (SELECT col1 FROM table2 WHERE\ntable1.col1=table2.col1);\n\nand both can be rewritten as:\n\nSELECT table1.* FROM table1 LEFT JOIN table2 ON table1.id=table2.id WHERE\ntable2.id IS NULL;\n\nSubqueries that can be rewritten as a LEFT JOIN are sometimes more efficient.\n\nUsing Subqueries instead of JOINS\n---------------------------------\n\nThere are some scenarios, though, which call for subqueries rather than joins:\n\n* When you want duplicates, but not false duplicates. Suppose Table_1\n has three rows — {1,1,2}\n — and Table_2 has two rows\n — {1,2,2}. If you need to list the rows\n in Table_1 which are also in Table_2, only this\n subquery-based SELECT statement will give the right answer\n (1,1,2):\n\nSELECT Table_1.column_1 \nFROM Table_1 \nWHERE Table_1.column_1 IN \n (SELECT Table_2.column_1\n FROM Table_2);\n\n* This SQL statement won\'t work:\n\nSELECT Table_1.column_1 \nFROM Table_1,Table_2 \nWHERE Table_1.column_1 = Table_2.column_1;\n\n* because the result will be {1,1,2,2}\n — and the duplication of 2 is an error. This SQL\n statement won\'t work either:\n\nSELECT DISTINCT Table_1.column_1 \nFROM Table_1,Table_2 \nWHERE Table_1.column_1 = Table_2.column_1;\n\n* because the result will be {1,2} — and\n the removal of the duplicated 1 is an error too.\n\n* When the outermost statement is not a query. The SQL statement:\n\nUPDATE Table_1 SET column_1 = (SELECT column_1 FROM Table_2);\n\n* can\'t be expressed using a join unless some rare SQL3 features are used.\n\n* When the join is over an expression. The SQL statement:\n\nSELECT * FROM Table_1 \nWHERE column_1 + 5 =\n (SELECT MAX(column_1) FROM Table_2);\n\n* is hard to express with a join. In fact, the only way we can think of is\n this SQL statement:\n\nSELECT Table_1.*\nFROM Table_1, \n (SELECT MAX(column_1) AS max_column_1 FROM Table_2) AS Table_2\nWHERE Table_1.column_1 + 5 = Table_2.max_column_1;\n\n* which still involves a parenthesized query, so nothing is gained from the\n transformation.\n\n* When you want to see the exception. For example, suppose the question is:\n what books are longer than Das Kapital? These two queries are effectively\n almost the same:\n\nSELECT DISTINCT Bookcolumn_1.* \nFROM Books AS Bookcolumn_1 JOIN Books AS Bookcolumn_2 USING(page_count) \nWHERE title = \'Das Kapital\';\n\nSELECT DISTINCT Bookcolumn_1.* \nFROM Books AS Bookcolumn_1 \nWHERE Bookcolumn_1.page_count > \n (SELECT DISTINCT page_count\n FROM Books AS Bookcolumn_2\n WHERE title = \'Das Kapital\');\n\n* The difference is between these two SQL statements is, if there are two\n editions of Das Kapital (with different page counts), then the self-join\n example will return the books which are longer than the shortest edition\n of Das Kapital. That might be the wrong answer, since the original\n question didn\'t ask for \"... longer than ANY book named Das Kapital\"\n (it seems to contain a false assumption that there\'s only one edition).\n\nURL: https://mariadb.com/kb/en/subqueries-and-joins/','','https://mariadb.com/kb/en/subqueries-and-joins/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (425,27,'Subquery Limitations','There are a number of limitations regarding subqueries, which are discussed\nbelow. The following tables and data will be used in the examples that follow:\n\nCREATE TABLE staff(name VARCHAR(10),age TINYINT);\n\nCREATE TABLE customer(name VARCHAR(10),age TINYINT);\n\nINSERT INTO staff VALUES \n(\'Bilhah\',37), (\'Valerius\',61), (\'Maia\',25);\n\nINSERT INTO customer VALUES \n(\'Thanasis\',48), (\'Valerius\',61), (\'Brion\',51);\n\nORDER BY and LIMIT\n------------------\n\nTo use ORDER BY or limit LIMIT in subqueries both must be used.. For example:\n\nSELECT * FROM staff WHERE name IN (SELECT name FROM customer ORDER BY name);\n+----------+------+\n| name | age |\n+----------+------+\n| Valerius | 61 |\n+----------+------+\n\nis valid, but\n\nSELECT * FROM staff WHERE name IN (SELECT NAME FROM customer ORDER BY name\nLIMIT 1);\nERROR 1235 (42000): This version of MariaDB doesn\'t \n yet support \'LIMIT & IN/ALL/ANY/SOME subquery\'\n\nis not.\n\nModifying and Selecting from the Same Table\n-------------------------------------------\n\nIt\'s not possible to both modify and select from the same table in a subquery.\nFor example:\n\nDELETE FROM staff WHERE name = (SELECT name FROM staff WHERE age=61);\nERROR 1093 (HY000): Table \'staff\' is specified twice, both \n as a target for \'DELETE\' and as a separate source for data\n\nRow Comparison Operations\n-------------------------\n\nThere is only partial support for row comparison operations. The expression in\n\nexpr op {ALL|ANY|SOME} subquery,\n\nmust be scalar and the subquery can only return a single column.\n\nHowever, because of the way IN is implemented (it is rewritten as a sequence\nof = comparisons and AND), the expression in\n\nexpression [NOT] IN subquery\n\nis permitted to be an n-tuple and the subquery can return rows of n-tuples.\n\nFor example:\n\nSELECT * FROM staff WHERE (name,age) NOT IN (\n SELECT name,age FROM customer WHERE age >=51]\n);\n+--------+------+\n| name | age |\n+--------+------+\n| Bilhah | 37 |\n| Maia | 25 |\n+--------+------+\n\nis permitted, but\n\nSELECT * FROM staff WHERE (name,age) = ALL (\n SELECT name,age FROM customer WHERE age >=51\n);\nERROR 1241 (21000): Operand should contain 1 column(s)\n\nis not.\n\nCorrelated Subqueries\n---------------------\n\nSubqueries in the FROM clause cannot be correlated subqueries. They cannot be\nevaluated for each row of the outer query since they are evaluated to produce\na result set during when the query is executed.\n\nStored Functions\n----------------\n\nA subquery can refer to a stored function which modifies data. This is an\nextension to the SQL standard, but can result in indeterminate outcomes. For\nexample, take:\n\nSELECT ... WHERE x IN (SELECT f() ...);\n\nwhere f() inserts rows. The function f() could be executed a different number\nof times depending on how the optimizer chooses to handle the query.\n\nThis sort of construct is therefore not safe to use in replication that is not\nrow-based, as there could be different results on the master and the slave.\n\nURL: https://mariadb.com/kb/en/subquery-limitations/','','https://mariadb.com/kb/en/subquery-limitations/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (426,27,'UNION','UNION is used to combine the results from multiple SELECT statements into a\nsingle result set.\n\nSyntax\n------\n\nSELECT ...\nUNION [ALL | DISTINCT] SELECT ...\n[UNION [ALL | DISTINCT] SELECT ...]\n[ORDER BY [column [, column ...]]]\n[LIMIT {[offset,] row_count | row_count OFFSET offset}]\n\nDescription\n-----------\n\nUNION is used to combine the results from multiple SELECT statements into a\nsingle result set.\n\nThe column names from the first SELECT statement are used as the column names\nfor the results returned. Selected columns listed in corresponding positions\nof each SELECT statement should have the same data type. (For example, the\nfirst column selected by the first statement should have the same type as the\nfirst column selected by the other statements.)\n\nIf they don\'t, the type and length of the columns in the result take into\naccount the values returned by all of the SELECTs, so there is no need for\nexplicit casting. Note that currently this is not the case for recursive CTEs\n- see MDEV-12325.\n\nTable names can be specified as db_name.tbl_name. This permits writing UNIONs\nwhich involve multiple databases. See Identifier Qualifiers for syntax details.\n\nUNION queries cannot be used with aggregate functions.\n\nEXCEPT and UNION have the same operation precedence and INTERSECT has a higher\nprecedence, unless running in Oracle mode, in which case all three have the\nsame precedence.\n\nALL/DISTINCT\n------------\n\nThe ALL keyword causes duplicate rows to be preserved. The DISTINCT keyword\n(the default if the keyword is omitted) causes duplicate rows to be removed by\nthe results.\n\nUNION ALL and UNION DISTINCT can both be present in a query. In this case,\nUNION DISTINCT will override any UNION ALLs to its left.\n\nMariaDB starting with 10.1.1\n----------------------------\nUntil MariaDB 10.1.1, all UNION ALL statements required the server to create a\ntemporary table. Since MariaDB 10.1.1, the server can in most cases execute\nUNION ALL without creating a temporary table, improving performance (see\nMDEV-334).\n\nORDER BY and LIMIT\n------------------\n\nIndividual SELECTs can contain their own ORDER BY and LIMIT clauses. In this\ncase, the individual queries need to be wrapped between parentheses. However,\nthis does not affect the order of the UNION, so they only are useful to limit\nthe record read by one SELECT.\n\nThe UNION can have global ORDER BY and LIMIT clauses, which affect the whole\nresultset. If the columns retrieved by individual SELECT statements have an\nalias (AS), the ORDER BY must use that alias, not the real column names.\n\nHIGH_PRIORITY\n-------------\n\nSpecifying a query as HIGH_PRIORITY will not work inside a UNION. If applied\nto the first SELECT, it will be ignored. Applying to a later SELECT results in\na syntax error:\n\nERROR 1234 (42000): Incorrect usage/placement of \'HIGH_PRIORITY\'\n\nSELECT ... INTO ...\n-------------------\n\nIndividual SELECTs cannot be written INTO DUMPFILE or INTO OUTFILE. If the\nlast SELECT statement specifies INTO DUMPFILE or INTO OUTFILE, the entire\nresult of the UNION will be written. Placing the clause after any other SELECT\nwill result in a syntax error.\n\nIf the result is a single row, SELECT ... INTO @var_name can also be used.\n\nMariaDB starting with 10.4.0\n----------------------------\n\nParentheses\n-----------\n\nFrom MariaDB 10.4.0, parentheses can be used to specify precedence. Before\nthis, a syntax error would be returned.\n\nExamples\n--------\n\nUNION between tables having different column names:\n\n(SELECT e_name AS name, email FROM employees)\nUNION\n(SELECT c_name AS name, email FROM customers);\n\nSpecifying the UNION\'s global order and limiting total rows:\n\n(SELECT name, email FROM employees)\nUNION\n(SELECT name, email FROM customers)\nORDER BY name LIMIT 10;\n\nAdding a constant row:\n\n(SELECT \'John Doe\' AS name, \'john.doe@example.net\' AS email)\nUNION\n(SELECT name, email FROM customers);\n\nDiffering types:\n\nSELECT CAST(\'x\' AS CHAR(1)) UNION SELECT REPEAT(\'y\',4);\n+----------------------+\n| CAST(\'x\' AS CHAR(1)) |\n+----------------------+\n| x |\n| yyyy |\n+----------------------+\n\nReturning the results in order of each individual SELECT by use of a sort\ncolumn:\n\n(SELECT 1 AS sort_column, e_name AS name, email FROM employees)\nUNION\n(SELECT 2, c_name AS name, email FROM customers) ORDER BY sort_column;\n\nDifference between UNION, EXCEPT and INTERSECT. INTERSECT ALL and EXCEPT ALL\nare available from MariaDB 10.5.0.\n\nCREATE TABLE seqs (i INT);\nINSERT INTO seqs VALUES (1),(2),(2),(3),(3),(4),(5),(6);\n\nSELECT i FROM seqs WHERE i <= 3 UNION SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 UNION ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n| 3 |\n| 3 |\n| 3 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n| 3 |\n+------+\n\nParentheses for specifying precedence, from MariaDB 10.4.0\n\nCREATE OR REPLACE TABLE t1 (a INT);\nCREATE OR REPLACE TABLE t2 (b INT);\nCREATE OR REPLACE TABLE t3 (c INT);\n\nINSERT INTO t1 VALUES (1),(2),(3),(4);\nINSERT INTO t2 VALUES (5),(6);\nINSERT INTO t3 VALUES (1),(6);\n\n((SELECT a FROM t1) UNION (SELECT b FROM t2)) INTERSECT (SELECT c FROM t3);\n+------+\n| a |\n+------+\n| 1 |\n| 6 |\n+------+\n\n(SELECT a FROM t1) UNION ((SELECT b FROM t2) INTERSECT (SELECT c FROM t3));\n+------+\n| a |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 6 |\n+------+\n\nURL: https://mariadb.com/kb/en/union/','','https://mariadb.com/kb/en/union/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (427,27,'EXCEPT','MariaDB starting with 10.3.0\n----------------------------\nEXCEPT was introduced in MariaDB 10.3.0.\n\nThe result of EXCEPT is all records of the left SELECT result set except\nrecords which are in right SELECT result set, i.e. it is subtraction of two\nresult sets. From MariaDB 10.6.1, MINUS is a synonym.\n\nSyntax\n------\n\nSELECT ...\n(INTERSECT [ALL | DISTINCT] | EXCEPT [ALL | DISTINCT] | UNION [ALL |\nDISTINCT]) SELECT ...\n[(INTERSECT [ALL | DISTINCT] | EXCEPT [ALL | DISTINCT] | UNION [ALL |\nDISTINCT]) SELECT ...]\n[ORDER BY [column [, column ...]]]\n[LIMIT {[offset,] row_count | row_count OFFSET offset}]\n\nPlease note:\n\n* Brackets for explicit operation precedence are not supported; use a subquery\nin the FROM clause as a workaround).\n\nDescription\n-----------\n\nMariaDB has supported EXCEPT and INTERSECT in addition to UNION since MariaDB\n10.3.\n\nAll behavior for naming columns, ORDER BY and LIMIT is the same as for UNION.\n\nEXCEPT implicitly supposes a DISTINCT operation.\n\nThe result of EXCEPT is all records of the left SELECT result except records\nwhich are in right SELECT result set, i.e. it is subtraction of two result\nsets.\n\nEXCEPT and UNION have the same operation precedence and INTERSECT has a higher\nprecedence, unless running in Oracle mode, in which case all three have the\nsame precedence.\n\nMariaDB starting with 10.4.0\n----------------------------\n\nParentheses\n-----------\n\nFrom MariaDB 10.4.0, parentheses can be used to specify precedence. Before\nthis, a syntax error would be returned.\n\nMariaDB starting with 10.5.0\n----------------------------\n\nALL/DISTINCT\n------------\n\nEXCEPT ALL and EXCEPT DISTINCT were introduced in MariaDB 10.5.0. The ALL\noperator leaves duplicates intact, while the DISTINCT operator removes\nduplicates. DISTINCT is the default behavior if neither operator is supplied,\nand the only behavior prior to MariaDB 10.5.\n\nExamples\n--------\n\nShow customers which are not employees:\n\n(SELECT e_name AS name, email FROM customers)\nEXCEPT\n(SELECT c_name AS name, email FROM employees);\n\nDifference between UNION, EXCEPT and INTERSECT. INTERSECT ALL and EXCEPT ALL\nare available from MariaDB 10.5.0.\n\nCREATE TABLE seqs (i INT);\nINSERT INTO seqs VALUES (1),(2),(2),(3),(3),(4),(5),(6);\n\nSELECT i FROM seqs WHERE i <= 3 UNION SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 UNION ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n| 3 |\n| 3 |\n| 3 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n| 3 |\n+------+\n\nParentheses for specifying precedence, from MariaDB 10.4.0\n\nCREATE OR REPLACE TABLE t1 (a INT);\nCREATE OR REPLACE TABLE t2 (b INT);\nCREATE OR REPLACE TABLE t3 (c INT);\n\nINSERT INTO t1 VALUES (1),(2),(3),(4);\nINSERT INTO t2 VALUES (5),(6);\nINSERT INTO t3 VALUES (1),(6);\n\n((SELECT a FROM t1) UNION (SELECT b FROM t2)) EXCEPT (SELECT c FROM t3);\n+------+\n| a |\n+------+\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n+------+\n\n(SELECT a FROM t1) UNION ((SELECT b FROM t2) EXCEPT (SELECT c FROM t3));\n+------+\n| a |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n+------+\n\nURL: https://mariadb.com/kb/en/except/','','https://mariadb.com/kb/en/except/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (427,27,'EXCEPT','MariaDB starting with 10.3.0\n----------------------------\nEXCEPT was introduced in MariaDB 10.3.0.\n\nThe result of EXCEPT is all records of the left SELECT result set except\nrecords which are in right SELECT result set, i.e. it is subtraction of two\nresult sets. From MariaDB 10.6.1, MINUS is a synonym.\n\nSyntax\n------\n\nSELECT ...\n(INTERSECT [ALL | DISTINCT] | EXCEPT [ALL | DISTINCT] | UNION [ALL |\nDISTINCT]) SELECT ...\n[(INTERSECT [ALL | DISTINCT] | EXCEPT [ALL | DISTINCT] | UNION [ALL |\nDISTINCT]) SELECT ...]\n[ORDER BY [{col_name | expr | position} [ASC | DESC] [, {col_name | expr |\nposition} [ASC | DESC] ...]]]\n[LIMIT {[offset,] row_count | row_count OFFSET offset}\n| OFFSET start { ROW | ROWS }\n| FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES } ]\n\nPlease note:\n\n* Brackets for explicit operation precedence are not supported; use a subquery\nin the FROM clause as a workaround).\n\nDescription\n-----------\n\nMariaDB has supported EXCEPT and INTERSECT in addition to UNION since MariaDB\n10.3.\n\nThe queries before and after EXCEPT must be SELECT or VALUES statements.\n\nAll behavior for naming columns, ORDER BY and LIMIT is the same as for UNION.\nNote that the alternative SELECT ... OFFSET ... FETCH syntax is only\nsupported. This allows us to use the WITH TIES clause.\n\nEXCEPT implicitly supposes a DISTINCT operation.\n\nThe result of EXCEPT is all records of the left SELECT result except records\nwhich are in right SELECT result set, i.e. it is subtraction of two result\nsets.\n\nEXCEPT and UNION have the same operation precedence and INTERSECT has a higher\nprecedence, unless running in Oracle mode, in which case all three have the\nsame precedence.\n\nMariaDB starting with 10.4.0\n----------------------------\n\nParentheses\n-----------\n\nFrom MariaDB 10.4.0, parentheses can be used to specify precedence. Before\nthis, a syntax error would be returned.\n\nMariaDB starting with 10.5.0\n----------------------------\n\nALL/DISTINCT\n------------\n\nEXCEPT ALL and EXCEPT DISTINCT were introduced in MariaDB 10.5.0. The ALL\noperator leaves duplicates intact, while the DISTINCT operator removes\nduplicates. DISTINCT is the default behavior if neither operator is supplied,\nand the only behavior prior to MariaDB 10.5.\n\nExamples\n--------\n\nShow customers which are not employees:\n\n(SELECT e_name AS name, email FROM customers)\nEXCEPT\n(SELECT c_name AS name, email FROM employees);\n\nDifference between UNION, EXCEPT and INTERSECT. INTERSECT ALL and EXCEPT ALL\nare available from MariaDB 10.5.0.\n\nCREATE TABLE seqs (i INT);\nINSERT INTO seqs VALUES (1),(2),(2),(3),(3),(4),(5),(6);\n\nSELECT i FROM seqs WHERE i <= 3 UNION SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 UNION ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n| 3 |\n| 3 |\n| 3 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n| 3 |\n+------+\n\nParentheses for specifying precedence, from MariaDB 10.4.0\n\nCREATE OR REPLACE TABLE t1 (a INT);\nCREATE OR REPLACE TABLE t2 (b INT);\nCREATE OR REPLACE TABLE t3 (c INT);\n\nINSERT INTO t1 VALUES (1),(2),(3),(4);\nINSERT INTO t2 VALUES (5),(6);\nINSERT INTO t3 VALUES (1),(6);\n\n((SELECT a FROM t1) UNION (SELECT b FROM t2)) EXCEPT (SELECT c FROM t3);\n+------+\n| a |\n+------+\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n+------+\n\n(SELECT a FROM t1) UNION ((SELECT b FROM t2) EXCEPT (SELECT c FROM t3));\n+------+\n| a |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n+------+\n\nHere is an example that makes use of the SEQUENCE storage engine and the\nVALUES statement, to generate a numeric sequence and remove some arbitrary\nnumbers from it:\n\n(SELECT seq FROM seq_1_to_10) EXCEPT VALUES (2), (3), (4);\n+-----+\n| seq |\n+-----+\n| 1 |\n| 5 |\n| 6 |\n| 7 |\n| 8 |\n| 9 |\n| 10 |\n+-----+\n\nURL: https://mariadb.com/kb/en/except/','','https://mariadb.com/kb/en/except/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (428,27,'INTERSECT','MariaDB starting with 10.3.0\n----------------------------\nINTERSECT was introduced in MariaDB 10.3.0.\n\nThe result of an intersect is the intersection of right and left SELECT\nresults, i.e. only records that are present in both result sets will be\nincluded in the result of the operation.\n\nSyntax\n------\n\nSELECT ...\n(INTERSECT [ALL | DISTINCT] | EXCEPT [ALL | DISTINCT] | UNION [ALL |\nDISTINCT]) SELECT ...\n[(INTERSECT [ALL | DISTINCT] | EXCEPT [ALL | DISTINCT] | UNION [ALL |\nDISTINCT]) SELECT ...]\n[ORDER BY [column [, column ...]]]\n[LIMIT {[offset,] row_count | row_count OFFSET offset}]\n\nDescription\n-----------\n\nMariaDB has supported INTERSECT (as well as EXCEPT) in addition to UNION since\nMariaDB 10.3.\n\nAll behavior for naming columns, ORDER BY and LIMIT is the same as for UNION.\n\nINTERSECT implicitly supposes a DISTINCT operation.\n\nThe result of an intersect is the intersection of right and left SELECT\nresults, i.e. only records that are present in both result sets will be\nincluded in the result of the operation.\n\nINTERSECT has higher precedence than UNION and EXCEPT (unless running running\nin Oracle mode, in which case all three have the same precedence). If possible\nit will be executed linearly but if not it will be translated to a subquery in\nthe FROM clause:\n\n(select a,b from t1)\nunion\n(select c,d from t2)\nintersect\n(select e,f from t3)\nunion\n(select 4,4);\n\nwill be translated to:\n\n(select a,b from t1)\nunion\nselect c,d from\n ((select c,d from t2)\n intersect\n (select e,f from t3)) dummy_subselect\nunion\n(select 4,4)\n\nMariaDB starting with 10.4.0\n----------------------------\n\nParentheses\n-----------\n\nFrom MariaDB 10.4.0, parentheses can be used to specify precedence. Before\nthis, a syntax error would be returned.\n\nMariaDB starting with 10.5.0\n----------------------------\n\nALL/DISTINCT\n------------\n\nINTERSECT ALL and INTERSECT DISTINCT were introduced in MariaDB 10.5.0. The\nALL operator leaves duplicates intact, while the DISTINCT operator removes\nduplicates. DISTINCT is the default behavior if neither operator is supplied,\nand the only behavior prior to MariaDB 10.5.\n\nExamples\n--------\n\nShow customers which are employees:\n\n(SELECT e_name AS name, email FROM employees)\nINTERSECT\n(SELECT c_name AS name, email FROM customers);\n\nDifference between UNION, EXCEPT and INTERSECT. INTERSECT ALL and EXCEPT ALL\nare available from MariaDB 10.5.0.\n\nCREATE TABLE seqs (i INT);\nINSERT INTO seqs VALUES (1),(2),(2),(3),(3),(4),(5),(6);\n\nSELECT i FROM seqs WHERE i <= 3 UNION SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 UNION ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n| 3 |\n| 3 |\n| 3 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n| 3 |\n+------+\n\nParentheses for specifying precedence, from MariaDB 10.4.0\n\nCREATE OR REPLACE TABLE t1 (a INT);\nCREATE OR REPLACE TABLE t2 (b INT);\nCREATE OR REPLACE TABLE t3 (c INT);\n\nINSERT INTO t1 VALUES (1),(2),(3),(4);\nINSERT INTO t2 VALUES (5),(6);\nINSERT INTO t3 VALUES (1),(6);\n\n((SELECT a FROM t1) UNION (SELECT b FROM t2)) INTERSECT (SELECT c FROM t3);\n+------+\n| a |\n+------+\n| 1 |\n| 6 |\n+------+\n\n(SELECT a FROM t1) UNION ((SELECT b FROM t2) INTERSECT (SELECT c FROM t3));\n+------+\n| a |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 6 |\n+------+\n\nURL: https://mariadb.com/kb/en/intersect/','','https://mariadb.com/kb/en/intersect/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (429,27,'Precedence Control in Table Operations','MariaDB starting with 10.4.0\n----------------------------\nBeginning in MariaDB 10.4, you can control the ordering of execution on table\noperations using parentheses.\n\nSyntax\n------\n\n( expression )\n[ORDER BY [column[, column...]]]\n[LIMIT {[offset,] row_count | row_count OFFSET offset}]\n\nDescription\n-----------\n\nUsing parentheses in your SQL allows you to control the order of execution for\nSELECT statements and Table Value Constructor, including UNION, EXCEPT, and\nINTERSECT operations. MariaDB executes the parenthetical expression before the\nrest of the statement. You can then use ORDER BY and LIMIT clauses the further\norganize the result-set.\n\nNote: In practice, the Optimizer may rearrange the exact order in which\nMariaDB executes different parts of the statement. When it calculates the\nresult-set, however, it returns values as though the parenthetical expression\nwere executed first.\n\nExample\n-------\n\nCREATE TABLE test.t1 (num INT);\n\nINSERT INTO test.t1 VALUES (1),(2),(3);\n\n(SELECT * FROM test.t1 \n UNION \n VALUES (10)) \nINTERSECT \nVALUES (1),(3),(10),(11);\n+------+\n| num |\n+------+\n| 1 |\n| 3 |\n| 10 |\n+------+\n\n((SELECT * FROM test.t1 \n UNION\n VALUES (10))\n INTERSECT \n VALUES (1),(3),(10),(11)) \nORDER BY 1 DESC;\n+------+\n| num |\n+------+\n| 10 |\n| 3 |\n| 1 |\n+------+\n\nURL: https://mariadb.com/kb/en/precedence-control-in-table-operations/','','https://mariadb.com/kb/en/precedence-control-in-table-operations/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (430,27,'LIMIT','Description\n-----------\n\nUse the LIMIT clause to restrict the number of returned rows. When you use a\nsingle integer n with LIMIT, the first n rows will be returned. Use the ORDER\nBY clause to control which rows come first. You can also select a number of\nrows after an offset using either of the following:\n\nLIMIT offset, row_count\nLIMIT row_count OFFSET offset\n\nWhen you provide an offset m with a limit n, the first m rows will be ignored,\nand the following n rows will be returned.\n\nExecuting an UPDATE with the LIMIT clause is not safe for replication. LIMIT 0\nis an exception to this rule (see MDEV-6170).\n\nThere is a LIMIT ROWS EXAMINED optimization which provides the means to\nterminate the execution of SELECT statements which examine too many rows, and\nthus use too many resources. See LIMIT ROWS EXAMINED.\n\nMulti-Table Updates\n-------------------\n\nMariaDB starting with 10.3.2\n----------------------------\nUntil MariaDB 10.3.1, it was not possible to use LIMIT (or ORDER BY) in a\nmulti-table UPDATE statement. This restriction was lifted in MariaDB 10.3.2.\n\nGROUP_CONCAT\n------------\n\nMariaDB starting with 10.3.2\n----------------------------\nStarting from MariaDB 10.3.3, it is possible to use LIMIT with GROUP_CONCAT().\n\nExamples\n--------\n\nCREATE TABLE members (name VARCHAR(20));\nINSERT INTO members VALUES(\'Jagdish\'),(\'Kenny\'),(\'Rokurou\'),(\'Immaculada\');\n\nSELECT * FROM members;\n+------------+\n| name |\n+------------+\n| Jagdish |\n| Kenny |\n| Rokurou |\n| Immaculada |\n+------------+\n\nSelect the first two names (no ordering specified):\n\nSELECT * FROM members LIMIT 2;\n+---------+\n| name |\n+---------+\n| Jagdish |\n| Kenny |\n+---------+\n\nAll the names in alphabetical order:\n\nSELECT * FROM members ORDER BY name;\n+------------+\n| name |\n+------------+\n| Immaculada |\n| Jagdish |\n| Kenny |\n| Rokurou |\n+------------+\n\nThe first two names, ordered alphabetically:\n\nSELECT * FROM members ORDER BY name LIMIT 2;\n+------------+\n| name |\n+------------+\n| Immaculada |\n| Jagdish |\n+------------+\n\nThe third name, ordered alphabetically (the first name would be offset zero,\nso the third is offset two):\n\nSELECT * FROM members ORDER BY name LIMIT 2,1;\n+-------+\n| name |\n+-------+\n| Kenny |\n+-------+\n\nFrom MariaDB 10.3.2, LIMIT can be used in a multi-table update:\n\nCREATE TABLE warehouse (product_id INT, qty INT);\nINSERT INTO warehouse VALUES (1,100),(2,100),(3,100),(4,100);\n\nCREATE TABLE store (product_id INT, qty INT);\nINSERT INTO store VALUES (1,5),(2,5),(3,5),(4,5);\n\nUPDATE warehouse,store SET warehouse.qty = warehouse.qty-2, store.qty =\nstore.qty+2 \n WHERE (warehouse.product_id = store.product_id AND store.product_id >= 1)\n ORDER BY store.product_id DESC LIMIT 2;\n\nSELECT * FROM warehouse;\n+------------+------+\n| product_id | qty |\n+------------+------+\n| 1 | 100 |\n| 2 | 100 |\n| 3 | 98 |\n| 4 | 98 |\n+------------+------+\n\nSELECT * FROM store;\n+------------+------+\n| product_id | qty |\n+------------+------+\n| 1 | 5 |\n| 2 | 5 |\n| 3 | 7 |\n| 4 | 7 |\n+------------+------+\n\nFrom MariaDB 10.3.3, LIMIT can be used with GROUP_CONCAT, so, for example,\ngiven the following table:\n\nCREATE TABLE d (dd DATE, cc INT);\n\nINSERT INTO d VALUES (\'2017-01-01\',1);\nINSERT INTO d VALUES (\'2017-01-02\',2);\nINSERT INTO d VALUES (\'2017-01-04\',3);\n\nthe following query:\n\nSELECT SUBSTRING_INDEX(GROUP_CONCAT(CONCAT_WS(\":\",dd,cc) ORDER BY cc\nDESC),\",\",1) FROM d;\n+----------------------------------------------------------------------------+\n| SUBSTRING_INDEX(GROUP_CONCAT(CONCAT_WS(\":\",dd,cc) ORDER BY cc DESC),\",\",1) |\n+----------------------------------------------------------------------------+\n| 2017-01-04:3 |\n+----------------------------------------------------------------------------+\n\ncan be more simply rewritten as:\n\nSELECT GROUP_CONCAT(CONCAT_WS(\":\",dd,cc) ORDER BY cc DESC LIMIT 1) FROM d;\n+-------------------------------------------------------------+\n| GROUP_CONCAT(CONCAT_WS(\":\",dd,cc) ORDER BY cc DESC LIMIT 1) |\n+-------------------------------------------------------------+\n| 2017-01-04:3 |\n+-------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/limit/','','https://mariadb.com/kb/en/limit/'); @@ -532,7 +532,7 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (434,27,'Non-Recursive Common Table Expressions Overview','Common Table Expressions (CTEs) are a standard SQL feature, and are\nessentially temporary named result sets. There are two kinds of CTEs:\nNon-Recursive, which this article covers; and Recursive.\n\nMariaDB starting with 10.2.1\n----------------------------\nCommon table expressions were introduced in MariaDB 10.2.1.\n\nNon-Recursive CTEs\n------------------\n\nThe WITH keyword signifies a CTE. It is given a name, followed by a body (the\nmain query) as follows:\n\nCTEs are similar to derived tables. For example\n\nWITH engineers AS \n ( SELECT * FROM employees\n WHERE dept = \'Engineering\' )\n\nSELECT * FROM engineers\nWHERE ...\n\nSELECT * FROM\n ( SELECT * FROM employees\n WHERE dept = \'Engineering\' ) AS engineers\nWHERE\n...\n\nA non-recursive CTE is basically a query-local VIEW. There are several\nadvantages and caveats to them. The syntax is more readable than nested FROM\n(SELECT ...). A CTE can refer to another and it can be referenced from\nmultiple places.\n\nA CTE referencing Another CTE\n-----------------------------\n\nUsing this format makes for a more readable SQL than a nested FROM(SELECT ...)\nclause. Below is an example of this:\n\nWITH engineers AS (\nSELECT * FROM employees\nWHERE dept IN(\'Development\',\'Support\') ),\neu_engineers AS ( SELECT * FROM engineers WHERE country IN(\'NL\',...) )\nSELECT\n...\nFROM eu_engineers;\n\nMultiple Uses of a CTE\n----------------------\n\nThis can be an \'anti-self join\', for example:\n\nWITH engineers AS (\nSELECT * FROM employees\nWHERE dept IN(\'Development\',\'Support\') )\n\nSELECT * FROM engineers E1\nWHERE NOT EXISTS\n (SELECT 1 FROM engineers E2\n WHERE E2.country=E1.country\n AND E2.name <> E1.name );\n\nOr, for year-over-year comparisons, for example:\n\nWITH sales_product_year AS (\nSELECT product, YEAR(ship_date) AS year,\nSUM(price) AS total_amt\nFROM item_sales\nGROUP BY product, year )\n\nSELECT *\nFROM sales_product_year CUR,\nsales_product_year PREV,\nWHERE CUR.product=PREV.product \nAND CUR.year=PREV.year + 1 \nAND CUR.total_amt > PREV.total_amt\n\nAnother use is to compare individuals against their group. Below is an example\nof how this might be executed:\n\nWITH sales_product_year AS (\nSELECT product,\nYEAR(ship_date) AS year,\nSUM(price) AS total_amt\nFROM item_sales\nGROUP BY product, year\n)\n\nSELECT * \nFROM sales_product_year S1\nWHERE\ntotal_amt > \n (SELECT 0.1 * SUM(total_amt)\n FROM sales_product_year S2\n WHERE S2.year = S1.year)\n\nURL: https://mariadb.com/kb/en/non-recursive-common-table-expressions-overview/','','https://mariadb.com/kb/en/non-recursive-common-table-expressions-overview/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (435,27,'Recursive Common Table Expressions Overview','MariaDB starting with 10.2.2\n----------------------------\nRecursive Common Table Expressions have been supported since MariaDB 10.2.2.\n\nCommon Table Expressions (CTEs) are a standard SQL feature, and are\nessentially temporary named result sets. CTEs first appeared in the SQL\nstandard in 1999, and the first implementations began appearing in 2007.\n\nThere are two kinds of CTEs:\n\n* Non-recursive\n* Recursive, which this article covers.\n\nSQL is generally poor at recursive structures.\n\nCTEs permit a query to reference itself. A recursive CTE will repeatedly\nexecute subsets of the data until it obtains the complete result set. This\nmakes it particularly useful for handing hierarchical or tree-structured data.\nmax_recursive_iterations avoids infinite loops.\n\nSyntax example\n--------------\n\nWITH RECURSIVE signifies a recursive CTE. It is given a name, followed by a\nbody (the main query) as follows:\n\nComputation\n-----------\n\nGiven the following structure:\n\nFirst execute the anchor part of the query:\n\nNext, execute the recursive part of the query:\n\nSummary so far\n--------------\n\nwith recursive R as (\n select anchor_data\n union [all]\n select recursive_part\n from R, ...\n)\nselect ...\n\n* Compute anchor_data\n* Compute recursive_part to get the new data\n* if (new data is non-empty) goto 2;\n\nCAST to avoid truncating data\n-----------------------------\n\nAs currently implemented by MariaDB and by the SQL Standard, data may be\ntruncated if not correctly cast. It is necessary to CAST the column to the\ncorrect width if the CTE\'s recursive part produces wider values for a column\nthan the CTE\'s nonrecursive part. Some other DBMS give an error in this\nsituation, and MariaDB\'s behavior may change in future - see MDEV-12325. See\nthe examples below.\n\nExamples\n--------\n\nTransitive closure - determining bus destinations\n-------------------------------------------------\n\nSample data:\n\nCREATE TABLE bus_routes (origin varchar(50), dst varchar(50));\nINSERT INTO bus_routes VALUES \n (\'New York\', \'Boston\'),\n (\'Boston\', \'New York\'),\n (\'New York\', \'Washington\'),\n (\'Washington\', \'Boston\'),\n (\'Washington\', \'Raleigh\');\n\nNow, we want to return the bus destinations with New York as the origin:\n\nWITH RECURSIVE bus_dst as ( \n SELECT origin as dst FROM bus_routes WHERE origin=\'New York\'\n UNION\n SELECT bus_routes.dst FROM bus_routes JOIN bus_dst ON bus_dst.dst=\nbus_routes.origin \n) \nSELECT * FROM bus_dst;\n+------------+\n| dst |\n+------------+\n| New York |\n| Boston |\n| Washington |\n| Raleigh |\n+------------+\n\nThe above example is computed as follows:\n\nFirst, the anchor data is calculated:\n\n* Starting from New York\n* Boston and Washington are added\n\nNext, the recursive part:\n\n* Starting from Boston and then Washington\n* Raleigh is added\n* UNION excludes nodes that are already present.\n\nComputing paths - determining bus routes\n----------------------------------------\n\nThis time, we are trying to get bus routes such as \"New York -> Washington ->\nRaleigh\".\n\nUsing the same sample data as the previous example:\n\nWITH RECURSIVE paths (cur_path, cur_dest) AS (\n SELECT origin, origin FROM bus_routes WHERE origin=\'New York\'\n UNION\n SELECT CONCAT(paths.cur_path, \',\', bus_routes.dst), bus_routes.dst\n FROM paths\n JOIN bus_routes\n ON paths.cur_dest = bus_routes.origin AND\n NOT FIND_IN_SET(bus_routes.dst, paths.cur_path)\n) \nSELECT * FROM paths;\n+-----------------------------+------------+\n| cur_path | cur_dest |\n+-----------------------------+------------+\n| New York | New York |\n| New York,Boston | Boston |\n| New York,Washington | Washington |\n| New York,Washington,Boston | Boston |\n| New York,Washington,Raleigh | Raleigh |\n+-----------------------------+------------+\n\nCAST to avoid data truncation\n-----------------------------\n\nIn the following example, data is truncated because the results are not\nspecifically cast to a wide enough type:\n\nWITH RECURSIVE tbl AS (\n SELECT NULL AS col\n UNION\n SELECT \"THIS NEVER SHOWS UP\" AS col FROM tbl\n)\nSELECT col FROM tbl\n+------+\n| col |\n+------+\n| NULL |\n| |\n+------+\n\nExplicitly use CAST to overcome this:\n\nWITH RECURSIVE tbl AS (\n SELECT CAST(NULL AS CHAR(50)) AS col\n UNION SELECT \"THIS NEVER SHOWS UP\" AS col FROM tbl\n) \nSELECT * FROM tbl;\n+---------------------+\n| col |\n+---------------------+\n| NULL |\n| THIS NEVER SHOWS UP |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/recursive-common-table-expressions-overview/','','https://mariadb.com/kb/en/recursive-common-table-expressions-overview/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (436,27,'SELECT WITH ROLLUP','Syntax\n------\n\nSee SELECT for the full syntax.\n\nDescription\n-----------\n\nThe WITH ROLLUP modifier adds extra rows to the resultset that represent\nsuper-aggregate summaries. The super-aggregated column is represented by a\nNULL value. Multiple aggregates over different columns will be added if there\nare multiple GROUP BY columns.\n\nThe LIMIT clause can be used at the same time, and is applied after the WITH\nROLLUP rows have been added.\n\nWITH ROLLUP cannot be used with ORDER BY. Some sorting is still possible by\nusing ASC or DESC clauses with the GROUP BY column, although the\nsuper-aggregate rows will always be added last.\n\nExamples\n--------\n\nThese examples use the following sample table\n\nCREATE TABLE booksales ( \n country VARCHAR(35), genre ENUM(\'fiction\',\'non-fiction\'), year YEAR, sales\nINT);\n\nINSERT INTO booksales VALUES\n (\'Senegal\',\'fiction\',2014,12234), (\'Senegal\',\'fiction\',2015,15647),\n (\'Senegal\',\'non-fiction\',2014,64980), (\'Senegal\',\'non-fiction\',2015,78901),\n (\'Paraguay\',\'fiction\',2014,87970), (\'Paraguay\',\'fiction\',2015,76940),\n (\'Paraguay\',\'non-fiction\',2014,8760), (\'Paraguay\',\'non-fiction\',2015,9030);\n\nThe addition of the WITH ROLLUP modifier in this example adds an extra row\nthat aggregates both years:\n\nSELECT year, SUM(sales) FROM booksales GROUP BY year;\n+------+------------+\n| year | SUM(sales) |\n+------+------------+\n| 2014 | 173944 |\n| 2015 | 180518 |\n+------+------------+\n2 rows in set (0.08 sec)\n\nSELECT year, SUM(sales) FROM booksales GROUP BY year WITH ROLLUP;\n+------+------------+\n| year | SUM(sales) |\n+------+------------+\n| 2014 | 173944 |\n| 2015 | 180518 |\n| NULL | 354462 |\n+------+------------+\n\nIn the following example, each time the genre, the year or the country change,\nanother super-aggregate row is added:\n\nSELECT country, year, genre, SUM(sales) \n FROM booksales GROUP BY country, year, genre;\n+----------+------+-------------+------------+\n| country | year | genre | SUM(sales) |\n+----------+------+-------------+------------+\n| Paraguay | 2014 | fiction | 87970 |\n| Paraguay | 2014 | non-fiction | 8760 |\n| Paraguay | 2015 | fiction | 76940 |\n| Paraguay | 2015 | non-fiction | 9030 |\n| Senegal | 2014 | fiction | 12234 |\n| Senegal | 2014 | non-fiction | 64980 |\n| Senegal | 2015 | fiction | 15647 |\n| Senegal | 2015 | non-fiction | 78901 |\n+----------+------+-------------+------------+\n\nSELECT country, year, genre, SUM(sales) \n FROM booksales GROUP BY country, year, genre WITH ROLLUP;\n+----------+------+-------------+------------+\n| country | year | genre | SUM(sales) |\n+----------+------+-------------+------------+\n| Paraguay | 2014 | fiction | 87970 |\n| Paraguay | 2014 | non-fiction | 8760 |\n| Paraguay | 2014 | NULL | 96730 |\n| Paraguay | 2015 | fiction | 76940 |\n| Paraguay | 2015 | non-fiction | 9030 |\n| Paraguay | 2015 | NULL | 85970 |\n| Paraguay | NULL | NULL | 182700 |\n| Senegal | 2014 | fiction | 12234 |\n| Senegal | 2014 | non-fiction | 64980 |\n| Senegal | 2014 | NULL | 77214 |\n| Senegal | 2015 | fiction | 15647 |\n| Senegal | 2015 | non-fiction | 78901 |\n| Senegal | 2015 | NULL | 94548 |\n| Senegal | NULL | NULL | 171762 |\n| NULL | NULL | NULL | 354462 |\n+----------+------+-------------+------------+\n\nThe LIMIT clause, applied after WITH ROLLUP:\n\nSELECT country, year, genre, SUM(sales) \n FROM booksales GROUP BY country, year, genre WITH ROLLUP LIMIT 4;\n+----------+------+-------------+------------+\n| country | year | genre | SUM(sales) |\n+----------+------+-------------+------------+\n| Paraguay | 2014 | fiction | 87970 |\n| Paraguay | 2014 | non-fiction | 8760 |\n| Paraguay | 2014 | NULL | 96730 |\n| Paraguay | 2015 | fiction | 76940 |\n+----------+------+-------------+------------+\n\nSorting by year descending:\n\nSELECT country, year, genre, SUM(sales) \n FROM booksales GROUP BY country, year DESC, genre WITH ROLLUP;\n+----------+------+-------------+------------+\n| country | year | genre | SUM(sales) |\n+----------+------+-------------+------------+\n| Paraguay | 2015 | fiction | 76940 |\n| Paraguay | 2015 | non-fiction | 9030 |\n| Paraguay | 2015 | NULL | 85970 |\n| Paraguay | 2014 | fiction | 87970 |\n| Paraguay | 2014 | non-fiction | 8760 |\n| Paraguay | 2014 | NULL | 96730 |\n| Paraguay | NULL | NULL | 182700 |\n| Senegal | 2015 | fiction | 15647 |\n| Senegal | 2015 | non-fiction | 78901 |\n| Senegal | 2015 | NULL | 94548 |\n| Senegal | 2014 | fiction | 12234 |\n| Senegal | 2014 | non-fiction | 64980 |\n| Senegal | 2014 | NULL | 77214 |\n| Senegal | NULL | NULL | 171762 |\n| NULL | NULL | NULL | 354462 |\n+----------+------+-------------+------------+\n\nURL: https://mariadb.com/kb/en/select-with-rollup/','','https://mariadb.com/kb/en/select-with-rollup/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (437,27,'SELECT INTO OUTFILE','Syntax\n------\n\nSELECT ... INTO OUTFILE \'file_name\'\n [CHARACTER SET charset_name]\n [export_options]\n\nexport_options:\n [{FIELDS | COLUMNS}\n [TERMINATED BY \'string\']\n [[OPTIONALLY] ENCLOSED BY \'char\']\n [ESCAPED BY \'char\']\n ]\n [LINES\n [STARTING BY \'string\']\n [TERMINATED BY \'string\']\n ]\n\nDescription\n-----------\n\nSELECT INTO OUTFILE writes the resulting rows to a file, and allows the use of\ncolumn and row terminators to specify a particular output format. The default\nis to terminate fields with tabs (\\t) and lines with newlines (\\n).\n\nThe file must not exist. It cannot be overwritten. A user needs the FILE\nprivilege to run this statement. Also, MariaDB needs permission to write files\nin the specified location. If the secure_file_priv system variable is set to a\nnon-empty directory name, the file can only be written to that directory.\n\nThe LOAD DATA INFILE statement complements SELECT INTO OUTFILE.\n\nCharacter-sets\n--------------\n\nThe CHARACTER SET clause specifies the character set in which the results are\nto be written. Without the clause, no conversion takes place (the binary\ncharacter set). In this case, if there are multiple character sets, the output\nwill contain these too, and may not easily be able to be reloaded.\n\nIn cases where you have two servers using different character-sets, using\nSELECT INTO OUTFILE to transfer data from one to the other can have unexpected\nresults. To ensure that MariaDB correctly interprets the escape sequences, use\nthe CHARACTER SET clause on both the SELECT INTO OUTFILE statement and the\nsubsequent LOAD DATA INFILE statement.\n\nExample\n-------\n\nThe following example produces a file in the CSV format:\n\nSELECT customer_id, firstname, surname INTO OUTFILE \'/exportdata/customers.txt\'\n FIELDS TERMINATED BY \',\' OPTIONALLY ENCLOSED BY \'\"\'\n LINES TERMINATED BY \'\\n\'\n FROM customers;\n\nURL: https://mariadb.com/kb/en/select-into-outfile/','','https://mariadb.com/kb/en/select-into-outfile/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (437,27,'SELECT INTO OUTFILE','Syntax\n------\n\nSELECT ... INTO OUTFILE \'file_name\'\n [CHARACTER SET charset_name]\n [export_options]\n\nexport_options:\n [{FIELDS | COLUMNS}\n [TERMINATED BY \'string\']\n [[OPTIONALLY] ENCLOSED BY \'char\']\n [ESCAPED BY \'char\']\n ]\n [LINES\n [STARTING BY \'string\']\n [TERMINATED BY \'string\']\n ]\n\nDescription\n-----------\n\nSELECT INTO OUTFILE writes the resulting rows to a file, and allows the use of\ncolumn and row terminators to specify a particular output format. The default\nis to terminate fields with tabs (\\t) and lines with newlines (\\n).\n\nThe file must not exist. It cannot be overwritten. A user needs the FILE\nprivilege to run this statement. Also, MariaDB needs permission to write files\nin the specified location. If the secure_file_priv system variable is set to a\nnon-empty directory name, the file can only be written to that directory.\n\nThe LOAD DATA INFILE statement complements SELECT INTO OUTFILE.\n\nCharacter-sets\n--------------\n\nThe CHARACTER SET clause specifies the character set in which the results are\nto be written. Without the clause, no conversion takes place (the binary\ncharacter set). In this case, if there are multiple character sets, the output\nwill contain these too, and may not easily be able to be reloaded.\n\nIn cases where you have two servers using different character-sets, using\nSELECT INTO OUTFILE to transfer data from one to the other can have unexpected\nresults. To ensure that MariaDB correctly interprets the escape sequences, use\nthe CHARACTER SET clause on both the SELECT INTO OUTFILE statement and the\nsubsequent LOAD DATA INFILE statement.\n\nExample\n-------\n\nThe following example produces a file in the CSV format:\n\nSELECT customer_id, firstname, surname from customer\n INTO OUTFILE \'/exportdata/customers.txt\'\n FIELDS TERMINATED BY \',\' OPTIONALLY ENCLOSED BY \'\"\'\n LINES TERMINATED BY \'\\n\';\n\nThe following ANSI syntax is also supported for simple SELECT without UNION\n\nSELECT customer_id, firstname, surname INTO OUTFILE \'/exportdata/customers.txt\'\n FIELDS TERMINATED BY \',\' OPTIONALLY ENCLOSED BY \'\"\'\n LINES TERMINATED BY \'\\n\'\n FROM customers;\n\nIf you want to use the ANSI syntax with UNION or similar construct you have to\nuse the syntax:\n\nSELECT * INTO OUTFILE \"/tmp/skr3\" FROM (SELECT * FROM t1 UNION SELECT * FROM\nt1);\n\nURL: https://mariadb.com/kb/en/select-into-outfile/','','https://mariadb.com/kb/en/select-into-outfile/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (438,27,'SELECT INTO DUMPFILE','Syntax\n------\n\nSELECT ... INTO DUMPFILE \'file_path\'\n\nDescription\n-----------\n\nSELECT ... INTO DUMPFILE is a SELECT clause which writes the resultset into a\nsingle unformatted row, without any separators, in a file. The results will\nnot be returned to the client.\n\nfile_path can be an absolute path, or a relative path starting from the data\ndirectory. It can only be specified as a string literal, not as a variable.\nHowever, the statement can be dynamically composed and executed as a prepared\nstatement to work around this limitation.\n\nThis statement is binary-safe and so is particularly useful for writing BLOB\nvalues to file. It can be used, for example, to copy an image or an audio\ndocument from the database to a file. SELECT ... INTO FILE can be used to save\na text file.\n\nThe file must not exist. It cannot be overwritten. A user needs the FILE\nprivilege to run this statement. Also, MariaDB needs permission to write files\nin the specified location. If the secure_file_priv system variable is set to a\nnon-empty directory name, the file can only be written to that directory.\n\nSince MariaDB 5.1, the character_set_filesystem system variable has controlled\ninterpretation of file names that are given as literal strings.\n\nExample\n-------\n\nSELECT _utf8\'Hello world!\' INTO DUMPFILE \'/tmp/world\';\n\nSELECT LOAD_FILE(\'/tmp/world\') AS world;\n+--------------+\n| world |\n+--------------+\n| Hello world! |\n+--------------+\n\nURL: https://mariadb.com/kb/en/select-into-dumpfile/','','https://mariadb.com/kb/en/select-into-dumpfile/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (439,27,'FOR UPDATE','InnoDB supports row-level locking. Selected rows can be locked using LOCK IN\nSHARE MODE or FOR UPDATE. In both cases, a lock is acquired on the rows read\nby the query, and it will be released when the current transaction is\ncommitted.\n\nThe FOR UPDATE clause of SELECT applies only when autocommit is set to 0 or\nthe SELECT is enclosed in a transaction. A lock is acquired on the rows, and\nother transactions are prevented from writing the rows, acquire locks, and\nfrom reading them (unless their isolation level is READ UNCOMMITTED).\n\nIf autocommit is set to 1, the LOCK IN SHARE MODE and FOR UPDATE clauses have\nno effect.\n\nIf the isolation level is set to SERIALIZABLE, all plain SELECT statements are\nconverted to SELECT ... LOCK IN SHARE MODE.\n\nExample\n-------\n\nSELECT * FROM trans WHERE period=2001 FOR UPDATE;\n\nURL: https://mariadb.com/kb/en/for-update/','','https://mariadb.com/kb/en/for-update/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (440,27,'LOCK IN SHARE MODE','InnoDB supports row-level locking. Selected rows can be locked using LOCK IN\nSHARE MODE or FOR UPDATE. In both cases, a lock is acquired on the rows read\nby the query, and it will be released when the current transaction is\ncommitted.\n\nWhen LOCK IN SHARE MODE is specified in a SELECT statement, MariaDB will wait\nuntil all transactions that have modified the rows are committed. Then, a\nwrite lock is acquired. All transactions can read the rows, but if they want\nto modify them, they have to wait until your transaction is committed.\n\nIf autocommit is set to 1, the LOCK IN SHARE MODE and FOR UPDATE clauses have\nno effect.\n\nURL: https://mariadb.com/kb/en/lock-in-share-mode/','','https://mariadb.com/kb/en/lock-in-share-mode/'); @@ -543,7 +543,7 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (445,27,'INSERT','Syntax\n------\n\nINSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]\n [INTO] tbl_name [PARTITION (partition_list)] [(col,...)]\n {VALUES | VALUE} ({expr | DEFAULT},...),(...),...\n [ ON DUPLICATE KEY UPDATE\n col=expr\n [, col=expr] ... ] [RETURNING select_expr\n [, select_expr ...]]\n\nOr:\n\nINSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]\n [INTO] tbl_name [PARTITION (partition_list)]\n SET col={expr | DEFAULT}, ...\n [ ON DUPLICATE KEY UPDATE\n col=expr\n [, col=expr] ... ] [RETURNING select_expr\n [, select_expr ...]]\n\nOr:\n\nINSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]\n [INTO] tbl_name [PARTITION (partition_list)] [(col,...)]\n SELECT ...\n [ ON DUPLICATE KEY UPDATE\n col=expr\n [, col=expr] ... ] [RETURNING select_expr\n [, select_expr ...]]\n\nThe INSERT statement is used to insert new rows into an existing table. The\nINSERT ... VALUES and INSERT ... SET forms of the statement insert rows based\non explicitly specified values. The INSERT ... SELECT form inserts rows\nselected from another table or tables. INSERT ... SELECT is discussed further\nin the INSERT ... SELECT article.\n\nThe table name can be specified in the form db_name.tbl_name or, if a default\ndatabase is selected, in the form tbl_name (see Identifier Qualifiers). This\nallows to use INSERT ... SELECT to copy rows between different databases.\n\nThe PARTITION clause can be used in both the INSERT and the SELECT part. See\nPartition Pruning and Selection for details.\n\nMariaDB starting with 10.5\n--------------------------\nThe RETURNING clause was introduced in MariaDB 10.5.\n\nThe columns list is optional. It specifies which values are explicitly\ninserted, and in which order. If this clause is not specified, all values must\nbe explicitly specified, in the same order they are listed in the table\ndefinition.\n\nThe list of value follow the VALUES or VALUE keyword (which are\ninterchangeable, regardless how much values you want to insert), and is\nwrapped by parenthesis. The values must be listed in the same order as the\ncolumns list. It is possible to specify more than one list to insert more than\none rows with a single statement. If many rows are inserted, this is a speed\noptimization.\n\nFor one-row statements, the SET clause may be more simple, because you don\'t\nneed to remember the columns order. All values are specified in the form col =\nexpr.\n\nValues can also be specified in the form of a SQL expression or subquery.\nHowever, the subquery cannot access the same table that is named in the INTO\nclause.\n\nIf you use the LOW_PRIORITY keyword, execution of the INSERT is delayed until\nno other clients are reading from the table. If you use the HIGH_PRIORITY\nkeyword, the statement has the same priority as SELECTs. This affects only\nstorage engines that use only table-level locking (MyISAM, MEMORY, MERGE).\nHowever, if one of these keywords is specified, concurrent inserts cannot be\nused. See HIGH_PRIORITY and LOW_PRIORITY clauses for details.\n\nINSERT DELAYED\n--------------\n\nFor more details on the DELAYED option, see INSERT DELAYED.\n\nHIGH PRIORITY and LOW PRIORITY\n------------------------------\n\nSee HIGH_PRIORITY and LOW_PRIORITY.\n\nDefaults and Duplicate Values\n-----------------------------\n\nSee INSERT - Default & Duplicate Values for details..\n\nINSERT IGNORE\n-------------\n\nSee INSERT IGNORE.\n\nINSERT ON DUPLICATE KEY UPDATE\n------------------------------\n\nSee INSERT ON DUPLICATE KEY UPDATE.\n\nExamples\n--------\n\nSpecifying the column names:\n\nINSERT INTO person (first_name, last_name) VALUES (\'John\', \'Doe\');\n\nInserting more than 1 row at a time:\n\nINSERT INTO tbl_name VALUES (1, \"row 1\"), (2, \"row 2\");\n\nUsing the SET clause:\n\nINSERT INTO person SET first_name = \'John\', last_name = \'Doe\';\n\nSELECTing from another table:\n\nINSERT INTO contractor SELECT * FROM person WHERE status = \'c\';\n\nSee INSERT ON DUPLICATE KEY UPDATE and INSERT IGNORE for further examples.\n\nINSERT ... RETURNING\n--------------------\n\nINSERT ... RETURNING returns a resultset of the inserted rows.\n\nThis returns the listed columns for all the rows that are inserted, or\nalternatively, the specified SELECT expression. Any SQL expressions which can\nbe calculated can be used in the select expression for the RETURNING clause,\nincluding virtual columns and aliases, expressions which use various operators\nsuch as bitwise, logical and arithmetic operators, string functions, date-time\nfunctions, numeric functions, control flow functions, secondary functions and\nstored functions. Along with this, statements which have subqueries and\nprepared statements can also be used.\n\nExamples\n--------\n\nSimple INSERT statement\n\nINSERT INTO t2 VALUES (1,\'Dog\'),(2,\'Lion\'),(3,\'Tiger\'),(4,\'Leopard\') \nRETURNING id2,id2+id2,id2&id2,id2||id2;\n+-----+---------+---------+----------+\n| id2 | id2+id2 | id2&id2 | id2||id2 |\n+-----+---------+---------+----------+\n| 1 | 2 | 1 | 1 |\n| 2 | 4 | 2 | 1 |\n| 3 | 6 | 3 | 1 |\n| 4 | 8 | 4 | 1 |\n+-----+---------+---------+----------+\n\nUsing stored functions in RETURNING\n\nDELIMITER |\nCREATE FUNCTION f(arg INT) RETURNS INT\n BEGIN\n RETURN (SELECT arg+arg);\n END|\n\nDELIMITER ;\n\nPREPARE stmt FROM \"INSERT INTO t1 SET id1=1, animal1=\'Bear\' RETURNING f(id1),\nUPPER(animal1)\";\n\nEXECUTE stmt;\n+---------+----------------+\n| f(id1) | UPPER(animal1) |\n+---------+----------------+\n| 2 | BEAR |\n+---------+----------------+\n\nSubqueries in the RETURNING clause that return more than one row or column\ncannot be used.\n\nAggregate functions cannot be used in the RETURNING clause. Since aggregate\nfunctions work on a set of values, and if the purpose is to get the row count,\nROW_COUNT() with SELECT can be used or it can be used in\nINSERT...SELECT...RETURNING if the table in the RETURNING clause is not the\nsame as the INSERT table.\n\nURL: https://mariadb.com/kb/en/insert/','','https://mariadb.com/kb/en/insert/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (446,27,'INSERT DELAYED','Syntax\n------\n\nINSERT DELAYED ...\n\nDescription\n-----------\n\nThe DELAYED option for the INSERT statement is a MariaDB/MySQL extension to\nstandard SQL that is very useful if you have clients that cannot or need not\nwait for the INSERT to complete. This is a common situation when you use\nMariaDB for logging and you also periodically run SELECT and UPDATE statements\nthat take a long time to complete.\n\nWhen a client uses INSERT DELAYED, it gets an okay from the server at once,\nand the row is queued to be inserted when the table is not in use by any other\nthread.\n\nAnother major benefit of using INSERT DELAYED is that inserts from many\nclients are bundled together and written in one block. This is much faster\nthan performing many separate inserts.\n\nNote that INSERT DELAYED is slower than a normal INSERT if the table is not\notherwise in use. There is also the additional overhead for the server to\nhandle a separate thread for each table for which there are delayed rows. This\nmeans that you should use INSERT DELAYED only when you are really sure that\nyou need it.\n\nThe queued rows are held only in memory until they are inserted into the\ntable. This means that if you terminate mysqld forcibly (for example, with\nkill -9) or if mysqld dies unexpectedly, any queued rows that have not been\nwritten to disk are lost.\n\nThe number of concurrent INSERT DELAYED threads is limited by the\nmax_delayed_threads server system variables. If it is set to 0, INSERT DELAYED\nis disabled. The session value can be equal to the global value, or 0 to\ndisable this statement for the current session. If this limit has been\nreached, the DELAYED clause will be silently ignore for subsequent statements\n(no error will be produced).\n\nLimitations\n-----------\n\nThere are some limitations on the use of DELAYED:\n\n* INSERT DELAYED works only with MyISAM, MEMORY, ARCHIVE,\n and BLACKHOLE tables. If you execute INSERT DELAYED with another storage\nengine, you will get an error like this: ERROR 1616 (HY000): DELAYED option\nnot supported for table \'tab_name\'\n* For MyISAM tables, if there are no free blocks in the middle of the data\n file, concurrent SELECT and INSERT statements are supported. Under these\n circumstances, you very seldom need to use INSERT DELAYED\n with MyISAM.\n* INSERT DELAYED should be used only for\n INSERT statements that specify value lists. The server\n ignores DELAYED for INSERT ... SELECT\n or INSERT ... ON DUPLICATE KEY UPDATE statements.\n* Because the INSERT DELAYED statement returns immediately,\n before the rows are inserted, you cannot use\n LAST_INSERT_ID() to get the\n AUTO_INCREMENT value that the statement might generate.\n* DELAYED rows are not visible to SELECT\n statements until they actually have been inserted.\n* After INSERT DELAYED, ROW_COUNT() returns the number of the rows you tried\nto insert, not the number of the successful writes.\n* DELAYED is ignored on slave replication servers, so that \n INSERT DELAYED is treated as a normal\n INSERT on slaves. This is because\n DELAYED could cause the slave to have different data than\n the master. INSERT DELAYED statements are not safe for replication.\n* Pending INSERT DELAYED statements are lost if a table is\n write locked and ALTER TABLE is used to modify the table structure.\n* INSERT DELAYED is not supported for views. If you try, you will get an error\nlike this: ERROR 1347 (HY000): \'view_name\' is not BASE TABLE\n* INSERT DELAYED is not supported for partitioned tables.\n* INSERT DELAYED is not supported within stored programs.\n* INSERT DELAYED does not work with triggers.\n* INSERT DELAYED does not work if there is a check constraint in place.\n* INSERT DELAYED does not work if skip-new mode is active.\n\nURL: https://mariadb.com/kb/en/insert-delayed/','','https://mariadb.com/kb/en/insert-delayed/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (447,27,'INSERT SELECT','Syntax\n------\n\nINSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]\n [INTO] tbl_name [(col_name,...)]\n SELECT ...\n [ ON DUPLICATE KEY UPDATE col_name=expr, ... ]\n\nDescription\n-----------\n\nWith INSERT ... SELECT, you can quickly insert many rows into a table from one\nor more other tables. For example:\n\nINSERT INTO tbl_temp2 (fld_id)\n SELECT tbl_temp1.fld_order_id\n FROM tbl_temp1 WHERE tbl_temp1.fld_order_id > 100;\n\ntbl_name can also be specified in the form db_name.tbl_name (see Identifier\nQualifiers). This allows to copy rows between different databases.\n\nIf the new table has a primary key or UNIQUE indexes, you can use IGNORE to\nhandle duplicate key errors during the query. The newer values will not be\ninserted if an identical value already exists.\n\nREPLACE can be used instead of INSERT to prevent duplicates on UNIQUE indexes\nby deleting old values. In that case, ON DUPLICATE KEY UPDATE cannot be used.\n\nINSERT ... SELECT works for tables which already exist. To create a table for\na given resultset, you can use CREATE TABLE ... SELECT.\n\nURL: https://mariadb.com/kb/en/insert-select/','','https://mariadb.com/kb/en/insert-select/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (448,27,'LOAD DATA INFILE','Syntax\n------\n\nLOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE \'file_name\'\n [REPLACE | IGNORE]\n INTO TABLE tbl_name\n [CHARACTER SET charset_name]\n [{FIELDS | COLUMNS}\n [TERMINATED BY \'string\']\n [[OPTIONALLY] ENCLOSED BY \'char\']\n [ESCAPED BY \'char\']\n ]\n [LINES\n [STARTING BY \'string\']\n [TERMINATED BY \'string\']\n ]\n [IGNORE number LINES]\n [(col_name_or_user_var,...)]\n [SET col_name = expr,...]\n\nDescription\n-----------\n\nLOAD DATA INFILE is unsafe for statement-based replication.\n\nReads rows from a text file into the designated table on the database at a\nvery high speed. The file name must be given as a literal string.\n\nFiles are written to disk using the SELECT INTO OUTFILE statement. You can\nthen read the files back into a table using the LOAD DATA INFILE statement.\nThe FIELDS and LINES clauses are the same in both statements. These clauses\nare optional, but if both are specified then the FIELDS clause must precede\nLINES.\n\nExecuting this statement activates INSERT triggers.\n\nOne must have the FILE privilege to be able to execute LOAD DATA. This is the\nensure the normal users will not attempt to read system files.\n\nNote that MariaDB\'s systemd unit file restricts access to /home, /root, and\n/run/user by default. See Configuring access to home directories.\n\nLOAD DATA LOCAL INFILE\n----------------------\n\nWhen you execute the LOAD DATA INFILE statement, MariaDB Server attempts to\nread the input file from its own file system. In contrast, when you execute\nthe LOAD DATA LOCAL INFILE statement, the client attempts to read the input\nfile from its file system, and it sends the contents of the input file to the\nMariaDB Server. This allows you to load files from the client\'s local file\nsystem into the database.\n\nIn the event that you don\'t want to permit this operation (such as for\nsecurity reasons), you can disable the LOAD DATA LOCAL INFILE statement on\neither the server or the client.\n\n* The LOAD DATA LOCAL INFILE statement can be disabled on the server by\nsetting the local_infile system variable to 0.\n* The LOAD DATA LOCAL INFILE statement can be disabled on the client. If you\nare using MariaDB Connector/C, this can be done by unsetting the\nCLIENT_LOCAL_FILES capability flag with the mysql_real_connect function or by\nunsetting the MYSQL_OPT_LOCAL_INFILE option with mysql_optionsv function. If\nyou are using a different client or client library, then see the documentation\nfor your specific client or client library to determine how it handles the\nLOAD DATA LOCAL INFILE statement.\n\nIf the LOAD DATA LOCAL INFILE statement is disabled by either the server or\nthe client and if the user attempts to execute it, then the server will cause\nthe statement to fail with the following error message:\n\nThe used command is not allowed with this MariaDB version\n\nNote that it is not entirely accurate to say that the MariaDB version does not\nsupport the command. It would be more accurate to say that the MariaDB\nconfiguration does not support the command. See MDEV-20500 for more\ninformation.\n\nFrom MariaDB 10.5.2, the error message is more accurate:\n\nThe used command is not allowed because the MariaDB server or client \n has disabled the local infile capability\n\nREPLACE and IGNORE\n------------------\n\nIn cases where you load data from a file into a table that already contains\ndata and has a primary key, you may encounter issues where the statement\nattempts to insert a row with a primary key that already exists. When this\nhappens, the statement fails with Error 1064, protecting the data already on\nthe table. In cases where you want MariaDB to overwrite duplicates, use the\nREPLACE keyword.\n\nThe REPLACE keyword works like the REPLACE statement. Here, the statement\nattempts to load the data from the file. If the row does not exist, it adds it\nto the table. If the row contains an existing Primary Key, it replaces the\ntable data. That is, in the event of a conflict, it assumes the file contains\nthe desired row.\n\nThis operation can cause a degradation in load speed by a factor of 20 or more\nif the part that has already been loaded is larger than the capacity of the\nInnoDB Buffer Pool. This happens because it causes a lot of turnaround in the\nbuffer pool.\n\nUse the IGNORE keyword when you want to skip any rows that contain a\nconflicting primary key. Here, the statement attempts to load the data from\nthe file. If the row does not exist, it adds it to the table. If the row\ncontains an existing primary key, it ignores the addition request and moves on\nto the next. That is, in the event of a conflict, it assumes the table\ncontains the desired row.\n\nCharacter-sets\n--------------\n\nWhen the statement opens the file, it attempts to read the contents using the\ndefault character-set, as defined by the character_set_database system\nvariable.\n\nIn the cases where the file was written using a character-set other than the\ndefault, you can specify the character-set to use with the CHARACTER SET\nclause in the statement. It ignores character-sets specified by the SET NAMES\nstatement and by the character_set_client system variable. Setting the\nCHARACTER SET clause to a value of binary indicates \"no conversion.\"\n\nThe statement interprets all fields in the file as having the same\ncharacter-set, regardless of the column data type. To properly interpret file\ncontents, you must ensure that it was written with the correct character-set.\nIf you write a data file with mysqldump -T or with the SELECT INTO OUTFILE\nstatement with the mysql client, be sure to use the --default-character-set\noption, so that the output is written with the desired character-set.\n\nWhen using mixed character sets, use the CHARACTER SET clause in both SELECT\nINTO OUTFILE and LOAD DATA INFILE to ensure that MariaDB correctly interprets\nthe escape sequences.\n\nThe character_set_filesystem system variable controls the interpretation of\nthe filename.\n\nIt is currently not possible to load data files that use the ucs2 character\nset.\n\nPreprocessing Inputs\n--------------------\n\ncol_name_or_user_var can be a column name, or a user variable. In the case of\na variable, the SET statement can be used to preprocess the value before\nloading into the table.\n\nPriority and Concurrency\n------------------------\n\nIn storage engines that perform table-level locking (MyISAM, MEMORY and\nMERGE), using the LOW_PRIORITY keyword, MariaDB delays insertions until no\nother clients are reading from the table. Alternatively, when using the MyISAM\nstorage engine, you can use the CONCURRENT keyword to perform concurrent\ninsertion.\n\nThe LOW_PRIORITY and CONCURRENT keywords are mutually exclusive. They cannot\nbe used in the same statement.\n\nProgress Reporting\n------------------\n\nThe LOAD DATA INFILE statement supports progress reporting. You may find this\nuseful when dealing with long-running operations. Using another client you can\nissue a SHOW PROCESSLIST query to check the progress of the data load.\n\nUsing mariadb-import/mysqlimport\n--------------------------------\n\nMariaDB ships with a separate utility for loading data from files:\nmariadb-import (or mysqlimport before MariaDB 10.5). It operates by sending\nLOAD DATA INFILE statements to the server.\n\nUsing mariadb-import/mysqlimport you can compress the file using the\n--compress option, to get better performance over slow networks, providing\nboth the client and server support the compressed protocol. Use the --local\noption to load from the local file system.\n\nIndexing\n--------\n\nIn cases where the storage engine supports ALTER TABLE... DISABLE KEYS\nstatements (MyISAM and Aria), the LOAD DATA INFILE statement automatically\ndisables indexes during the execution.\n\nExamples\n--------\n\nYou have a file with this content (note the the separator is \',\', not tab,\nwhich is the default):\n\n2,2\n3,3\n4,4\n5,5\n6,8\n\nCREATE TABLE t1 (a int, b int, c int, d int);\nLOAD DATA LOCAL INFILE \n \'/tmp/loaddata7.dat\' into table t1 fields terminated by \',\' (a,b) set c=a+b;\nSELECT * FROM t1;\n+------+------+------+\n| a | b | c |\n+------+------+------+\n| 2 | 2 | 4 |\n| 3 | 3 | 6 |\n| 4 | 4 | 8 |\n| 5 | 5 | 10 |\n| 6 | 8 | 14 |\n+------+------+------+\n\nAnother example, given the following data (the separator is a tab):\n\n1 a\n2 b\n\nThe value of the first column is doubled before loading:\n\nLOAD DATA INFILE \'ld.txt\' INTO TABLE ld (@i,v) SET i=@i*2;\n\nSELECT * FROM ld;\n+------+------+\n| i | v |\n+------+------+\n| 2 | a |\n| 4 | b |\n+------+------+\n\nURL: https://mariadb.com/kb/en/load-data-infile/','','https://mariadb.com/kb/en/load-data-infile/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (448,27,'LOAD DATA INFILE','Syntax\n------\n\nLOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE \'file_name\'\n [REPLACE | IGNORE]\n INTO TABLE tbl_name\n [CHARACTER SET charset_name]\n [{FIELDS | COLUMNS}\n [TERMINATED BY \'string\']\n [[OPTIONALLY] ENCLOSED BY \'char\']\n [ESCAPED BY \'char\']\n ]\n [LINES\n [STARTING BY \'string\']\n [TERMINATED BY \'string\']\n ]\n [IGNORE number LINES]\n [(col_name_or_user_var,...)]\n [SET col_name = expr,...]\n\nDescription\n-----------\n\nLOAD DATA INFILE is unsafe for statement-based replication.\n\nReads rows from a text file into the designated table on the database at a\nvery high speed. The file name must be given as a literal string.\n\nFiles are written to disk using the SELECT INTO OUTFILE statement. You can\nthen read the files back into a table using the LOAD DATA INFILE statement.\nThe FIELDS and LINES clauses are the same in both statements. These clauses\nare optional, but if both are specified then the FIELDS clause must precede\nLINES.\n\nExecuting this statement activates INSERT triggers.\n\nOne must have the FILE privilege to be able to execute LOAD DATA INFILE. This\nis to ensure normal users cannot read system files. LOAD DATA LOCAL INFILE\ndoes not have this requirement.\n\nIf the secure_file_priv system variable is set (by default it is not), the\nloaded file must be present in the specified directory.\n\nNote that MariaDB\'s systemd unit file restricts access to /home, /root, and\n/run/user by default. See Configuring access to home directories.\n\nLOAD DATA LOCAL INFILE\n----------------------\n\nWhen you execute the LOAD DATA INFILE statement, MariaDB Server attempts to\nread the input file from its own file system. By contrast, when you execute\nthe LOAD DATA LOCAL INFILE statement, the client attempts to read the input\nfile from its file system, and it sends the contents of the input file to the\nMariaDB Server. This allows you to load files from the client\'s local file\nsystem into the database.\n\nIf you don\'t want to permit this operation (perhaps for security reasons), you\ncan disable the LOAD DATA LOCAL INFILE statement on either the server or the\nclient.\n\n* The LOAD DATA LOCAL INFILE statement can be disabled on the server by\nsetting the local_infile system variable to 0.\n* The LOAD DATA LOCAL INFILE statement can be disabled on the client. If you\nare using MariaDB Connector/C, this can be done by unsetting the\nCLIENT_LOCAL_FILES capability flag with the mysql_real_connect function or by\nunsetting the MYSQL_OPT_LOCAL_INFILE option with mysql_optionsv function. If\nyou are using a different client or client library, then see the documentation\nfor your specific client or client library to determine how it handles the\nLOAD DATA LOCAL INFILE statement.\n\nIf the LOAD DATA LOCAL INFILE statement is disabled by either the server or\nthe client and if the user attempts to execute it, then the server will cause\nthe statement to fail with the following error message:\n\nThe used command is not allowed with this MariaDB version\n\nNote that it is not entirely accurate to say that the MariaDB version does not\nsupport the command. It would be more accurate to say that the MariaDB\nconfiguration does not support the command. See MDEV-20500 for more\ninformation.\n\nFrom MariaDB 10.5.2, the error message is more accurate:\n\nThe used command is not allowed because the MariaDB server or client \n has disabled the local infile capability\n\nREPLACE and IGNORE\n------------------\n\nIf you load data from a file into a table that already contains data and has a\nprimary key, you may encounter issues where the statement attempts to insert a\nrow with a primary key that already exists. When this happens, the statement\nfails with Error 1064, protecting the data already on the table. If you want\nMariaDB to overwrite duplicates, use the REPLACE keyword.\n\nThe REPLACE keyword works like the REPLACE statement. Here, the statement\nattempts to load the data from the file. If the row does not exist, it adds it\nto the table. If the row contains an existing primary key, it replaces the\ntable data. That is, in the event of a conflict, it assumes the file contains\nthe desired row.\n\nThis operation can cause a degradation in load speed by a factor of 20 or more\nif the part that has already been loaded is larger than the capacity of the\nInnoDB Buffer Pool. This happens because it causes a lot of turnaround in the\nbuffer pool.\n\nUse the IGNORE keyword when you want to skip any rows that contain a\nconflicting primary key. Here, the statement attempts to load the data from\nthe file. If the row does not exist, it adds it to the table. If the row\ncontains an existing primary key, it ignores the addition request and moves on\nto the next. That is, in the event of a conflict, it assumes the table\ncontains the desired row.\n\nCharacter-sets\n--------------\n\nWhen the statement opens the file, it attempts to read the contents using the\ndefault character-set, as defined by the character_set_database system\nvariable.\n\nIn the cases where the file was written using a character-set other than the\ndefault, you can specify the character-set to use with the CHARACTER SET\nclause in the statement. It ignores character-sets specified by the SET NAMES\nstatement and by the character_set_client system variable. Setting the\nCHARACTER SET clause to a value of binary indicates \"no conversion.\"\n\nThe statement interprets all fields in the file as having the same\ncharacter-set, regardless of the column data type. To properly interpret file\ncontents, you must ensure that it was written with the correct character-set.\nIf you write a data file with mysqldump -T or with the SELECT INTO OUTFILE\nstatement with the mysql client, be sure to use the --default-character-set\noption, so that the output is written with the desired character-set.\n\nWhen using mixed character sets, use the CHARACTER SET clause in both SELECT\nINTO OUTFILE and LOAD DATA INFILE to ensure that MariaDB correctly interprets\nthe escape sequences.\n\nThe character_set_filesystem system variable controls the interpretation of\nthe filename.\n\nIt is currently not possible to load data files that use the ucs2 character\nset.\n\nPreprocessing Inputs\n--------------------\n\ncol_name_or_user_var can be a column name, or a user variable. In the case of\na variable, the SET statement can be used to preprocess the value before\nloading into the table.\n\nPriority and Concurrency\n------------------------\n\nIn storage engines that perform table-level locking (MyISAM, MEMORY and\nMERGE), using the LOW_PRIORITY keyword, MariaDB delays insertions until no\nother clients are reading from the table. Alternatively, when using the MyISAM\nstorage engine, you can use the CONCURRENT keyword to perform concurrent\ninsertion.\n\nThe LOW_PRIORITY and CONCURRENT keywords are mutually exclusive. They cannot\nbe used in the same statement.\n\nProgress Reporting\n------------------\n\nThe LOAD DATA INFILE statement supports progress reporting. You may find this\nuseful when dealing with long-running operations. Using another client you can\nissue a SHOW PROCESSLIST query to check the progress of the data load.\n\nUsing mariadb-import/mysqlimport\n--------------------------------\n\nMariaDB ships with a separate utility for loading data from files:\nmariadb-import (or mysqlimport before MariaDB 10.5). It operates by sending\nLOAD DATA INFILE statements to the server.\n\nUsing mariadb-import/mysqlimport you can compress the file using the\n--compress option, to get better performance over slow networks, providing\nboth the client and server support the compressed protocol. Use the --local\noption to load from the local file system.\n\nIndexing\n--------\n\nIn cases where the storage engine supports ALTER TABLE... DISABLE KEYS\nstatements (MyISAM and Aria), the LOAD DATA INFILE statement automatically\ndisables indexes during the execution.\n\nExamples\n--------\n\nYou have a file with this content (note the the separator is \',\', not tab,\nwhich is the default):\n\n2,2\n3,3\n4,4\n5,5\n6,8\n\nCREATE TABLE t1 (a int, b int, c int, d int, PRIMARY KEY (a));\nLOAD DATA LOCAL INFILE \n \'/tmp/loaddata7.dat\' INTO TABLE t1 FIELDS TERMINATED BY \',\' (a,b) SET c=a+b;\nSELECT * FROM t1;\n+------+------+------+\n| a | b | c |\n+------+------+------+\n| 2 | 2 | 4 |\n| 3 | 3 | 6 |\n| 4 | 4 | 8 |\n| 5 | 5 | 10 |\n| 6 | 8 | 14 |\n+------+------+------+\n\nAnother example, given the following data (the separator is a tab):\n\n1 a\n2 b\n\nThe value of the first column is doubled before loading:\n\nLOAD DATA INFILE \'ld.txt\' INTO TABLE ld (@i,v) SET i=@i*2;\n\nSELECT * FROM ld;\n+------+------+\n| i | v |\n+------+------+\n| 2 | a |\n| 4 | b |\n+------+------+\n\nURL: https://mariadb.com/kb/en/load-data-infile/','','https://mariadb.com/kb/en/load-data-infile/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (449,27,'LOAD XML','Syntax\n------\n\nLOAD XML [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE \'file_name\'\n [REPLACE | IGNORE]\n INTO TABLE [db_name.]tbl_name\n [CHARACTER SET charset_name]\n [ROWS IDENTIFIED BY \'\']\n [IGNORE number {LINES | ROWS}]\n [(column_or_user_var,...)]\n [SET col_name = expr,...]\n\nDescription\n-----------\n\nThe LOAD XML statement reads data from an XML file into a table. The file_name\nmust be given as a literal string. The tagname in the optional ROWS IDENTIFIED\nBY clause must also be given as a literal string, and must be surrounded by\nangle brackets (< and >).\n\nLOAD XML acts as the complement of running the mysql client in XML output mode\n(that is, starting the client with the --xml option). To write data from a\ntable to an XML file, use a command such as the following one from the system\nshell:\n\nshell> mysql --xml -e \'SELECT * FROM mytable\' > file.xml\n\nTo read the file back into a table, use LOAD XML INFILE. By default, the \nelement is considered to be the equivalent of a database table row; this can\nbe changed using the ROWS IDENTIFIED BY clause.\n\nThis statement supports three different XML formats:\n\n* Column names as attributes and column values as attribute values:\n\n\n\n* Column names as tags and column values as the content of these tags:\n\n\n value1\n value2\n\n\n* Column names are the name attributes of tags, and values are\n the contents of these tags:\n\n\n value1\n value2\n\n\nThis is the format used by other tools, such as mysqldump.\n\nAll 3 formats can be used in the same XML file; the import routine\nautomatically detects the format for each row and interprets it correctly.\nTags are matched based on the tag or attribute name and the column name.\n\nThe following clauses work essentially the same way for LOAD XML as they do\nfor LOAD DATA:\n\n* LOW_PRIORITY or CONCURRENT\n* LOCAL\n* REPLACE or IGNORE\n* CHARACTER SET\n* (column_or_user_var,...)\n* SET\n\nSee LOAD DATA for more information about these clauses.\n\nThe IGNORE number LINES or IGNORE number ROWS clause causes the first number\nrows in the XML file to be skipped. It is analogous to the LOAD DATA\nstatement\'s IGNORE ... LINES clause.\n\nIf the LOW_PRIORITY keyword is used, insertions are delayed until no other\nclients are reading from the table. The CONCURRENT keyword allowes the use of\nconcurrent inserts. These clauses cannot be specified together.\n\nThis statement activates INSERT triggers.\n\nURL: https://mariadb.com/kb/en/load-xml/','','https://mariadb.com/kb/en/load-xml/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (450,27,'Concurrent Inserts','The MyISAM storage engine supports concurrent inserts. This feature allows\nSELECT statements to be executed during INSERT operations, reducing contention.\n\nWhether concurrent inserts can be used or not depends on the value of the\nconcurrent_insert server system variable:\n\n* NEVER (0) disables concurrent inserts.\n* AUTO (1) allows concurrent inserts only when the target table has no free\nblocks (no data in the middle of the table has been deleted after the last\nOPTIMIZE TABLE). This is the default.\n* ALWAYS (2) always enables concurrent inserts, in which case new rows are\nadded at the end of a table if the table is being used by another thread.\n\nIf the binary log is used, CREATE TABLE ... SELECT and INSERT ... SELECT\nstatements cannot use concurrent inserts. These statements acquire a read lock\non the table, so concurrent inserts will need to wait. This way the log can be\nsafely used to restore data.\n\nConcurrent inserts are not used by replicas with the row based replication\n(see binary log formats).\n\nIf an INSERT statement contain the HIGH_PRIORITY clause, concurrent inserts\ncannot be used. INSERT ... DELAYED is usually unneeded if concurrent inserts\nare enabled.\n\nLOAD DATA INFILE uses concurrent inserts if the CONCURRENT keyword is\nspecified and concurrent_insert is not NEVER. This makes the statement slower\n(even if no other sessions access the table) but reduces contention.\n\nLOCK TABLES allows non-conflicting concurrent inserts if a READ LOCAL lock is\nused. Concurrent inserts are not allowed if the LOCAL keyword is omitted.\n\nNotes\n-----\n\nThe decision to enable concurrent insert for a table is done when the table is\nopened. If you change the value of concurrent_insert it will only affect new\nopened tables. If you want it to work for also for tables in use or cached,\nyou should do FLUSH TABLES after setting the variable.\n\nURL: https://mariadb.com/kb/en/concurrent-inserts/','','https://mariadb.com/kb/en/concurrent-inserts/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (451,27,'HIGH_PRIORITY and LOW_PRIORITY','The InnoDB storage engine uses row-level locking to ensure data integrity.\nHowever some storage engines (such as MEMORY, MyISAM, Aria and MERGE) lock the\nwhole table to prevent conflicts. These storage engines use two separate\nqueues to remember pending statements; one is for SELECTs and the other one is\nfor write statements (INSERT, DELETE, UPDATE). By default, the latter has a\nhigher priority.\n\nTo give write operations a lower priority, the low_priority_updates server\nsystem variable can be set to ON. The option is available on both the global\nand session levels, and it can be set at startup or via the SET statement.\n\nWhen too many table locks have been set by write statements, some pending\nSELECTs are executed. The maximum number of write locks that can be acquired\nbefore this happens is determined by the max_write_lock_count server system\nvariable, which is dynamic.\n\nIf write statements have a higher priority (default), the priority of\nindividual write statements (INSERT, REPLACE, UPDATE, DELETE) can be changed\nvia the LOW_PRIORITY attribute, and the priority of a SELECT statement can be\nraised via the HIGH_PRIORITY attribute. Also, LOCK TABLES supports a\nLOW_PRIORITY attribute for WRITE locks.\n\nIf read statements have a higher priority, the priority of an INSERT can be\nchanged via the HIGH_PRIORITY attribute. However, the priority of other write\nstatements cannot be raised individually.\n\nThe use of LOW_PRIORITY or HIGH_PRIORITY for an INSERT prevents Concurrent\nInserts from being used.\n\nURL: https://mariadb.com/kb/en/high_priority-and-low_priority/','','https://mariadb.com/kb/en/high_priority-and-low_priority/'); @@ -641,13 +641,13 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (537,31,'MINUTE','Syntax\n------\n\nMINUTE(time)\n\nDescription\n-----------\n\nReturns the minute for time, in the range 0 to 59.\n\nExamples\n--------\n\nSELECT MINUTE(\'2013-08-03 11:04:03\');\n+-------------------------------+\n| MINUTE(\'2013-08-03 11:04:03\') |\n+-------------------------------+\n| 4 |\n+-------------------------------+\n\nSELECT MINUTE (\'23:12:50\');\n+---------------------+\n| MINUTE (\'23:12:50\') |\n+---------------------+\n| 12 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/minute/','','https://mariadb.com/kb/en/minute/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (538,31,'MONTH','Syntax\n------\n\nMONTH(date)\n\nDescription\n-----------\n\nReturns the month for date in the range 1 to 12 for January to December, or 0\nfor dates such as \'0000-00-00\' or \'2008-00-00\' that have a zero month part.\n\nExamples\n--------\n\nSELECT MONTH(\'2019-01-03\');\n+---------------------+\n| MONTH(\'2019-01-03\') |\n+---------------------+\n| 1 |\n+---------------------+\n\nSELECT MONTH(\'2019-00-03\');\n+---------------------+\n| MONTH(\'2019-00-03\') |\n+---------------------+\n| 0 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/month/','','https://mariadb.com/kb/en/month/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (539,31,'MONTHNAME','Syntax\n------\n\nMONTHNAME(date)\n\nDescription\n-----------\n\nReturns the full name of the month for date. The language used for the name is\ncontrolled by the value of the lc_time_names system variable. See server\nlocale for more on the supported locales.\n\nExamples\n--------\n\nSELECT MONTHNAME(\'2019-02-03\');\n+-------------------------+\n| MONTHNAME(\'2019-02-03\') |\n+-------------------------+\n| February |\n+-------------------------+\n\nChanging the locale:\n\nSET lc_time_names = \'fr_CA\';\n\nSELECT MONTHNAME(\'2019-05-21\');\n+-------------------------+\n| MONTHNAME(\'2019-05-21\') |\n+-------------------------+\n| mai |\n+-------------------------+\n\nURL: https://mariadb.com/kb/en/monthname/','','https://mariadb.com/kb/en/monthname/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (540,31,'NOW','Syntax\n------\n\nNOW([precision])\nCURRENT_TIMESTAMP\nCURRENT_TIMESTAMP([precision])\nLOCALTIME, LOCALTIME([precision])\nLOCALTIMESTAMP\nLOCALTIMESTAMP([precision])\n\nDescription\n-----------\n\nReturns the current date and time as a value in \'YYYY-MM-DD HH:MM:SS\' or\nYYYYMMDDHHMMSS.uuuuuu format, depending on whether the function is used in a\nstring or numeric context. The value is expressed in the current time zone.\n\nThe optional precision determines the microsecond precision. See Microseconds\nin MariaDB.\n\nNOW() (or its synonyms) can be used as the default value for TIMESTAMP columns\nas well as, since MariaDB 10.0.1, DATETIME columns. Before MariaDB 10.0.1, it\nwas only possible for a single TIMESTAMP column per table to contain the\nCURRENT_TIMESTAMP as its default.\n\nWhen displayed in the INFORMATION_SCHEMA.COLUMNS table, a default CURRENT\nTIMESTAMP is displayed as CURRENT_TIMESTAMP up until MariaDB 10.2.2, and as\ncurrent_timestamp() from MariaDB 10.2.3, due to to MariaDB 10.2 accepting\nexpressions in the DEFAULT clause.\n\nExamples\n--------\n\nSELECT NOW();\n+---------------------+\n| NOW() |\n+---------------------+\n| 2010-03-27 13:13:25 |\n+---------------------+\n\nSELECT NOW() + 0;\n+-----------------------+\n| NOW() + 0 |\n+-----------------------+\n| 20100327131329.000000 |\n+-----------------------+\n\nWith precision:\n\nSELECT CURRENT_TIMESTAMP(2);\n+------------------------+\n| CURRENT_TIMESTAMP(2) |\n+------------------------+\n| 2018-07-10 09:47:26.24 |\n+------------------------+\n\nUsed as a default TIMESTAMP:\n\nCREATE TABLE t (createdTS TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP);\n\nFrom MariaDB 10.2.2:\n\nSELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=\'test\'\n AND COLUMN_NAME LIKE \'%ts%\'\\G\n*************************** 1. row ***************************\n TABLE_CATALOG: def\n TABLE_SCHEMA: test\n TABLE_NAME: t\n COLUMN_NAME: ts\n ORDINAL_POSITION: 1\n COLUMN_DEFAULT: current_timestamp()\n...\n\n<= MariaDB 10.2.1\n\nSELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=\'test\'\n AND COLUMN_NAME LIKE \'%ts%\'\\G\n*************************** 1. row ***************************\n TABLE_CATALOG: def\n TABLE_SCHEMA: test\n TABLE_NAME: t\n COLUMN_NAME: createdTS\n ORDINAL_POSITION: 1\n COLUMN_DEFAULT: CURRENT_TIMESTAMP\n...\n\nURL: https://mariadb.com/kb/en/now/','','https://mariadb.com/kb/en/now/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (540,31,'NOW','Syntax\n------\n\nNOW([precision])\nCURRENT_TIMESTAMP\nCURRENT_TIMESTAMP([precision])\nLOCALTIME, LOCALTIME([precision])\nLOCALTIMESTAMP\nLOCALTIMESTAMP([precision])\n\nDescription\n-----------\n\nReturns the current date and time as a value in \'YYYY-MM-DD HH:MM:SS\' or\nYYYYMMDDHHMMSS.uuuuuu format, depending on whether the function is used in a\nstring or numeric context. The value is expressed in the current time zone.\n\nThe optional precision determines the microsecond precision. See Microseconds\nin MariaDB.\n\nNOW() (or its synonyms) can be used as the default value for TIMESTAMP columns\nas well as, since MariaDB 10.0.1, DATETIME columns. Before MariaDB 10.0.1, it\nwas only possible for a single TIMESTAMP column per table to contain the\nCURRENT_TIMESTAMP as its default.\n\nWhen displayed in the INFORMATION_SCHEMA.COLUMNS table, a default CURRENT\nTIMESTAMP is displayed as CURRENT_TIMESTAMP up until MariaDB 10.2.2, and as\ncurrent_timestamp() from MariaDB 10.2.3, due to to MariaDB 10.2 accepting\nexpressions in the DEFAULT clause.\n\nChanging the timestamp system variable with a SET timestamp statement affects\nthe value returned by NOW(), but not by SYSDATE().\n\nExamples\n--------\n\nSELECT NOW();\n+---------------------+\n| NOW() |\n+---------------------+\n| 2010-03-27 13:13:25 |\n+---------------------+\n\nSELECT NOW() + 0;\n+-----------------------+\n| NOW() + 0 |\n+-----------------------+\n| 20100327131329.000000 |\n+-----------------------+\n\nWith precision:\n\nSELECT CURRENT_TIMESTAMP(2);\n+------------------------+\n| CURRENT_TIMESTAMP(2) |\n+------------------------+\n| 2018-07-10 09:47:26.24 |\n+------------------------+\n\nUsed as a default TIMESTAMP:\n\nCREATE TABLE t (createdTS TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP);\n\nFrom MariaDB 10.2.2:\n\nSELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=\'test\'\n AND COLUMN_NAME LIKE \'%ts%\'\\G\n*************************** 1. row ***************************\n TABLE_CATALOG: def\n TABLE_SCHEMA: test\n TABLE_NAME: t\n COLUMN_NAME: ts\n ORDINAL_POSITION: 1\n COLUMN_DEFAULT: current_timestamp()\n...\n\n<= MariaDB 10.2.1\n\nSELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=\'test\'\n AND COLUMN_NAME LIKE \'%ts%\'\\G\n*************************** 1. row ***************************\n TABLE_CATALOG: def\n TABLE_SCHEMA: test\n TABLE_NAME: t\n COLUMN_NAME: createdTS\n ORDINAL_POSITION: 1\n COLUMN_DEFAULT: CURRENT_TIMESTAMP\n...\n\nURL: https://mariadb.com/kb/en/now/','','https://mariadb.com/kb/en/now/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (541,31,'PERIOD_ADD','Syntax\n------\n\nPERIOD_ADD(P,N)\n\nDescription\n-----------\n\nAdds N months to period P. P is in the format YYMM or YYYYMM, and is not a\ndate value. If P contains a two-digit year, values from 00 to 69 are converted\nto from 2000 to 2069, while values from 70 are converted to 1970 upwards.\n\nReturns a value in the format YYYYMM.\n\nExamples\n--------\n\nSELECT PERIOD_ADD(200801,2);\n+----------------------+\n| PERIOD_ADD(200801,2) |\n+----------------------+\n| 200803 |\n+----------------------+\n\nSELECT PERIOD_ADD(6910,2);\n+--------------------+\n| PERIOD_ADD(6910,2) |\n+--------------------+\n| 206912 |\n+--------------------+\n\nSELECT PERIOD_ADD(7010,2);\n+--------------------+\n| PERIOD_ADD(7010,2) |\n+--------------------+\n| 197012 |\n+--------------------+\n\nURL: https://mariadb.com/kb/en/period_add/','','https://mariadb.com/kb/en/period_add/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (542,31,'PERIOD_DIFF','Syntax\n------\n\nPERIOD_DIFF(P1,P2)\n\nDescription\n-----------\n\nReturns the number of months between periods P1 and P2. P1 and P2 can be in\nthe format YYMM or YYYYMM, and are not date values.\n\nIf P1 or P2 contains a two-digit year, values from 00 to 69 are converted to\nfrom 2000 to 2069, while values from 70 are converted to 1970 upwards.\n\nExamples\n--------\n\nSELECT PERIOD_DIFF(200802,200703);\n+----------------------------+\n| PERIOD_DIFF(200802,200703) |\n+----------------------------+\n| 11 |\n+----------------------------+\n\nSELECT PERIOD_DIFF(6902,6803);\n+------------------------+\n| PERIOD_DIFF(6902,6803) |\n+------------------------+\n| 11 |\n+------------------------+\n\nSELECT PERIOD_DIFF(7002,6803);\n+------------------------+\n| PERIOD_DIFF(7002,6803) |\n+------------------------+\n| -1177 |\n+------------------------+\n\nURL: https://mariadb.com/kb/en/period_diff/','','https://mariadb.com/kb/en/period_diff/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (543,31,'QUARTER','Syntax\n------\n\nQUARTER(date)\n\nDescription\n-----------\n\nReturns the quarter of the year for date, in the range 1 to 4. Returns 0 if\nmonth contains a zero value, or NULL if the given value is not otherwise a\nvalid date (zero values are accepted).\n\nExamples\n--------\n\nSELECT QUARTER(\'2008-04-01\');\n+-----------------------+\n| QUARTER(\'2008-04-01\') |\n+-----------------------+\n| 2 |\n+-----------------------+\n\nSELECT QUARTER(\'2019-00-01\');\n+-----------------------+\n| QUARTER(\'2019-00-01\') |\n+-----------------------+\n| 0 |\n+-----------------------+\n\nURL: https://mariadb.com/kb/en/quarter/','','https://mariadb.com/kb/en/quarter/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (544,31,'SECOND','Syntax\n------\n\nSECOND(time)\n\nDescription\n-----------\n\nReturns the second for a given time (which can include microseconds), in the\nrange 0 to 59, or NULL if not given a valid time value.\n\nExamples\n--------\n\nSELECT SECOND(\'10:05:03\');\n+--------------------+\n| SECOND(\'10:05:03\') |\n+--------------------+\n| 3 |\n+--------------------+\n\nSELECT SECOND(\'10:05:01.999999\');\n+---------------------------+\n| SECOND(\'10:05:01.999999\') |\n+---------------------------+\n| 1 |\n+---------------------------+\n\nURL: https://mariadb.com/kb/en/second/','','https://mariadb.com/kb/en/second/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (545,31,'SEC_TO_TIME','Syntax\n------\n\nSEC_TO_TIME(seconds)\n\nDescription\n-----------\n\nReturns the seconds argument, converted to hours, minutes, and seconds, as a\nTIME value. The range of the result is constrained to that of the TIME data\ntype. A warning occurs if the argument corresponds to a value outside that\nrange.\n\nThe time will be returned in the format hh:mm:ss, or hhmmss if used in a\nnumeric calculation.\n\nExamples\n--------\n\nSELECT SEC_TO_TIME(12414);\n+--------------------+\n| SEC_TO_TIME(12414) |\n+--------------------+\n| 03:26:54 |\n+--------------------+\n\nSELECT SEC_TO_TIME(12414)+0;\n+----------------------+\n| SEC_TO_TIME(12414)+0 |\n+----------------------+\n| 32654 |\n+----------------------+\n\nSELECT SEC_TO_TIME(9999999);\n+----------------------+\n| SEC_TO_TIME(9999999) |\n+----------------------+\n| 838:59:59 |\n+----------------------+\n1 row in set, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+---------+------+-------------------------------------------+\n| Level | Code | Message |\n+---------+------+-------------------------------------------+\n| Warning | 1292 | Truncated incorrect time value: \'9999999\' |\n+---------+------+-------------------------------------------+\n\nURL: https://mariadb.com/kb/en/sec_to_time/','','https://mariadb.com/kb/en/sec_to_time/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (546,31,'STR_TO_DATE','Syntax\n------\n\nSTR_TO_DATE(str,format)\n\nDescription\n-----------\n\nThis is the inverse of the DATE_FORMAT() function. It takes a string str and a\nformat string format. STR_TO_DATE() returns a DATETIME value if the format\nstring contains both date and time parts, or a DATE or TIME value if the\nstring contains only date or time parts.\n\nThe date, time, or datetime values contained in str should be given in the\nformat indicated by format. If str contains an illegal date, time, or datetime\nvalue, STR_TO_DATE() returns NULL. An illegal value also produces a warning.\n\nThe options that can be used by STR_TO_DATE(), as well as its inverse\nDATE_FORMAT() and the FROM_UNIXTIME() function, are:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| %a | Short weekday name in current locale |\n| | (Variable lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %b | Short form month name in current locale. For |\n| | locale en_US this is one of: |\n| | Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov |\n| | or Dec. |\n+---------------------------+------------------------------------------------+\n| %c | Month with 1 or 2 digits. |\n+---------------------------+------------------------------------------------+\n| %D | Day with English suffix \'th\', \'nd\', \'st\' or |\n| | \'rd\'\'. (1st, 2nd, 3rd...). |\n+---------------------------+------------------------------------------------+\n| %d | Day with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %e | Day with 1 or 2 digits. |\n+---------------------------+------------------------------------------------+\n| %f | Microseconds 6 digits. |\n+---------------------------+------------------------------------------------+\n| %H | Hour with 2 digits between 00-23. |\n+---------------------------+------------------------------------------------+\n| %h | Hour with 2 digits between 01-12. |\n+---------------------------+------------------------------------------------+\n| %I | Hour with 2 digits between 01-12. |\n+---------------------------+------------------------------------------------+\n| %i | Minute with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %j | Day of the year (001-366) |\n+---------------------------+------------------------------------------------+\n| %k | Hour with 1 digits between 0-23. |\n+---------------------------+------------------------------------------------+\n| %l | Hour with 1 digits between 1-12. |\n+---------------------------+------------------------------------------------+\n| %M | Full month name in current locale (Variable |\n| | lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %m | Month with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %p | AM/PM according to current locale (Variable |\n| | lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %r | Time in 12 hour format, followed by AM/PM. |\n| | Short for \'%I:%i:%S %p\'. |\n+---------------------------+------------------------------------------------+\n| %S | Seconds with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %s | Seconds with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %T | Time in 24 hour format. Short for \'%H:%i:%S\'. |\n+---------------------------+------------------------------------------------+\n| %U | Week number (00-53), when first day of the |\n| | week is Sunday. |\n+---------------------------+------------------------------------------------+\n| %u | Week number (00-53), when first day of the |\n| | week is Monday. |\n+---------------------------+------------------------------------------------+\n| %V | Week number (01-53), when first day of the |\n| | week is Sunday. Used with %X. |\n+---------------------------+------------------------------------------------+\n| %v | Week number (01-53), when first day of the |\n| | week is Monday. Used with %x. |\n+---------------------------+------------------------------------------------+\n| %W | Full weekday name in current locale (Variable |\n| | lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %w | Day of the week. 0 = Sunday, 6 = Saturday. |\n+---------------------------+------------------------------------------------+\n| %X | Year with 4 digits when first day of the week |\n| | is Sunday. Used with %V. |\n+---------------------------+------------------------------------------------+\n| %x | Year with 4 digits when first day of the week |\n| | is Monday. Used with %v. |\n+---------------------------+------------------------------------------------+\n| %Y | Year with 4 digits. |\n+---------------------------+------------------------------------------------+\n| %y | Year with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %# | For str_to_date(), skip all numbers. |\n+---------------------------+------------------------------------------------+\n| %. | For str_to_date(), skip all punctation |\n| | characters. |\n+---------------------------+------------------------------------------------+\n| %@ | For str_to_date(), skip all alpha characters. |\n+---------------------------+------------------------------------------------+\n| %% | A literal % character. |\n+---------------------------+------------------------------------------------+\n\nExamples\n--------\n\nSELECT STR_TO_DATE(\'Wednesday, June 2, 2014\', \'%W, %M %e, %Y\');\n+---------------------------------------------------------+\n| STR_TO_DATE(\'Wednesday, June 2, 2014\', \'%W, %M %e, %Y\') |\n+---------------------------------------------------------+\n| 2014-06-02 |\n+---------------------------------------------------------+\n\nSELECT STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W, %M %e, %Y\');\n+--------------------------------------------------------------+\n| STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W, %M %e, %Y\') |\n+--------------------------------------------------------------+\n| NULL |\n+--------------------------------------------------------------+\n1 row in set, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+---------+------+-------------------------------------------------------------\n---------------------+\n| Level | Code | Message \n |\n+---------+------+-------------------------------------------------------------\n---------------------+\n| Warning | 1411 | Incorrect datetime value: \'Wednesday23423, June 2, 2014\'\nfor function str_to_date |\n+---------+------+-------------------------------------------------------------\n---------------------+\n\nSELECT STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W%#, %M %e, %Y\');\n+----------------------------------------------------------------+\n| STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W%#, %M %e, %Y\') |\n+----------------------------------------------------------------+\n| 2014-06-02 |\n+----------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/str_to_date/','','https://mariadb.com/kb/en/str_to_date/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (546,31,'STR_TO_DATE','Syntax\n------\n\nSTR_TO_DATE(str,format)\n\nDescription\n-----------\n\nThis is the inverse of the DATE_FORMAT() function. It takes a string str and a\nformat string format. STR_TO_DATE() returns a DATETIME value if the format\nstring contains both date and time parts, or a DATE or TIME value if the\nstring contains only date or time parts.\n\nThe date, time, or datetime values contained in str should be given in the\nformat indicated by format. If str contains an illegal date, time, or datetime\nvalue, STR_TO_DATE() returns NULL. An illegal value also produces a warning.\n\nUnder specific SQL_MODE settings an error may also be generated if the str\nisn\'t a valid date:\n\n* ALLOW_INVALID_DATES\n* NO_ZERO_DATE\n* NO_ZERO_IN_DATE\n\nThe options that can be used by STR_TO_DATE(), as well as its inverse\nDATE_FORMAT() and the FROM_UNIXTIME() function, are:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| %a | Short weekday name in current locale |\n| | (Variable lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %b | Short form month name in current locale. For |\n| | locale en_US this is one of: |\n| | Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov |\n| | or Dec. |\n+---------------------------+------------------------------------------------+\n| %c | Month with 1 or 2 digits. |\n+---------------------------+------------------------------------------------+\n| %D | Day with English suffix \'th\', \'nd\', \'st\' or |\n| | \'rd\'\'. (1st, 2nd, 3rd...). |\n+---------------------------+------------------------------------------------+\n| %d | Day with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %e | Day with 1 or 2 digits. |\n+---------------------------+------------------------------------------------+\n| %f | Microseconds 6 digits. |\n+---------------------------+------------------------------------------------+\n| %H | Hour with 2 digits between 00-23. |\n+---------------------------+------------------------------------------------+\n| %h | Hour with 2 digits between 01-12. |\n+---------------------------+------------------------------------------------+\n| %I | Hour with 2 digits between 01-12. |\n+---------------------------+------------------------------------------------+\n| %i | Minute with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %j | Day of the year (001-366) |\n+---------------------------+------------------------------------------------+\n| %k | Hour with 1 digits between 0-23. |\n+---------------------------+------------------------------------------------+\n| %l | Hour with 1 digits between 1-12. |\n+---------------------------+------------------------------------------------+\n| %M | Full month name in current locale (Variable |\n| | lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %m | Month with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %p | AM/PM according to current locale (Variable |\n| | lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %r | Time in 12 hour format, followed by AM/PM. |\n| | Short for \'%I:%i:%S %p\'. |\n+---------------------------+------------------------------------------------+\n| %S | Seconds with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %s | Seconds with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %T | Time in 24 hour format. Short for \'%H:%i:%S\'. |\n+---------------------------+------------------------------------------------+\n| %U | Week number (00-53), when first day of the |\n| | week is Sunday. |\n+---------------------------+------------------------------------------------+\n| %u | Week number (00-53), when first day of the |\n| | week is Monday. |\n+---------------------------+------------------------------------------------+\n| %V | Week number (01-53), when first day of the |\n| | week is Sunday. Used with %X. |\n+---------------------------+------------------------------------------------+\n| %v | Week number (01-53), when first day of the |\n| | week is Monday. Used with %x. |\n+---------------------------+------------------------------------------------+\n| %W | Full weekday name in current locale (Variable |\n| | lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %w | Day of the week. 0 = Sunday, 6 = Saturday. |\n+---------------------------+------------------------------------------------+\n| %X | Year with 4 digits when first day of the week |\n| | is Sunday. Used with %V. |\n+---------------------------+------------------------------------------------+\n| %x | Year with 4 digits when first day of the week |\n| | is Monday. Used with %v. |\n+---------------------------+------------------------------------------------+\n| %Y | Year with 4 digits. |\n+---------------------------+------------------------------------------------+\n| %y | Year with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %# | For str_to_date(), skip all numbers. |\n+---------------------------+------------------------------------------------+\n| %. | For str_to_date(), skip all punctation |\n| | characters. |\n+---------------------------+------------------------------------------------+\n| %@ | For str_to_date(), skip all alpha characters. |\n+---------------------------+------------------------------------------------+\n| %% | A literal % character. |\n+---------------------------+------------------------------------------------+\n\nExamples\n--------\n\nSELECT STR_TO_DATE(\'Wednesday, June 2, 2014\', \'%W, %M %e, %Y\');\n+---------------------------------------------------------+\n| STR_TO_DATE(\'Wednesday, June 2, 2014\', \'%W, %M %e, %Y\') |\n+---------------------------------------------------------+\n| 2014-06-02 |\n+---------------------------------------------------------+\n\nSELECT STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W, %M %e, %Y\');\n+--------------------------------------------------------------+\n| STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W, %M %e, %Y\') |\n+--------------------------------------------------------------+\n| NULL |\n+--------------------------------------------------------------+\n1 row in set, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+---------+------+-------------------------------------------------------------\n---------------------+\n| Level | Code | Message \n |\n+---------+------+-------------------------------------------------------------\n---------------------+\n| Warning | 1411 | Incorrect datetime value: \'Wednesday23423, June 2, 2014\'\nfor function str_to_date |\n+---------+------+-------------------------------------------------------------\n---------------------+\n\nSELECT STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W%#, %M %e, %Y\');\n+----------------------------------------------------------------+\n| STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W%#, %M %e, %Y\') |\n+----------------------------------------------------------------+\n| 2014-06-02 |\n+----------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/str_to_date/','','https://mariadb.com/kb/en/str_to_date/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (547,31,'SUBDATE','Syntax\n------\n\nSUBDATE(date,INTERVAL expr unit), SUBDATE(expr,days)\n\nDescription\n-----------\n\nWhen invoked with the INTERVAL form of the second argument, SUBDATE() is a\nsynonym for DATE_SUB(). See Date and Time Units for a complete list of\npermitted units.\n\nThe second form allows the use of an integer value for days. In such cases, it\nis interpreted as the number of days to be subtracted from the date or\ndatetime expression expr.\n\nExamples\n--------\n\nSELECT DATE_SUB(\'2008-01-02\', INTERVAL 31 DAY);\n+-----------------------------------------+\n| DATE_SUB(\'2008-01-02\', INTERVAL 31 DAY) |\n+-----------------------------------------+\n| 2007-12-02 |\n+-----------------------------------------+\n\nSELECT SUBDATE(\'2008-01-02\', INTERVAL 31 DAY);\n+----------------------------------------+\n| SUBDATE(\'2008-01-02\', INTERVAL 31 DAY) |\n+----------------------------------------+\n| 2007-12-02 |\n+----------------------------------------+\n\nSELECT SUBDATE(\'2008-01-02 12:00:00\', 31);\n+------------------------------------+\n| SUBDATE(\'2008-01-02 12:00:00\', 31) |\n+------------------------------------+\n| 2007-12-02 12:00:00 |\n+------------------------------------+\n\nCREATE TABLE t1 (d DATETIME);\nINSERT INTO t1 VALUES\n (\"2007-01-30 21:31:07\"),\n (\"1983-10-15 06:42:51\"),\n (\"2011-04-21 12:34:56\"),\n (\"2011-10-30 06:31:41\"),\n (\"2011-01-30 14:03:25\"),\n (\"2004-10-07 11:19:34\");\n\nSELECT d, SUBDATE(d, 10) from t1;\n+---------------------+---------------------+\n| d | SUBDATE(d, 10) |\n+---------------------+---------------------+\n| 2007-01-30 21:31:07 | 2007-01-20 21:31:07 |\n| 1983-10-15 06:42:51 | 1983-10-05 06:42:51 |\n| 2011-04-21 12:34:56 | 2011-04-11 12:34:56 |\n| 2011-10-30 06:31:41 | 2011-10-20 06:31:41 |\n| 2011-01-30 14:03:25 | 2011-01-20 14:03:25 |\n| 2004-10-07 11:19:34 | 2004-09-27 11:19:34 |\n+---------------------+---------------------+\n\nSELECT d, SUBDATE(d, INTERVAL 10 MINUTE) from t1;\n+---------------------+--------------------------------+\n| d | SUBDATE(d, INTERVAL 10 MINUTE) |\n+---------------------+--------------------------------+\n| 2007-01-30 21:31:07 | 2007-01-30 21:21:07 |\n| 1983-10-15 06:42:51 | 1983-10-15 06:32:51 |\n| 2011-04-21 12:34:56 | 2011-04-21 12:24:56 |\n| 2011-10-30 06:31:41 | 2011-10-30 06:21:41 |\n| 2011-01-30 14:03:25 | 2011-01-30 13:53:25 |\n| 2004-10-07 11:19:34 | 2004-10-07 11:09:34 |\n+---------------------+--------------------------------+\n\nURL: https://mariadb.com/kb/en/subdate/','','https://mariadb.com/kb/en/subdate/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (548,31,'SUBTIME','Syntax\n------\n\nSUBTIME(expr1,expr2)\n\nDescription\n-----------\n\nSUBTIME() returns expr1 - expr2 expressed as a value in the same format as\nexpr1. expr1 is a time or datetime expression, and expr2 is a time expression.\n\nExamples\n--------\n\nSELECT SUBTIME(\'2007-12-31 23:59:59.999999\',\'1 1:1:1.000002\');\n+--------------------------------------------------------+\n| SUBTIME(\'2007-12-31 23:59:59.999999\',\'1 1:1:1.000002\') |\n+--------------------------------------------------------+\n| 2007-12-30 22:58:58.999997 |\n+--------------------------------------------------------+\n\nSELECT SUBTIME(\'01:00:00.999999\', \'02:00:00.999998\');\n+-----------------------------------------------+\n| SUBTIME(\'01:00:00.999999\', \'02:00:00.999998\') |\n+-----------------------------------------------+\n| -00:59:59.999999 |\n+-----------------------------------------------+\n\nURL: https://mariadb.com/kb/en/subtime/','','https://mariadb.com/kb/en/subtime/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (549,31,'SYSDATE','Syntax\n------\n\nSYSDATE([precision])\n\nDescription\n-----------\n\nReturns the current date and time as a value in \'YYYY-MM-DD HH:MM:SS\' or\nYYYYMMDDHHMMSS.uuuuuu format, depending on whether the function is used in a\nstring or numeric context.\n\nThe optional precision determines the microsecond precision. See Microseconds\nin MariaDB.\n\nSYSDATE() returns the time at which it executes. This differs from the\nbehavior for NOW(), which returns a constant time that indicates the time at\nwhich the statement began to execute. (Within a stored routine or trigger,\nNOW() returns the time at which the routine or triggering statement began to\nexecute.)\n\nIn addition, changing the timestamp system variable with a SET timestamp\nstatement affects the value returned by NOW() but not by SYSDATE(). This means\nthat timestamp settings in the binary log have no effect on invocations of\nSYSDATE().\n\nBecause SYSDATE() can return different values even within the same statement,\nand is not affected by SET TIMESTAMP, it is non-deterministic and therefore\nunsafe for replication if statement-based binary logging is used. If that is a\nproblem, you can use row-based logging, or start the server with the mysqld\noption --sysdate-is-now to cause SYSDATE() to be an alias for NOW(). The\nnon-deterministic nature of SYSDATE() also means that indexes cannot be used\nfor evaluating expressions that refer to it, and that statements using the\nSYSDATE() function are unsafe for statement-based replication.\n\nExamples\n--------\n\nDifference between NOW() and SYSDATE():\n\nSELECT NOW(), SLEEP(2), NOW();\n+---------------------+----------+---------------------+\n| NOW() | SLEEP(2) | NOW() |\n+---------------------+----------+---------------------+\n| 2010-03-27 13:23:40 | 0 | 2010-03-27 13:23:40 |\n+---------------------+----------+---------------------+\n\nSELECT SYSDATE(), SLEEP(2), SYSDATE();\n+---------------------+----------+---------------------+\n| SYSDATE() | SLEEP(2) | SYSDATE() |\n+---------------------+----------+---------------------+\n| 2010-03-27 13:23:52 | 0 | 2010-03-27 13:23:54 |\n+---------------------+----------+---------------------+\n\nWith precision:\n\nSELECT SYSDATE(4);\n+--------------------------+\n| SYSDATE(4) |\n+--------------------------+\n| 2018-07-10 10:17:13.1689 |\n+--------------------------+\n\nURL: https://mariadb.com/kb/en/sysdate/','','https://mariadb.com/kb/en/sysdate/'); @@ -793,8 +793,8 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (689,36,'Type Conversion','Implicit type conversion takes place when MariaDB is using operands or\ndifferent types, in order to make the operands compatible.\n\nIt is best practice not to rely upon implicit conversion; rather use CAST to\nexplicitly convert types.\n\nRules for Conversion on Comparison\n----------------------------------\n\n* If either argument is NULL, the result of the comparison is NULL unless the\nNULL-safe <=> equality comparison operator is used.\n* If both arguments are integers, they are compared as integers.\n* If both arguments are strings, they are compared as strings.\n* If one argument is decimal and the other argument is decimal or integer,\nthey are compared as decimals.\n* If one argument is decimal and the other argument is a floating point, they\nare compared as floating point values.\n* If one argument is string and the other argument is integer, they are\ncompared as decimals. This conversion was added in MariaDB 10.3.36. Prior to\n10.3.36, this combination was compared as floating point values, which did not\nalways work well for huge 64-bit integers because of a possible precision loss\non conversion to double.\n* If a hexadecimal argument is not compared to a number, it is treated as a\nbinary string.\n* If a constant is compared to a TIMESTAMP or DATETIME, the constant is\nconverted to a timestamp, unless used as an argument to the IN function.\n* In other cases, arguments are compared as floating point, or real, numbers.\n\nNote that if a string column is being compared with a numeric value, MariaDB\nwill not use the index on the column, as there are numerous alternatives that\nmay evaluate as equal (see examples below).\n\nComparison Examples\n-------------------\n\nConverting a string to a number:\n\nSELECT 15+\'15\';\n+---------+\n| 15+\'15\' |\n+---------+\n| 30 |\n+---------+\n\nConverting a number to a string:\n\nSELECT CONCAT(15,\'15\');\n+-----------------+\n| CONCAT(15,\'15\') |\n+-----------------+\n| 1515 |\n+-----------------+\n\nFloating point number errors:\n\nSELECT \'9746718491924563214\' = 9746718491924563213;\n+---------------------------------------------+\n| \'9746718491924563214\' = 9746718491924563213 |\n+---------------------------------------------+\n| 1 |\n+---------------------------------------------+\n\nNumeric equivalence with strings:\n\nSELECT \'5\' = 5;\n+---------+\n| \'5\' = 5 |\n+---------+\n| 1 |\n+---------+\n\nSELECT \' 5\' = 5;\n+------------+\n| \' 5\' = 5 |\n+------------+\n| 1 |\n+------------+\n\nSELECT \' 5 \' = 5;\n+--------------+\n| \' 5 \' = 5 |\n+--------------+\n| 1 |\n+--------------+\n1 row in set, 1 warning (0.000 sec)\n\nSHOW WARNINGS;\n+-------+------+--------------------------------------------+\n| Level | Code | Message |\n+-------+------+--------------------------------------------+\n| Note | 1292 | Truncated incorrect DOUBLE value: \' 5 \' |\n+-------+------+--------------------------------------------+\n\nAs a result of the above, MariaDB cannot use the index when comparing a string\nwith a numeric value in the example below:\n\nCREATE TABLE t (a VARCHAR(10), b VARCHAR(10), INDEX idx_a (a));\n\nINSERT INTO t VALUES \n (\'1\', \'1\'), (\'2\', \'2\'), (\'3\', \'3\'),\n (\'4\', \'4\'), (\'5\', \'5\'), (\'1\', \'5\');\n\nEXPLAIN SELECT * FROM t WHERE a = \'3\' \\G\n*************************** 1. row ***************************\n id: 1\n select_type: SIMPLE\n table: t\n type: ref\npossible_keys: idx_a\n key: idx_a\n key_len: 13\n ref: const\n rows: 1\n Extra: Using index condition\n\nEXPLAIN SELECT * FROM t WHERE a = 3 \\G\n*************************** 1. row ***************************\n id: 1\n select_type: SIMPLE\n table: t\n type: ALL\npossible_keys: idx_a\n key: NULL\n key_len: NULL\n ref: NULL\n rows: 6\n Extra: Using where\n\nRules for Conversion on Dyadic Arithmetic Operations\n----------------------------------------------------\n\nImplicit type conversion also takes place on dyadic arithmetic operations\n(+,-,*,/). MariaDB chooses the minimum data type that is guaranteed to fit the\nresult and converts both arguments to the result data type.\n\nFor addition (+), subtraction (-) and multiplication (*), the result data type\nis chosen as follows:\n\n* If either of the arguments is an approximate number (float, double), the\nresult is double.\n* If either of the arguments is a string (char, varchar, text), the result is\ndouble.\n* If either of the arguments is a decimal number, the result is decimal.\n* If either of the arguments is of a temporal type with a non-zero fractional\nsecond precision (time(N), datetime(N), timestamp(N)), the result is decimal.\n* If either of the arguments is of a temporal type with a zero fractional\nsecond precision (time(0), date, datetime(0), timestamp(0)), the result may\nvary between int, int unsigned, bigint or bigint unsigned, depending on the\nexact data type combination.\n* If both arguments are integer numbers (tinyint, smallint, mediumint,\nbigint), the result may vary between int, int unsigned, bigint or bigint\nunsigned, depending of the exact data types and their signs.\n\nFor division (/), the result data type is chosen as follows:\n\n* If either of the arguments is an approximate number (float, double), the\nresult is double.\n* If either of the arguments is a string (char, varchar, text), the result is\ndouble.\n* Otherwise, the result is decimal.\n\nArithmetic Examples\n-------------------\n\nNote, the above rules mean that when an argument of a temporal data type\nappears in addition or subtraction, it\'s treated as a number by default.\n\nSELECT TIME\'10:20:30\' + 1;\n+--------------------+\n| TIME\'10:20:30\' + 1 |\n+--------------------+\n| 102031 |\n+--------------------+\n\nIn order to do temporal addition or subtraction instead, use the DATE_ADD() or\nDATE_SUB() functions, or an INTERVAL expression as the second argument:\n\nSELECT TIME\'10:20:30\' + INTERVAL 1 SECOND;\n+------------------------------------+\n| TIME\'10:20:30\' + INTERVAL 1 SECOND |\n+------------------------------------+\n| 10:20:31 |\n+------------------------------------+\n\nSELECT \"2.2\" + 3;\n+-----------+\n| \"2.2\" + 3 |\n+-----------+\n| 5.2 |\n+-----------+\n\nSELECT 2.2 + 3;\n+---------+\n| 2.2 + 3 |\n+---------+\n| 5.2 |\n+---------+\n\nSELECT 2.2 / 3;\n+---------+\n| 2.2 / 3 |\n+---------+\n| 0.73333 |\n+---------+\n\nSELECT \"2.2\" / 3;\n+--------------------+\n| \"2.2\" / 3 |\n+--------------------+\n| 0.7333333333333334 |\n+--------------------+\n\nURL: https://mariadb.com/kb/en/type-conversion/','','https://mariadb.com/kb/en/type-conversion/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (690,37,'_rowid','Syntax\n------\n\n_rowid\n\nDescription\n-----------\n\nThe _rowid pseudo column is mapped to the primary key in the related table.\nThis can be used as a replacement of the rowid pseudo column in other\ndatabases. Another usage is to simplify sql queries as one doesn\'t have to\nknow the name of the primary key.\n\nExamples\n--------\n\ncreate table t1 (a int primary key, b varchar(80));\ninsert into t1 values (1,\"one\"),(2,\"two\");\nselect * from t1 where _rowid=1;\n\n+---+------+\n| a | b |\n+---+------+\n| 1 | one |\n+---+------+\n\nupdate t1 set b=\"three\" where _rowid=2;\nselect * from t1 where _rowid>=1 and _rowid<=10;\n\n+---+-------+\n| a | b |\n+---+-------+\n| 1 | one |\n| 2 | three |\n+---+-------+\n\nURL: https://mariadb.com/kb/en/_rowid/','','https://mariadb.com/kb/en/_rowid/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (691,38,'ALTER TABLE','Syntax\n------\n\nALTER [ONLINE] [IGNORE] TABLE [IF EXISTS] tbl_name\n [WAIT n | NOWAIT]\n alter_specification [, alter_specification] ...\nalter_specification:\n table_option ...\n | ADD [COLUMN] [IF NOT EXISTS] col_name column_definition\n [FIRST | AFTER col_name ]\n | ADD [COLUMN] [IF NOT EXISTS] (col_name column_definition,...)\n | ADD {INDEX|KEY} [IF NOT EXISTS] [index_name]\n [index_type] (index_col_name,...) [index_option] ...\n | ADD [CONSTRAINT [symbol]] PRIMARY KEY\n [index_type] (index_col_name,...) [index_option] ...\n | ADD [CONSTRAINT [symbol]]\n UNIQUE [INDEX|KEY] [index_name]\n [index_type] (index_col_name,...) [index_option] ...\n | ADD FULLTEXT [INDEX|KEY] [index_name]\n (index_col_name,...) [index_option] ...\n | ADD SPATIAL [INDEX|KEY] [index_name]\n (index_col_name,...) [index_option] ...\n | ADD [CONSTRAINT [symbol]]\n FOREIGN KEY [IF NOT EXISTS] [index_name] (index_col_name,...)\n reference_definition\n | ADD PERIOD FOR SYSTEM_TIME (start_column_name, end_column_name)\n | ALTER [COLUMN] col_name SET DEFAULT literal | (expression)\n | ALTER [COLUMN] col_name DROP DEFAULT\n | ALTER {INDEX|KEY} index_name [NOT] INVISIBLE\n | CHANGE [COLUMN] [IF EXISTS] old_col_name new_col_name column_definition\n [FIRST|AFTER col_name]\n | MODIFY [COLUMN] [IF EXISTS] col_name column_definition\n [FIRST | AFTER col_name]\n | DROP [COLUMN] [IF EXISTS] col_name [RESTRICT|CASCADE]\n | DROP PRIMARY KEY\n | DROP {INDEX|KEY} [IF EXISTS] index_name\n | DROP FOREIGN KEY [IF EXISTS] fk_symbol\n | DROP CONSTRAINT [IF EXISTS] constraint_name\n | DISABLE KEYS\n | ENABLE KEYS\n | RENAME [TO] new_tbl_name\n | ORDER BY col_name [, col_name] ...\n | RENAME COLUMN old_col_name TO new_col_name\n | RENAME {INDEX|KEY} old_index_name TO new_index_name\n | CONVERT TO CHARACTER SET charset_name [COLLATE collation_name]\n | [DEFAULT] CHARACTER SET [=] charset_name\n | [DEFAULT] COLLATE [=] collation_name\n | DISCARD TABLESPACE\n | IMPORT TABLESPACE\n | ALGORITHM [=] {DEFAULT|INPLACE|COPY|NOCOPY|INSTANT}\n | LOCK [=] {DEFAULT|NONE|SHARED|EXCLUSIVE}\n | FORCE\n | partition_options\n | ADD PARTITION [IF NOT EXISTS] (partition_definition)\n | DROP PARTITION [IF EXISTS] partition_names\n | COALESCE PARTITION number\n | REORGANIZE PARTITION [partition_names INTO (partition_definitions)]\n | ANALYZE PARTITION partition_names\n | CHECK PARTITION partition_names\n | OPTIMIZE PARTITION partition_names\n | REBUILD PARTITION partition_names\n | REPAIR PARTITION partition_names\n | EXCHANGE PARTITION partition_name WITH TABLE tbl_name\n | REMOVE PARTITIONING\n | ADD SYSTEM VERSIONING\n | DROP SYSTEM VERSIONING\nindex_col_name:\n col_name [(length)] [ASC | DESC]\nindex_type:\n USING {BTREE | HASH | RTREE}\nindex_option:\n [ KEY_BLOCK_SIZE [=] value\n | index_type\n | WITH PARSER parser_name\n | COMMENT \'string\'\n | CLUSTERING={YES| NO} ]\n [ IGNORED | NOT IGNORED ]\ntable_options:\n table_option [[,] table_option] ...\n\nDescription\n-----------\n\nALTER TABLE enables you to change the structure of an existing table. For\nexample, you can add or delete columns, create or destroy indexes, change the\ntype of existing columns, or rename columns or the table itself. You can also\nchange the comment for the table and the storage engine of the table.\n\nIf another connection is using the table, a metadata lock is active, and this\nstatement will wait until the lock is released. This is also true for\nnon-transactional tables.\n\nWhen adding a UNIQUE index on a column (or a set of columns) which have\nduplicated values, an error will be produced and the statement will be\nstopped. To suppress the error and force the creation of UNIQUE indexes,\ndiscarding duplicates, the IGNORE option can be specified. This can be useful\nif a column (or a set of columns) should be UNIQUE but it contains duplicate\nvalues; however, this technique provides no control on which rows are\npreserved and which are deleted. Also, note that IGNORE is accepted but\nignored in ALTER TABLE ... EXCHANGE PARTITION statements.\n\nThis statement can also be used to rename a table. For details see RENAME\nTABLE.\n\nWhen an index is created, the storage engine may use a configurable buffer in\nthe process. Incrementing the buffer speeds up the index creation. Aria and\nMyISAM allocate a buffer whose size is defined by aria_sort_buffer_size or\nmyisam_sort_buffer_size, also used for REPAIR TABLE. InnoDB allocates three\nbuffers whose size is defined by innodb_sort_buffer_size.\n\nPrivileges\n----------\n\nExecuting the ALTER TABLE statement generally requires at least the ALTER\nprivilege for the table or the database..\n\nIf you are renaming a table, then it also requires the DROP, CREATE and INSERT\nprivileges for the table or the database as well.\n\nOnline DDL\n----------\n\nOnline DDL is supported with the ALGORITHM and LOCK clauses.\n\nSee InnoDB Online DDL Overview for more information on online DDL with InnoDB.\n\nALTER ONLINE TABLE\n------------------\n\nALTER ONLINE TABLE also works for partitioned tables.\n\nOnline ALTER TABLE is available by executing the following:\n\nALTER ONLINE TABLE ...;\n\nThis statement has the following semantics:\n\nThis statement is equivalent to the following:\n\nALTER TABLE ... LOCK=NONE;\n\nSee the LOCK alter specification for more information.\n\nThis statement is equivalent to the following:\n\nALTER TABLE ... ALGORITHM=INPLACE;\n\nSee the ALGORITHM alter specification for more information.\n\nWAIT/NOWAIT\n-----------\n\nMariaDB starting with 10.3.0\n----------------------------\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nIF EXISTS\n---------\n\nThe IF EXISTS and IF NOT EXISTS clauses are available for the following:\n\nADD COLUMN [IF NOT EXISTS]\nADD INDEX [IF NOT EXISTS]\nADD FOREIGN KEY [IF NOT EXISTS]\nADD PARTITION [IF NOT EXISTS]\nCREATE INDEX [IF NOT EXISTS]\nDROP COLUMN [IF EXISTS]\nDROP INDEX [IF EXISTS]\nDROP FOREIGN KEY [IF EXISTS]\nDROP PARTITION [IF EXISTS]\nCHANGE COLUMN [IF EXISTS]\nMODIFY COLUMN [IF EXISTS]\nDROP INDEX [IF EXISTS]\nWhen IF EXISTS and IF NOT EXISTS are used in clauses, queries will not report\nerrors when the condition is triggered for that clause. A warning with the\nsame message text will be issued and the ALTER will move on to the next clause\nin the statement (or end if finished).\n\nMariaDB starting with 10.5.2\n----------------------------\nIf this is directive is used after ALTER ... TABLE, one will not get an error\nif the table doesn\'t exist.\n\nColumn Definitions\n------------------\n\nSee CREATE TABLE: Column Definitions for information about column definitions.\n\nIndex Definitions\n-----------------\n\nSee CREATE TABLE: Index Definitions for information about index definitions.\n\nThe CREATE INDEX and DROP INDEX statements can also be used to add or remove\nan index.\n\nCharacter Sets and Collations\n-----------------------------\n\nCONVERT TO CHARACTER SET charset_name [COLLATE collation_name]\n[DEFAULT] CHARACTER SET [=] charset_name\n[DEFAULT] COLLATE [=] collation_name\nSee Setting Character Sets and Collations for details on setting the character\nsets and collations.\n\nAlter Specifications\n--------------------\n\nTable Options\n-------------\n\nSee CREATE TABLE: Table Options for information about table options.\n\nADD COLUMN\n----------\n\n... ADD COLUMN [IF NOT EXISTS] (col_name column_definition,...)\nAdds a column to the table. The syntax is the same as in CREATE TABLE. If you\nare using IF NOT_EXISTS the column will not be added if it was not there\nalready. This is very useful when doing scripts to modify tables.\n\nThe FIRST and AFTER clauses affect the physical order of columns in the\ndatafile. Use FIRST to add a column in the first (leftmost) position, or AFTER\nfollowed by a column name to add the new column in any other position. Note\nthat, nowadays, the physical position of a column is usually irrelevant.\n\nSee also Instant ADD COLUMN for InnoDB.\n\nDROP COLUMN\n-----------\n\n... DROP COLUMN [IF EXISTS] col_name [CASCADE|RESTRICT]\nDrops the column from the table. If you are using IF EXISTS you will not get\nan error if the column didn\'t exist. If the column is part of any index, the\ncolumn will be dropped from them, except if you add a new column with\nidentical name at the same time. The index will be dropped if all columns from\nthe index were dropped. If the column was used in a view or trigger, you will\nget an error next time the view or trigger is accessed.\n\nMariaDB starting with 10.2.8\n----------------------------\nDropping a column that is part of a multi-column UNIQUE constraint is not\npermitted. For example:\n\nCREATE TABLE a (\n a int,\n b int,\n primary key (a,b)\n);\n\nALTER TABLE x DROP COLUMN a;\n[42000][1072] Key column \'A\' doesn\'t exist in table\n\nThe reason is that dropping column a would result in the new constraint that\nall values in column b be unique. In order to drop the column, an explicit\nDROP PRIMARY KEY and ADD PRIMARY KEY would be required. Up until MariaDB\n10.2.7, the column was dropped and the additional constraint applied,\nresulting in the following structure:\n\nALTER TABLE x DROP COLUMN a;\nQuery OK, 0 rows affected (0.46 sec)\n\nDESC x;\n+-------+---------+------+-----+---------+-------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+---------+------+-----+---------+-------+\n| b | int(11) | NO | PRI | NULL | |\n+-------+---------+------+-----+---------+-------+\n\nMariaDB starting with 10.4.0\n----------------------------\nMariaDB 10.4.0 supports instant DROP COLUMN. DROP COLUMN of an indexed column\nwould imply DROP INDEX (and in the case of a non-UNIQUE multi-column index,\npossibly ADD INDEX). These will not be allowed with ALGORITHM=INSTANT, but\nunlike before, they can be allowed with ALGORITHM=NOCOPY\n\nRESTRICT and CASCADE are allowed to make porting from other database systems\neasier. In MariaDB, they do nothing.\n\nMODIFY COLUMN\n-------------\n\nAllows you to modify the type of a column. The column will be at the same\nplace as the original column and all indexes on the column will be kept. Note\nthat when modifying column, you should specify all attributes for the new\ncolumn.\n\nCREATE TABLE t1 (a INT UNSIGNED AUTO_INCREMENT, PRIMARY KEY((a));\nALTER TABLE t1 MODIFY a BIGINT UNSIGNED AUTO_INCREMENT;\n\nCHANGE COLUMN\n-------------\n\nWorks like MODIFY COLUMN except that you can also change the name of the\ncolumn. The column will be at the same place as the original column and all\nindex on the column will be kept.\n\nCREATE TABLE t1 (a INT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(a));\nALTER TABLE t1 CHANGE a b BIGINT UNSIGNED AUTO_INCREMENT;\n\nALTER COLUMN\n------------\n\nThis lets you change column options.\n\nCREATE TABLE t1 (a INT UNSIGNED AUTO_INCREMENT, b varchar(50), PRIMARY KEY(a));\nALTER TABLE t1 ALTER b SET DEFAULT \'hello\';\n\nRENAME INDEX/KEY\n----------------\n\nMariaDB starting with 10.5.2\n----------------------------\nFrom MariaDB 10.5.2, it is possible to rename an index using the RENAME INDEX\n(or RENAME KEY) syntax, for example:\n\nALTER TABLE t1 RENAME INDEX i_old TO i_new;\n\nRENAME COLUMN\n-------------\n\nMariaDB starting with 10.5.2\n----------------------------\nFrom MariaDB 10.5.2, it is possible to rename a column using the RENAME COLUMN\nsyntax, for example:\n\nALTER TABLE t1 RENAME COLUMN c_old TO c_new;\n\nADD PRIMARY KEY\n---------------\n\nAdd a primary key.\n\nFor PRIMARY KEY indexes, you can specify a name for the index, but it is\nsilently ignored, and the name of the index is always PRIMARY.\n\nSee Getting Started with Indexes: Primary Key for more information.\n\nDROP PRIMARY KEY\n----------------\n\nDrop a primary key.\n\nFor PRIMARY KEY indexes, you can specify a name for the index, but it is\nsilently ignored, and the name of the index is always PRIMARY.\n\nSee Getting Started with Indexes: Primary Key for more information.\n\nADD FOREIGN KEY\n---------------\n\nAdd a foreign key.\n\nFor FOREIGN KEY indexes, a reference definition must be provided.\n\nFor FOREIGN KEY indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nFirst, you have to specify the name of the target (parent) table and a column\nor a column list which must be indexed and whose values must match to the\nforeign key\'s values. The MATCH clause is accepted to improve the\ncompatibility with other DBMS\'s, but has no meaning in MariaDB. The ON DELETE\nand ON UPDATE clauses specify what must be done when a DELETE (or a REPLACE)\nstatements attempts to delete a referenced row from the parent table, and when\nan UPDATE statement attempts to modify the referenced foreign key columns in a\nparent table row, respectively. The following options are allowed:\n\n* RESTRICT: The delete/update operation is not performed. The statement\nterminates with a 1451 error (SQLSTATE \'2300\').\n* NO ACTION: Synonym for RESTRICT.\n* CASCADE: The delete/update operation is performed in both tables.\n* SET NULL: The update or delete goes ahead in the parent table, and the\ncorresponding foreign key fields in the child table are set to NULL. (They\nmust not be defined as NOT NULL for this to succeed).\n* SET DEFAULT: This option is implemented only for the legacy PBXT storage\nengine, which is disabled by default and no longer maintained. It sets the\nchild table\'s foreign key fields to their DEFAULT values when the referenced\nparent table key entries are updated or deleted.\n\nIf either clause is omitted, the default behavior for the omitted clause is\nRESTRICT.\n\nSee Foreign Keys for more information.\n\nDROP FOREIGN KEY\n----------------\n\nDrop a foreign key.\n\nSee Foreign Keys for more information.\n\nADD INDEX\n---------\n\nAdd a plain index.\n\nPlain indexes are regular indexes that are not unique, and are not acting as a\nprimary key or a foreign key. They are also not the \"specialized\" FULLTEXT or\nSPATIAL indexes.\n\nSee Getting Started with Indexes: Plain Indexes for more information.\n\nDROP INDEX\n----------\n\nDrop a plain index.\n\nPlain indexes are regular indexes that are not unique, and are not acting as a\nprimary key or a foreign key. They are also not the \"specialized\" FULLTEXT or\nSPATIAL indexes.\n\nSee Getting Started with Indexes: Plain Indexes for more information.\n\nADD UNIQUE INDEX\n----------------\n\nAdd a unique index.\n\nThe UNIQUE keyword means that the index will not accept duplicated values,','','https://mariadb.com/kb/en/alter-table/'); -update help_topic set description = CONCAT(description, '\nexcept for NULLs. An error will raise if you try to insert duplicate values in\na UNIQUE index.\n\nFor UNIQUE indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nSee Getting Started with Indexes: Unique Index for more information.\n\nDROP UNIQUE INDEX\n-----------------\n\nDrop a unique index.\n\nThe UNIQUE keyword means that the index will not accept duplicated values,\nexcept for NULLs. An error will raise if you try to insert duplicate values in\na UNIQUE index.\n\nFor UNIQUE indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nSee Getting Started with Indexes: Unique Index for more information.\n\nADD FULLTEXT INDEX\n------------------\n\nAdd a FULLTEXT index.\n\nSee Full-Text Indexes for more information.\n\nDROP FULLTEXT INDEX\n-------------------\n\nDrop a FULLTEXT index.\n\nSee Full-Text Indexes for more information.\n\nADD SPATIAL INDEX\n-----------------\n\nAdd a SPATIAL index.\n\nSee SPATIAL INDEX for more information.\n\nDROP SPATIAL INDEX\n------------------\n\nDrop a SPATIAL index.\n\nSee SPATIAL INDEX for more information.\n\nENABLE/ DISABLE KEYS\n--------------------\n\nDISABLE KEYS will disable all non unique keys for the table for storage\nengines that support this (at least MyISAM and Aria). This can be used to\nspeed up inserts into empty tables.\n\nENABLE KEYS will enable all disabled keys.\n\nRENAME TO\n---------\n\nRenames the table. See also RENAME TABLE.\n\nADD CONSTRAINT\n--------------\n\nModifies the table adding a constraint on a particular column or columns.\n\nMariaDB starting with 10.2.1\n----------------------------\nMariaDB 10.2.1 introduced new ways to define a constraint.\n\nNote: Before MariaDB 10.2.1, constraint expressions were accepted in syntax,\nbut ignored.\n\nALTER TABLE table_name \nADD CONSTRAINT [constraint_name] CHECK(expression);\nBefore a row is inserted or updated, all constraints are evaluated in the\norder they are defined. If any constraint fails, then the row will not be\nupdated. One can use most deterministic functions in a constraint, including\nUDF\'s.\n\nCREATE TABLE account_ledger (\n id INT PRIMARY KEY AUTO_INCREMENT,\n transaction_name VARCHAR(100),\n credit_account VARCHAR(100),\n credit_amount INT,\n debit_account VARCHAR(100),\n debit_amount INT);\n\nALTER TABLE account_ledger \nADD CONSTRAINT is_balanced \n CHECK((debit_amount + credit_amount) = 0);\n\nThe constraint_name is optional. If you don\'t provide one in the ALTER TABLE\nstatement, MariaDB auto-generates a name for you. This is done so that you can\nremove it later using DROP CONSTRAINT clause.\n\nYou can disable all constraint expression checks by setting the variable\ncheck_constraint_checks to OFF. You may find this useful when loading a table\nthat violates some constraints that you want to later find and fix in SQL.\n\nTo view constraints on a table, query information_schema.TABLE_CONSTRAINTS:\n\nSELECT CONSTRAINT_NAME, TABLE_NAME, CONSTRAINT_TYPE \nFROM information_schema.TABLE_CONSTRAINTS\nWHERE TABLE_NAME = \'account_ledger\';\n\n+-----------------+----------------+-----------------+\n| CONSTRAINT_NAME | TABLE_NAME | CONSTRAINT_TYPE |\n+-----------------+----------------+-----------------+\n| is_balanced | account_ledger | CHECK |\n+-----------------+----------------+-----------------+\n\nDROP CONSTRAINT\n---------------\n\nMariaDB starting with 10.2.22\n-----------------------------\nDROP CONSTRAINT for UNIQUE and FOREIGN KEY constraints was introduced in\nMariaDB 10.2.22 and MariaDB 10.3.13.\n\nMariaDB starting with 10.2.1\n----------------------------\nDROP CONSTRAINT for CHECK constraints was introduced in MariaDB 10.2.1\n\nModifies the table, removing the given constraint.\n\nALTER TABLE table_name\nDROP CONSTRAINT constraint_name;\n\nWhen you add a constraint to a table, whether through a CREATE TABLE or ALTER\nTABLE...ADD CONSTRAINT statement, you can either set a constraint_name\nyourself, or allow MariaDB to auto-generate one for you. To view constraints\non a table, query information_schema.TABLE_CONSTRAINTS. For instance,\n\nCREATE TABLE t (\n a INT,\n b INT,\n c INT,\n CONSTRAINT CHECK(a > b),\n CONSTRAINT check_equals CHECK(a = c));\n\nSELECT CONSTRAINT_NAME, TABLE_NAME, CONSTRAINT_TYPE \nFROM information_schema.TABLE_CONSTRAINTS\nWHERE TABLE_NAME = \'t\';\n\n+-----------------+----------------+-----------------+\n| CONSTRAINT_NAME | TABLE_NAME | CONSTRAINT_TYPE |\n+-----------------+----------------+-----------------+\n| check_equals | t | CHECK |\n| CONSTRAINT_1 | t | CHECK |\n+-----------------+----------------+-----------------+\n\nTo remove a constraint from the table, issue an ALTER TABLE...DROP CONSTRAINT\nstatement. For example,\n\nALTER TABLE t DROP CONSTRAINT is_unique;\n\nADD SYSTEM VERSIONING\n---------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nSystem-versioned tables was added in MariaDB 10.3.4.\n\nAdd system versioning.\n\nDROP SYSTEM VERSIONING\n----------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nSystem-versioned tables was added in MariaDB 10.3.4.\n\nDrop system versioning.\n\nADD PERIOD FOR SYSTEM_TIME\n--------------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nSystem-versioned tables was added in MariaDB 10.3.4.\n\nFORCE\n-----\n\nALTER TABLE ... FORCE can force MariaDB to re-build the table.\n\nIn MariaDB 5.5 and before, this could only be done by setting the ENGINE table\noption to its old value. For example, for an InnoDB table, one could execute\nthe following:\n\nALTER TABLE tab_name ENGINE = InnoDB;\n\nThe FORCE option can be used instead. For example, :\n\nALTER TABLE tab_name FORCE;\n\nWith InnoDB, the table rebuild will only reclaim unused space (i.e. the space\npreviously used for deleted rows) if the innodb_file_per_table system variable\nis set to ON. If the system variable is OFF, then the space will not be\nreclaimed, but it will be-re-used for new data that\'s later added.\n\nEXCHANGE PARTITION\n------------------\n\nThis is used to exchange the tablespace files between a partition and another\ntable.\n\nSee copying InnoDB\'s transportable tablespaces for more information.\n\nDISCARD TABLESPACE\n------------------\n\nThis is used to discard an InnoDB table\'s tablespace.\n\nSee copying InnoDB\'s transportable tablespaces for more information.\n\nIMPORT TABLESPACE\n-----------------\n\nThis is used to import an InnoDB table\'s tablespace. The tablespace should\nhave been copied from its original server after executing FLUSH TABLES FOR\nEXPORT.\n\nSee copying InnoDB\'s transportable tablespaces for more information.\n\nALTER TABLE ... IMPORT only applies to InnoDB tables. Most other popular\nstorage engines, such as Aria and MyISAM, will recognize their data files as\nsoon as they\'ve been placed in the proper directory under the datadir, and no\nspecial DDL is required to import them.\n\nALGORITHM\n---------\n\nThe ALTER TABLE statement supports the ALGORITHM clause. This clause is one of\nthe clauses that is used to implement online DDL. ALTER TABLE supports several\ndifferent algorithms. An algorithm can be explicitly chosen for an ALTER TABLE\noperation by setting the ALGORITHM clause. The supported values are:\n\n* ALGORITHM=DEFAULT - This implies the default behavior for the specific\nstatement, such as if no ALGORITHM clause is specified.\n* ALGORITHM=COPY\n* ALGORITHM=INPLACE\n* ALGORITHM=NOCOPY - This was added in MariaDB 10.3.7.\n* ALGORITHM=INSTANT - This was added in MariaDB 10.3.7.\n\nSee InnoDB Online DDL Overview: ALGORITHM for information on how the ALGORITHM\nclause affects InnoDB.\n\nALGORITHM=DEFAULT\n-----------------\n\nThe default behavior, which occurs if ALGORITHM=DEFAULT is specified, or if\nALGORITHM is not specified at all, usually only makes a copy if the operation\ndoesn\'t support being done in-place at all. In this case, the most efficient\navailable algorithm will usually be used.\n\nHowever, in MariaDB 10.3.6 and before, if the value of the old_alter_table\nsystem variable is set to ON, then the default behavior is to perform ALTER\nTABLE operations by making a copy of the table using the old algorithm.\n\nIn MariaDB 10.3.7 and later, the old_alter_table system variable is\ndeprecated. Instead, the alter_algorithm system variable defines the default\nalgorithm for ALTER TABLE operations.\n\nALGORITHM=COPY\n--------------\n\nALGORITHM=COPY is the name for the original ALTER TABLE algorithm from early\nMariaDB versions.\n\nWhen ALGORITHM=COPY is set, MariaDB essentially does the following operations:\n\n-- Create a temporary table with the new definition\nCREATE TEMPORARY TABLE tmp_tab (\n...\n);\n\n-- Copy the data from the original table\nINSERT INTO tmp_tab\n SELECT * FROM original_tab;\n\n-- Drop the original table\nDROP TABLE original_tab;\n\n-- Rename the temporary table, so that it replaces the original one\nRENAME TABLE tmp_tab TO original_tab;\n\nThis algorithm is very inefficient, but it is generic, so it works for all\nstorage engines.\n\nIf ALGORITHM=COPY is specified, then the copy algorithm will be used even if\nit is not necessary. This can result in a lengthy table copy. If multiple\nALTER TABLE operations are required that each require the table to be rebuilt,\nthen it is best to specify all operations in a single ALTER TABLE statement,\nso that the table is only rebuilt once.\n\nALGORITHM=INPLACE\n-----------------\n\nALGORITHM=COPY can be incredibly slow, because the whole table has to be\ncopied and rebuilt. ALGORITHM=INPLACE was introduced as a way to avoid this by\nperforming operations in-place and avoiding the table copy and rebuild, when\npossible.\n\nWhen ALGORITHM=INPLACE is set, the underlying storage engine uses\noptimizations to perform the operation while avoiding the table copy and\nrebuild. However, INPLACE is a bit of a misnomer, since some operations may\nstill require the table to be rebuilt for some storage engines. Regardless,\nseveral operations can be performed without a full copy of the table for some\nstorage engines.\n\nA more accurate name would have been ALGORITHM=ENGINE, where ENGINE refers to\nan \"engine-specific\" algorithm.\n\nIf an ALTER TABLE operation supports ALGORITHM=INPLACE, then it can be\nperformed using optimizations by the underlying storage engine, but it may\nrebuilt.\n\nSee InnoDB Online DDL Operations with ALGORITHM=INPLACE for more.\n\nALGORITHM=NOCOPY\n----------------\n\nALGORITHM=NOCOPY was introduced in MariaDB 10.3.7.\n\nALGORITHM=INPLACE can sometimes be surprisingly slow in instances where it has\nto rebuild the clustered index, because when the clustered index has to be\nrebuilt, the whole table has to be rebuilt. ALGORITHM=NOCOPY was introduced as\na way to avoid this.\n\nIf an ALTER TABLE operation supports ALGORITHM=NOCOPY, then it can be\nperformed without rebuilding the clustered index.\n\nIf ALGORITHM=NOCOPY is specified for an ALTER TABLE operation that does not\nsupport ALGORITHM=NOCOPY, then an error will be raised. In this case, raising\nan error is preferable, if the alternative is for the operation to rebuild the\nclustered index, and perform unexpectedly slowly.\n\nSee InnoDB Online DDL Operations with ALGORITHM=NOCOPY for more.\n\nALGORITHM=INSTANT\n-----------------\n\nALGORITHM=INSTANT was introduced in MariaDB 10.3.7.\n\nALGORITHM=INPLACE can sometimes be surprisingly slow in instances where it has\nto modify data files. ALGORITHM=INSTANT was introduced as a way to avoid this.\n\nIf an ALTER TABLE operation supports ALGORITHM=INSTANT, then it can be\nperformed without modifying any data files.\n\nIf ALGORITHM=INSTANT is specified for an ALTER TABLE operation that does not\nsupport ALGORITHM=INSTANT, then an error will be raised. In this case, raising\nan error is preferable, if the alternative is for the operation to modify data\nfiles, and perform unexpectedly slowly.\n\nSee InnoDB Online DDL Operations with ALGORITHM=INSTANT for more.\n\nLOCK\n----\n\nThe ALTER TABLE statement supports the LOCK clause. This clause is one of the\nclauses that is used to implement online DDL. ALTER TABLE supports several\ndifferent locking strategies. A locking strategy can be explicitly chosen for\nan ALTER TABLE operation by setting the LOCK clause. The supported values are:\n\n* DEFAULT: Acquire the least restrictive lock on the table that is supported\nfor the specific operation. Permit the maximum amount of concurrency that is\nsupported for the specific operation.\n* NONE: Acquire no lock on the table. Permit all concurrent DML. If this\nlocking strategy is not permitted for an operation, then an error is raised.\n* SHARED: Acquire a read lock on the table. Permit read-only concurrent DML.\nIf this locking strategy is not permitted for an operation, then an error is\nraised.\n* EXCLUSIVE: Acquire a write lock on the table. Do not permit concurrent DML.\n\nDifferent storage engines support different locking strategies for different\noperations. If a specific locking strategy is chosen for an ALTER TABLE\noperation, and that table\'s storage engine does not support that locking\nstrategy for that specific operation, then an error will be raised.\n\nIf the LOCK clause is not explicitly set, then the operation uses LOCK=DEFAULT.\n\nALTER ONLINE TABLE is equivalent to LOCK=NONE. Therefore, the ALTER ONLINE\nTABLE statement can be used to ensure that your ALTER TABLE operation allows\nall concurrent DML.\n\nSee InnoDB Online DDL Overview: LOCK for information on how the LOCK clause\naffects InnoDB.\n\nProgress Reporting\n------------------\n\nMariaDB provides progress reporting for ALTER TABLE statement for clients that\nsupport the new progress reporting protocol. For example, if you were using\nthe mysql client, then the progress report might look like this::\n\nALTER TABLE test ENGINE=Aria;\nStage: 1 of 2 \'copy to tmp table\' 46% of stage\n\nThe progress report is also shown in the output of the SHOW PROCESSLIST\nstatement and in the contents of the information_schema.PROCESSLIST table.\n\nSee Progress Reporting for more information.\n\nAborting ALTER TABLE Operations\n-------------------------------\n\nIf an ALTER TABLE operation is being performed and the connection is killed,\nthe changes will be rolled back in a controlled manner. The rollback can be a') WHERE help_topic_id = 691; -update help_topic set description = CONCAT(description, '\nslow operation as the time it takes is relative to how far the operation has\nprogressed.\n\nMariaDB starting with 10.2.13\n-----------------------------\nAborting ALTER TABLE ... ALGORITHM=COPY was made faster by removing excessive\nundo logging (MDEV-11415). This significantly shortens the time it takes to\nabort a running ALTER TABLE operation.\n\nAtomic ALTER TABLE\n------------------\n\nMariaDB starting with 10.6.1\n----------------------------\nFrom MariaDB 10.6, ALTER TABLE is atomic for most engines, including InnoDB,\nMyRocks, MyISAM and Aria (MDEV-25180). This means that if there is a crash\n(server down or power outage) during an ALTER TABLE operation, after recovery,\neither the old table and associated triggers and status will be intact, or the\nnew table will be active.\n\nIn older MariaDB versions one could get leftover #sql-alter..\',\n\'#sql-backup..\' or \'table_name.frm˝\' files if the system crashed during the\nALTER TABLE operation.\n\nSee Atomic DDL for more information.\n\nReplication\n-----------\n\nMariaDB starting with 10.8.0\n----------------------------\nBefore MariaDB 10.8.0, ALTER TABLE got fully executed on the primary first,\nand only then was it replicated and started executing on replicas. From\nMariaDB 10.8.0, ALTER TABLE gets replicated and starts executing on replicas\nwhen it starts executing on the primary, not when it finishes. This way the\nreplication lag caused by a heavy ALTER TABLE can be completely eliminated\n(MDEV-11675).\n\nExamples\n--------\n\nAdding a new column:\n\nALTER TABLE t1 ADD x INT;\n\nDropping a column:\n\nALTER TABLE t1 DROP x;\n\nModifying the type of a column:\n\nALTER TABLE t1 MODIFY x bigint unsigned;\n\nChanging the name and type of a column:\n\nALTER TABLE t1 CHANGE a b bigint unsigned auto_increment;\n\nCombining multiple clauses in a single ALTER TABLE statement, separated by\ncommas:\n\nALTER TABLE t1 DROP x, ADD x2 INT, CHANGE y y2 INT;\n\nChanging the storage engine and adding a comment:\n\nALTER TABLE t1 \n ENGINE = InnoDB\n COMMENT = \'First of three tables containing usage info\';\n\nRebuilding the table (the previous example will also rebuild the table if it\nwas already InnoDB):\n\nALTER TABLE t1 FORCE;\n\nDropping an index:\n\nALTER TABLE rooms DROP INDEX u;\n\nAdding a unique index:\n\nALTER TABLE rooms ADD UNIQUE INDEX u(room_number);\n\nFrom MariaDB 10.5.3, adding a primary key for an application-time period table\nwith a WITHOUT OVERLAPS constraint:\n\nALTER TABLE rooms ADD PRIMARY KEY(room_number, p WITHOUT OVERLAPS);\n\nURL: https://mariadb.com/kb/en/alter-table/') WHERE help_topic_id = 691; +update help_topic set description = CONCAT(description, '\nexcept for NULLs. An error will raise if you try to insert duplicate values in\na UNIQUE index.\n\nFor UNIQUE indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nSee Getting Started with Indexes: Unique Index for more information.\n\nDROP UNIQUE INDEX\n-----------------\n\nDrop a unique index.\n\nThe UNIQUE keyword means that the index will not accept duplicated values,\nexcept for NULLs. An error will raise if you try to insert duplicate values in\na UNIQUE index.\n\nFor UNIQUE indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nSee Getting Started with Indexes: Unique Index for more information.\n\nADD FULLTEXT INDEX\n------------------\n\nAdd a FULLTEXT index.\n\nSee Full-Text Indexes for more information.\n\nDROP FULLTEXT INDEX\n-------------------\n\nDrop a FULLTEXT index.\n\nSee Full-Text Indexes for more information.\n\nADD SPATIAL INDEX\n-----------------\n\nAdd a SPATIAL index.\n\nSee SPATIAL INDEX for more information.\n\nDROP SPATIAL INDEX\n------------------\n\nDrop a SPATIAL index.\n\nSee SPATIAL INDEX for more information.\n\nENABLE/ DISABLE KEYS\n--------------------\n\nDISABLE KEYS will disable all non unique keys for the table for storage\nengines that support this (at least MyISAM and Aria). This can be used to\nspeed up inserts into empty tables.\n\nENABLE KEYS will enable all disabled keys.\n\nRENAME TO\n---------\n\nRenames the table. See also RENAME TABLE.\n\nADD CONSTRAINT\n--------------\n\nModifies the table adding a constraint on a particular column or columns.\n\nMariaDB starting with 10.2.1\n----------------------------\nMariaDB 10.2.1 introduced new ways to define a constraint.\n\nNote: Before MariaDB 10.2.1, constraint expressions were accepted in syntax,\nbut ignored.\n\nALTER TABLE table_name \nADD CONSTRAINT [constraint_name] CHECK(expression);\nBefore a row is inserted or updated, all constraints are evaluated in the\norder they are defined. If any constraint fails, then the row will not be\nupdated. One can use most deterministic functions in a constraint, including\nUDF\'s.\n\nCREATE TABLE account_ledger (\n id INT PRIMARY KEY AUTO_INCREMENT,\n transaction_name VARCHAR(100),\n credit_account VARCHAR(100),\n credit_amount INT,\n debit_account VARCHAR(100),\n debit_amount INT);\n\nALTER TABLE account_ledger \nADD CONSTRAINT is_balanced \n CHECK((debit_amount + credit_amount) = 0);\n\nThe constraint_name is optional. If you don\'t provide one in the ALTER TABLE\nstatement, MariaDB auto-generates a name for you. This is done so that you can\nremove it later using DROP CONSTRAINT clause.\n\nYou can disable all constraint expression checks by setting the variable\ncheck_constraint_checks to OFF. You may find this useful when loading a table\nthat violates some constraints that you want to later find and fix in SQL.\n\nTo view constraints on a table, query information_schema.TABLE_CONSTRAINTS:\n\nSELECT CONSTRAINT_NAME, TABLE_NAME, CONSTRAINT_TYPE \nFROM information_schema.TABLE_CONSTRAINTS\nWHERE TABLE_NAME = \'account_ledger\';\n\n+-----------------+----------------+-----------------+\n| CONSTRAINT_NAME | TABLE_NAME | CONSTRAINT_TYPE |\n+-----------------+----------------+-----------------+\n| is_balanced | account_ledger | CHECK |\n+-----------------+----------------+-----------------+\n\nDROP CONSTRAINT\n---------------\n\nMariaDB starting with 10.2.22\n-----------------------------\nDROP CONSTRAINT for UNIQUE and FOREIGN KEY constraints was introduced in\nMariaDB 10.2.22 and MariaDB 10.3.13.\n\nMariaDB starting with 10.2.1\n----------------------------\nDROP CONSTRAINT for CHECK constraints was introduced in MariaDB 10.2.1\n\nModifies the table, removing the given constraint.\n\nALTER TABLE table_name\nDROP CONSTRAINT constraint_name;\n\nWhen you add a constraint to a table, whether through a CREATE TABLE or ALTER\nTABLE...ADD CONSTRAINT statement, you can either set a constraint_name\nyourself, or allow MariaDB to auto-generate one for you. To view constraints\non a table, query information_schema.TABLE_CONSTRAINTS. For instance,\n\nCREATE TABLE t (\n a INT,\n b INT,\n c INT,\n CONSTRAINT CHECK(a > b),\n CONSTRAINT check_equals CHECK(a = c));\n\nSELECT CONSTRAINT_NAME, TABLE_NAME, CONSTRAINT_TYPE \nFROM information_schema.TABLE_CONSTRAINTS\nWHERE TABLE_NAME = \'t\';\n\n+-----------------+----------------+-----------------+\n| CONSTRAINT_NAME | TABLE_NAME | CONSTRAINT_TYPE |\n+-----------------+----------------+-----------------+\n| check_equals | t | CHECK |\n| CONSTRAINT_1 | t | CHECK |\n+-----------------+----------------+-----------------+\n\nTo remove a constraint from the table, issue an ALTER TABLE...DROP CONSTRAINT\nstatement. For example,\n\nALTER TABLE t DROP CONSTRAINT is_unique;\n\nADD SYSTEM VERSIONING\n---------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nSystem-versioned tables was added in MariaDB 10.3.4.\n\nAdd system versioning.\n\nDROP SYSTEM VERSIONING\n----------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nSystem-versioned tables was added in MariaDB 10.3.4.\n\nDrop system versioning.\n\nADD PERIOD FOR SYSTEM_TIME\n--------------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nSystem-versioned tables was added in MariaDB 10.3.4.\n\nFORCE\n-----\n\nALTER TABLE ... FORCE can force MariaDB to re-build the table.\n\nIn MariaDB 5.5 and before, this could only be done by setting the ENGINE table\noption to its old value. For example, for an InnoDB table, one could execute\nthe following:\n\nALTER TABLE tab_name ENGINE = InnoDB;\n\nThe FORCE option can be used instead. For example, :\n\nALTER TABLE tab_name FORCE;\n\nWith InnoDB, the table rebuild will only reclaim unused space (i.e. the space\npreviously used for deleted rows) if the innodb_file_per_table system variable\nis set to ON (the default). If the system variable is OFF, then the space will\nnot be reclaimed, but it will be-re-used for new data that\'s later added.\n\nEXCHANGE PARTITION\n------------------\n\nThis is used to exchange the contents of a partition with another table.\n\nThis is performed by swapping the tablespaces of the partition with the other\ntable.\n\nSee copying InnoDB\'s transportable tablespaces for more information.\n\nDISCARD TABLESPACE\n------------------\n\nThis is used to discard an InnoDB table\'s tablespace.\n\nSee copying InnoDB\'s transportable tablespaces for more information.\n\nIMPORT TABLESPACE\n-----------------\n\nThis is used to import an InnoDB table\'s tablespace. The tablespace should\nhave been copied from its original server after executing FLUSH TABLES FOR\nEXPORT.\n\nSee copying InnoDB\'s transportable tablespaces for more information.\n\nALTER TABLE ... IMPORT only applies to InnoDB tables. Most other popular\nstorage engines, such as Aria and MyISAM, will recognize their data files as\nsoon as they\'ve been placed in the proper directory under the datadir, and no\nspecial DDL is required to import them.\n\nALGORITHM\n---------\n\nThe ALTER TABLE statement supports the ALGORITHM clause. This clause is one of\nthe clauses that is used to implement online DDL. ALTER TABLE supports several\ndifferent algorithms. An algorithm can be explicitly chosen for an ALTER TABLE\noperation by setting the ALGORITHM clause. The supported values are:\n\n* ALGORITHM=DEFAULT - This implies the default behavior for the specific\nstatement, such as if no ALGORITHM clause is specified.\n* ALGORITHM=COPY\n* ALGORITHM=INPLACE\n* ALGORITHM=NOCOPY - This was added in MariaDB 10.3.7.\n* ALGORITHM=INSTANT - This was added in MariaDB 10.3.7.\n\nSee InnoDB Online DDL Overview: ALGORITHM for information on how the ALGORITHM\nclause affects InnoDB.\n\nALGORITHM=DEFAULT\n-----------------\n\nThe default behavior, which occurs if ALGORITHM=DEFAULT is specified, or if\nALGORITHM is not specified at all, usually only makes a copy if the operation\ndoesn\'t support being done in-place at all. In this case, the most efficient\navailable algorithm will usually be used.\n\nHowever, in MariaDB 10.3.6 and before, if the value of the old_alter_table\nsystem variable is set to ON, then the default behavior is to perform ALTER\nTABLE operations by making a copy of the table using the old algorithm.\n\nIn MariaDB 10.3.7 and later, the old_alter_table system variable is\ndeprecated. Instead, the alter_algorithm system variable defines the default\nalgorithm for ALTER TABLE operations.\n\nALGORITHM=COPY\n--------------\n\nALGORITHM=COPY is the name for the original ALTER TABLE algorithm from early\nMariaDB versions.\n\nWhen ALGORITHM=COPY is set, MariaDB essentially does the following operations:\n\n-- Create a temporary table with the new definition\nCREATE TEMPORARY TABLE tmp_tab (\n...\n);\n\n-- Copy the data from the original table\nINSERT INTO tmp_tab\n SELECT * FROM original_tab;\n\n-- Drop the original table\nDROP TABLE original_tab;\n\n-- Rename the temporary table, so that it replaces the original one\nRENAME TABLE tmp_tab TO original_tab;\n\nThis algorithm is very inefficient, but it is generic, so it works for all\nstorage engines.\n\nIf ALGORITHM=COPY is specified, then the copy algorithm will be used even if\nit is not necessary. This can result in a lengthy table copy. If multiple\nALTER TABLE operations are required that each require the table to be rebuilt,\nthen it is best to specify all operations in a single ALTER TABLE statement,\nso that the table is only rebuilt once.\n\nALGORITHM=INPLACE\n-----------------\n\nALGORITHM=COPY can be incredibly slow, because the whole table has to be\ncopied and rebuilt. ALGORITHM=INPLACE was introduced as a way to avoid this by\nperforming operations in-place and avoiding the table copy and rebuild, when\npossible.\n\nWhen ALGORITHM=INPLACE is set, the underlying storage engine uses\noptimizations to perform the operation while avoiding the table copy and\nrebuild. However, INPLACE is a bit of a misnomer, since some operations may\nstill require the table to be rebuilt for some storage engines. Regardless,\nseveral operations can be performed without a full copy of the table for some\nstorage engines.\n\nA more accurate name would have been ALGORITHM=ENGINE, where ENGINE refers to\nan \"engine-specific\" algorithm.\n\nIf an ALTER TABLE operation supports ALGORITHM=INPLACE, then it can be\nperformed using optimizations by the underlying storage engine, but it may\nrebuilt.\n\nSee InnoDB Online DDL Operations with ALGORITHM=INPLACE for more.\n\nALGORITHM=NOCOPY\n----------------\n\nALGORITHM=NOCOPY was introduced in MariaDB 10.3.7.\n\nALGORITHM=INPLACE can sometimes be surprisingly slow in instances where it has\nto rebuild the clustered index, because when the clustered index has to be\nrebuilt, the whole table has to be rebuilt. ALGORITHM=NOCOPY was introduced as\na way to avoid this.\n\nIf an ALTER TABLE operation supports ALGORITHM=NOCOPY, then it can be\nperformed without rebuilding the clustered index.\n\nIf ALGORITHM=NOCOPY is specified for an ALTER TABLE operation that does not\nsupport ALGORITHM=NOCOPY, then an error will be raised. In this case, raising\nan error is preferable, if the alternative is for the operation to rebuild the\nclustered index, and perform unexpectedly slowly.\n\nSee InnoDB Online DDL Operations with ALGORITHM=NOCOPY for more.\n\nALGORITHM=INSTANT\n-----------------\n\nALGORITHM=INSTANT was introduced in MariaDB 10.3.7.\n\nALGORITHM=INPLACE can sometimes be surprisingly slow in instances where it has\nto modify data files. ALGORITHM=INSTANT was introduced as a way to avoid this.\n\nIf an ALTER TABLE operation supports ALGORITHM=INSTANT, then it can be\nperformed without modifying any data files.\n\nIf ALGORITHM=INSTANT is specified for an ALTER TABLE operation that does not\nsupport ALGORITHM=INSTANT, then an error will be raised. In this case, raising\nan error is preferable, if the alternative is for the operation to modify data\nfiles, and perform unexpectedly slowly.\n\nSee InnoDB Online DDL Operations with ALGORITHM=INSTANT for more.\n\nLOCK\n----\n\nThe ALTER TABLE statement supports the LOCK clause. This clause is one of the\nclauses that is used to implement online DDL. ALTER TABLE supports several\ndifferent locking strategies. A locking strategy can be explicitly chosen for\nan ALTER TABLE operation by setting the LOCK clause. The supported values are:\n\n* DEFAULT: Acquire the least restrictive lock on the table that is supported\nfor the specific operation. Permit the maximum amount of concurrency that is\nsupported for the specific operation.\n* NONE: Acquire no lock on the table. Permit all concurrent DML. If this\nlocking strategy is not permitted for an operation, then an error is raised.\n* SHARED: Acquire a read lock on the table. Permit read-only concurrent DML.\nIf this locking strategy is not permitted for an operation, then an error is\nraised.\n* EXCLUSIVE: Acquire a write lock on the table. Do not permit concurrent DML.\n\nDifferent storage engines support different locking strategies for different\noperations. If a specific locking strategy is chosen for an ALTER TABLE\noperation, and that table\'s storage engine does not support that locking\nstrategy for that specific operation, then an error will be raised.\n\nIf the LOCK clause is not explicitly set, then the operation uses LOCK=DEFAULT.\n\nALTER ONLINE TABLE is equivalent to LOCK=NONE. Therefore, the ALTER ONLINE\nTABLE statement can be used to ensure that your ALTER TABLE operation allows\nall concurrent DML.\n\nSee InnoDB Online DDL Overview: LOCK for information on how the LOCK clause\naffects InnoDB.\n\nProgress Reporting\n------------------\n\nMariaDB provides progress reporting for ALTER TABLE statement for clients that\nsupport the new progress reporting protocol. For example, if you were using\nthe mysql client, then the progress report might look like this::\n\nALTER TABLE test ENGINE=Aria;\nStage: 1 of 2 \'copy to tmp table\' 46% of stage\n\nThe progress report is also shown in the output of the SHOW PROCESSLIST\nstatement and in the contents of the information_schema.PROCESSLIST table.\n\nSee Progress Reporting for more information.\n\nAborting ALTER TABLE Operations\n-------------------------------\n\nIf an ALTER TABLE operation is being performed and the connection is killed,') WHERE help_topic_id = 691; +update help_topic set description = CONCAT(description, '\nthe changes will be rolled back in a controlled manner. The rollback can be a\nslow operation as the time it takes is relative to how far the operation has\nprogressed.\n\nMariaDB starting with 10.2.13\n-----------------------------\nAborting ALTER TABLE ... ALGORITHM=COPY was made faster by removing excessive\nundo logging (MDEV-11415). This significantly shortens the time it takes to\nabort a running ALTER TABLE operation.\n\nAtomic ALTER TABLE\n------------------\n\nMariaDB starting with 10.6.1\n----------------------------\nFrom MariaDB 10.6, ALTER TABLE is atomic for most engines, including InnoDB,\nMyRocks, MyISAM and Aria (MDEV-25180). This means that if there is a crash\n(server down or power outage) during an ALTER TABLE operation, after recovery,\neither the old table and associated triggers and status will be intact, or the\nnew table will be active.\n\nIn older MariaDB versions one could get leftover #sql-alter..\',\n\'#sql-backup..\' or \'table_name.frm˝\' files if the system crashed during the\nALTER TABLE operation.\n\nSee Atomic DDL for more information.\n\nReplication\n-----------\n\nMariaDB starting with 10.8.1\n----------------------------\nBefore MariaDB 10.8.1, ALTER TABLE got fully executed on the primary first,\nand only then was it replicated and started executing on replicas. From\nMariaDB 10.8.1, ALTER TABLE gains an option to replicate sooner and begin\nexecuting on replicas when it merely starts executing on the primary, not when\nit finishes. This way the replication lag caused by a heavy ALTER TABLE can be\ncompletely eliminated (MDEV-11675).\n\nExamples\n--------\n\nAdding a new column:\n\nALTER TABLE t1 ADD x INT;\n\nDropping a column:\n\nALTER TABLE t1 DROP x;\n\nModifying the type of a column:\n\nALTER TABLE t1 MODIFY x bigint unsigned;\n\nChanging the name and type of a column:\n\nALTER TABLE t1 CHANGE a b bigint unsigned auto_increment;\n\nCombining multiple clauses in a single ALTER TABLE statement, separated by\ncommas:\n\nALTER TABLE t1 DROP x, ADD x2 INT, CHANGE y y2 INT;\n\nChanging the storage engine and adding a comment:\n\nALTER TABLE t1 \n ENGINE = InnoDB\n COMMENT = \'First of three tables containing usage info\';\n\nRebuilding the table (the previous example will also rebuild the table if it\nwas already InnoDB):\n\nALTER TABLE t1 FORCE;\n\nDropping an index:\n\nALTER TABLE rooms DROP INDEX u;\n\nAdding a unique index:\n\nALTER TABLE rooms ADD UNIQUE INDEX u(room_number);\n\nFrom MariaDB 10.5.3, adding a primary key for an application-time period table\nwith a WITHOUT OVERLAPS constraint:\n\nALTER TABLE rooms ADD PRIMARY KEY(room_number, p WITHOUT OVERLAPS);\n\nFrom MariaDB 10.8.1, ALTER query can be replicated faster with the setting of\n\nSET @@SESSION.binlog_alter_two_phase = true;\n\nprior the ALTER query. Binlog would contain two event groups\n\n| master-bin.000001 | 495 | Gtid | 1 | 537 | GTID\n0-1-2 START ALTER |\n| master-bin.000001 | 537 | Query | 1 | 655 | use\n`test`; alter table t add column b int, algorithm=inplace |\n| master-bin.000001 | 655 | Gtid | 1 | 700 | GTID\n0-1-3 COMMIT ALTER id=2 |\n| master-bin.000001 | 700 | Query | 1 | 835 | use\n`test`; alter table t add column b int, algorithm=inplace |\n\nof which the first one gets delivered to replicas before ALTER is taken to\nactual execution on the primary.\n\nURL: https://mariadb.com/kb/en/alter-table/') WHERE help_topic_id = 691; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (692,38,'ALTER DATABASE','Modifies a database, changing its overall characteristics.\n\nSyntax\n------\n\nALTER {DATABASE | SCHEMA} [db_name]\n alter_specification ...\nALTER {DATABASE | SCHEMA} db_name\n UPGRADE DATA DIRECTORY NAME\n\nalter_specification:\n [DEFAULT] CHARACTER SET [=] charset_name\n | [DEFAULT] COLLATE [=] collation_name\n | COMMENT [=] \'comment\'\n\nDescription\n-----------\n\nALTER DATABASE enables you to change the overall characteristics of a\ndatabase. These characteristics are stored in the db.opt file in the database\ndirectory. To use ALTER DATABASE, you need the ALTER privilege on the\ndatabase. ALTER SCHEMA is a synonym for ALTER DATABASE.\n\nThe CHARACTER SET clause changes the default database character set. The\nCOLLATE clause changes the default database collation. See Character Sets and\nCollations for more.\n\nYou can see what character sets and collations are available using,\nrespectively, the SHOW CHARACTER SET and SHOW COLLATION statements.\n\nChanging the default character set/collation of a database does not change the\ncharacter set/collation of any stored procedures or stored functions that were\npreviously created, and relied on the defaults. These need to be dropped and\nrecreated in order to apply the character set/collation changes.\n\nThe database name can be omitted from the first syntax, in which case the\nstatement applies to the default database.\n\nThe syntax that includes the UPGRADE DATA DIRECTORY NAME clause was added in\nMySQL 5.1.23. It updates the name of the directory associated with the\ndatabase to use the encoding implemented in MySQL 5.1 for mapping database\nnames to database directory names (see Identifier to File Name Mapping). This\nclause is for use under these conditions:\n\n* It is intended when upgrading MySQL to 5.1 or later from older versions.\n* It is intended to update a database directory name to the current encoding\nformat if the name contains special characters that need encoding.\n* The statement is used by mysqlcheck (as invoked by mysql_upgrade).\n\nFor example,if a database in MySQL 5.0 has a name of a-b-c, the name contains\ninstance of the `-\' character. In 5.0, the database directory is also named\na-b-c, which is not necessarily safe for all file systems. In MySQL 5.1 and\nup, the same database name is encoded as a@002db@002dc to produce a file\nsystem-neutral directory name.\n\nWhen a MySQL installation is upgraded to MySQL 5.1 or later from an older\nversion,the server displays a name such as a-b-c (which is in the old format)\nas #mysql50#a-b-c, and you must refer to the name using the #mysql50# prefix.\nUse UPGRADE DATA DIRECTORY NAME in this case to explicitly tell the server to\nre-encode the database directory name to the current encoding format:\n\nALTER DATABASE `#mysql50#a-b-c` UPGRADE DATA DIRECTORY NAME;\n\nAfter executing this statement, you can refer to the database as a-b-c without\nthe special #mysql50# prefix.\n\nCOMMENT\n-------\n\nMariaDB starting with 10.5.0\n----------------------------\nFrom MariaDB 10.5.0, it is possible to add a comment of a maximum of 1024\nbytes. If the comment length exceeds this length, a error/warning code 4144 is\nthrown. The database comment is also added to the db.opt file, as well as to\nthe information_schema.schemata table.\n\nExamples\n--------\n\nALTER DATABASE test CHARACTER SET=\'utf8\' COLLATE=\'utf8_bin\';\n\nFrom MariaDB 10.5.0:\n\nALTER DATABASE p COMMENT=\'Presentations\';\n\nURL: https://mariadb.com/kb/en/alter-database/','','https://mariadb.com/kb/en/alter-database/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (693,38,'ALTER EVENT','Modifies one or more characteristics of an existing event.\n\nSyntax\n------\n\nALTER\n [DEFINER = { user | CURRENT_USER }]\n EVENT event_name\n [ON SCHEDULE schedule]\n [ON COMPLETION [NOT] PRESERVE]\n [RENAME TO new_event_name]\n [ENABLE | DISABLE | DISABLE ON SLAVE]\n [COMMENT \'comment\']\n [DO sql_statement]\n\nDescription\n-----------\n\nThe ALTER EVENT statement is used to change one or more of the characteristics\nof an existing event without the need to drop and recreate it. The syntax for\neach of the DEFINER, ON SCHEDULE, ON COMPLETION, COMMENT, ENABLE / DISABLE,\nand DO clauses is exactly the same as when used with CREATE EVENT.\n\nThis statement requires the EVENT privilege. When a user executes a successful\nALTER EVENT statement, that user becomes the definer for the affected event.\n\n(In MySQL 5.1.11 and earlier, an event could be altered only by its definer,\nor by a user having the SUPER privilege.)\n\nALTER EVENT works only with an existing event:\n\nALTER EVENT no_such_event ON SCHEDULE EVERY \'2:3\' DAY_HOUR;\nERROR 1539 (HY000): Unknown event \'no_such_event\'\n\nExamples\n--------\n\nALTER EVENT myevent \n ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 2 HOUR\n DO\n UPDATE myschema.mytable SET mycol = mycol + 1;\n\nURL: https://mariadb.com/kb/en/alter-event/','','https://mariadb.com/kb/en/alter-event/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (694,38,'ALTER FUNCTION','Syntax\n------\n\nALTER FUNCTION func_name [characteristic ...]\n\ncharacteristic:\n { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }\n | SQL SECURITY { DEFINER | INVOKER }\n | COMMENT \'string\'\n\nDescription\n-----------\n\nThis statement can be used to change the characteristics of a stored function.\nMore than one change may be specified in an ALTER FUNCTION statement. However,\nyou cannot change the parameters or body of a stored function using this\nstatement; to make such changes, you must drop and re-create the function\nusing DROP FUNCTION and CREATE FUNCTION.\n\nYou must have the ALTER ROUTINE privilege for the function. (That privilege is\ngranted automatically to the function creator.) If binary logging is enabled,\nthe ALTER FUNCTION statement might also require the SUPER privilege, as\ndescribed in Binary Logging of Stored Routines.\n\nExample\n-------\n\nALTER FUNCTION hello SQL SECURITY INVOKER;\n\nURL: https://mariadb.com/kb/en/alter-function/','','https://mariadb.com/kb/en/alter-function/'); @@ -803,16 +803,16 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (697,38,'ALTER SERVER','Syntax\n------\n\nALTER SERVER server_name\n OPTIONS (option [, option] ...)\n\nDescription\n-----------\n\nAlters the server information for server_name, adjusting the specified options\nas per the CREATE SERVER command. The corresponding fields in the\nmysql.servers table are updated accordingly. This statement requires the SUPER\nprivilege or, from MariaDB 10.5.2, the FEDERATED ADMIN privilege.\n\nALTER SERVER is not written to the binary log, irrespective of the binary log\nformat being used. From MariaDB 10.1.13, Galera replicates the CREATE SERVER,\nALTER SERVER and DROP SERVER statements.\n\nExamples\n--------\n\nALTER SERVER s OPTIONS (USER \'sally\');\n\nURL: https://mariadb.com/kb/en/alter-server/','','https://mariadb.com/kb/en/alter-server/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (698,38,'ALTER TABLESPACE','The ALTER TABLESPACE statement is not supported by MariaDB. It was originally\ninherited from MySQL NDB Cluster. In MySQL 5.7 and later, the statement is\nalso supported for InnoDB. However, MariaDB has chosen not to include that\nspecific feature. See MDEV-19294 for more information.\n\nURL: https://mariadb.com/kb/en/alter-tablespace/','','https://mariadb.com/kb/en/alter-tablespace/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (699,38,'ALTER VIEW','Syntax\n------\n\nALTER\n [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]\n [DEFINER = { user | CURRENT_USER }]\n [SQL SECURITY { DEFINER | INVOKER }]\n VIEW view_name [(column_list)]\n AS select_statement\n [WITH [CASCADED | LOCAL] CHECK OPTION]\n\nDescription\n-----------\n\nThis statement changes the definition of a view, which must exist. The syntax\nis similar to that for CREATE VIEW and the effect is the same as for CREATE OR\nREPLACE VIEW if the view exists. This statement requires the CREATE VIEW and\nDROP privileges for the view, and some privilege for each column referred to\nin the SELECT statement. ALTER VIEW is allowed only to the definer or users\nwith the SUPER privilege.\n\nExample\n-------\n\nALTER VIEW v AS SELECT a, a*3 AS a2 FROM t;\n\nURL: https://mariadb.com/kb/en/alter-view/','','https://mariadb.com/kb/en/alter-view/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (700,38,'CREATE TABLE','Syntax\n------\n\nCREATE [OR REPLACE] [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name\n (create_definition,...) [table_options ]... [partition_options]\nCREATE [OR REPLACE] [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name\n [(create_definition,...)] [table_options ]... [partition_options]\n select_statement\nCREATE [OR REPLACE] [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name\n { LIKE old_table_name | (LIKE old_table_name) }\nselect_statement:\n [IGNORE | REPLACE] [AS] SELECT ... (Some legal select statement)\n\nDescription\n-----------\n\nUse the CREATE TABLE statement to create a table with the given name.\n\nIn its most basic form, the CREATE TABLE statement provides a table name\nfollowed by a list of columns, indexes, and constraints. By default, the table\nis created in the default database. Specify a database with db_name.tbl_name.\nIf you quote the table name, you must quote the database name and table name\nseparately as `db_name`.`tbl_name`. This is particularly useful for CREATE\nTABLE ... SELECT, because it allows to create a table into a database, which\ncontains data from other databases. See Identifier Qualifiers.\n\nIf a table with the same name exists, error 1050 results. Use IF NOT EXISTS to\nsuppress this error and issue a note instead. Use SHOW WARNINGS to see notes.\n\nThe CREATE TABLE statement automatically commits the current transaction,\nexcept when using the TEMPORARY keyword.\n\nFor valid identifiers to use as table names, see Identifier Names.\n\nNote: if the default_storage_engine is set to ColumnStore then it needs\nsetting on all UMs. Otherwise when the tables using the default engine are\nreplicated across UMs they will use the wrong engine. You should therefore not\nuse this option as a session variable with ColumnStore.\n\nMicrosecond precision can be between 0-6. If no precision is specified it is\nassumed to be 0, for backward compatibility reasons.\n\nPrivileges\n----------\n\nExecuting the CREATE TABLE statement requires the CREATE privilege for the\ntable or the database.\n\nCREATE OR REPLACE\n-----------------\n\nIf the OR REPLACE clause is used and the table already exists, then instead of\nreturning an error, the server will drop the existing table and replace it\nwith the newly defined table.\n\nThis syntax was originally added to make replication more robust if it has to\nrollback and repeat statements such as CREATE ... SELECT on replicas.\n\nCREATE OR REPLACE TABLE table_name (a int);\n\nis basically the same as:\n\nDROP TABLE IF EXISTS table_name;\nCREATE TABLE table_name (a int);\n\nwith the following exceptions:\n\n* If table_name was locked with LOCK TABLES it will continue to be locked\nafter the statement.\n* Temporary tables are only dropped if the TEMPORARY keyword was used. (With\nDROP TABLE, temporary tables are preferred to be dropped before normal\ntables).\n\nThings to be Aware of With CREATE OR REPLACE\n--------------------------------------------\n\n* The table is dropped first (if it existed), after that the CREATE is done.\nBecause of this, if the CREATE fails, then the table will not exist anymore\nafter the statement. If the table was used with LOCK TABLES it will be\nunlocked.\n* One can\'t use OR REPLACE together with IF EXISTS.\n* Slaves in replication will by default use CREATE OR REPLACE when replicating\nCREATE statements that don\'\'t use IF EXISTS. This can be changed by setting\nthe variable slave-ddl-exec-mode to STRICT.\n\nCREATE TABLE IF NOT EXISTS\n--------------------------\n\nIf the IF NOT EXISTS clause is used, then the table will only be created if a\ntable with the same name does not already exist. If the table already exists,\nthen a warning will be triggered by default.\n\nCREATE TEMPORARY TABLE\n----------------------\n\nUse the TEMPORARY keyword to create a temporary table that is only available\nto the current session. Temporary tables are dropped when the session ends.\nTemporary table names are specific to the session. They will not conflict with\nother temporary tables from other sessions even if they share the same name.\nThey will shadow names of non-temporary tables or views, if they are\nidentical. A temporary table can have the same name as a non-temporary table\nwhich is located in the same database. In that case, their name will reference\nthe temporary table when used in SQL statements. You must have the CREATE\nTEMPORARY TABLES privilege on the database to create temporary tables. If no\nstorage engine is specified, the default_tmp_storage_engine setting will\ndetermine the engine.\n\nROCKSDB temporary tables cannot be created by setting the\ndefault_tmp_storage_engine system variable, or using CREATE TEMPORARY TABLE\nLIKE. Before MariaDB 10.7, they could be specified, but would silently fail,\nand a MyISAM table would be created instead. From MariaDB 10.7 an error is\nreturned. Explicitly creating a temporary table with ENGINE=ROCKSDB has never\nbeen permitted.\n\nCREATE TABLE ... LIKE\n---------------------\n\nUse the LIKE clause instead of a full table definition to create a table with\nthe same definition as another table, including columns, indexes, and table\noptions. Foreign key definitions, as well as any DATA DIRECTORY or INDEX\nDIRECTORY table options specified on the original table, will not be created.\n\nCREATE TABLE ... SELECT\n-----------------------\n\nYou can create a table containing data from other tables using the CREATE ...\nSELECT statement. Columns will be created in the table for each field returned\nby the SELECT query.\n\nYou can also define some columns normally and add other columns from a SELECT.\nYou can also create columns in the normal way and assign them some values\nusing the query, this is done to force a certain type or other field\ncharacteristics. The columns that are not named in the query will be placed\nbefore the others. For example:\n\nCREATE TABLE test (a INT NOT NULL, b CHAR(10)) ENGINE=MyISAM\n SELECT 5 AS b, c, d FROM another_table;\n\nRemember that the query just returns data. If you want to use the same\nindexes, or the same columns attributes ([NOT] NULL, DEFAULT, AUTO_INCREMENT)\nin the new table, you need to specify them manually. Types and sizes are not\nautomatically preserved if no data returned by the SELECT requires the full\nsize, and VARCHAR could be converted into CHAR. The CAST() function can be\nused to forcee the new table to use certain types.\n\nAliases (AS) are taken into account, and they should always be used when you\nSELECT an expression (function, arithmetical operation, etc).\n\nIf an error occurs during the query, the table will not be created at all.\n\nIf the new table has a primary key or UNIQUE indexes, you can use the IGNORE\nor REPLACE keywords to handle duplicate key errors during the query. IGNORE\nmeans that the newer values must not be inserted an identical value exists in\nthe index. REPLACE means that older values must be overwritten.\n\nIf the columns in the new table are more than the rows returned by the query,\nthe columns populated by the query will be placed after other columns. Note\nthat if the strict SQL_MODE is on, and the columns that are not names in the\nquery do not have a DEFAULT value, an error will raise and no rows will be\ncopied.\n\nConcurrent inserts are not used during the execution of a CREATE ... SELECT.\n\nIf the table already exists, an error similar to the following will be\nreturned:\n\nERROR 1050 (42S01): Table \'t\' already exists\n\nIf the IF NOT EXISTS clause is used and the table exists, a note will be\nproduced instead of an error.\n\nTo insert rows from a query into an existing table, INSERT ... SELECT can be\nused.\n\nColumn Definitions\n------------------\n\ncreate_definition:\n { col_name column_definition | index_definition | period_definition | CHECK\n(expr) }\ncolumn_definition:\n data_type\n [NOT NULL | NULL] [DEFAULT default_value | (expression)]\n [ON UPDATE [NOW | CURRENT_TIMESTAMP] [(precision)]]\n [AUTO_INCREMENT] [ZEROFILL] [UNIQUE [KEY] | [PRIMARY] KEY]\n [INVISIBLE] [{WITH|WITHOUT} SYSTEM VERSIONING]\n [COMMENT \'string\'] [REF_SYSTEM_ID = value]\n [reference_definition]\n | data_type [GENERATED ALWAYS]\n AS { { ROW {START|END} } | { (expression) [VIRTUAL | PERSISTENT | STORED] } }\n [UNIQUE [KEY]] [COMMENT \'string\']\nconstraint_definition:\n CONSTRAINT [constraint_name] CHECK (expression)\nNote: Until MariaDB 10.4, MariaDB accepts the shortcut format with a\nREFERENCES clause only in ALTER TABLE and CREATE TABLE statements, but that\nsyntax does nothing. For example:\n\nCREATE TABLE b(for_key INT REFERENCES a(not_key));\n\nMariaDB simply parses it without returning any error or warning, for\ncompatibility with other DBMS\'s. Before MariaDB 10.2.1 this was also true for\nCHECK constraints. However, only the syntax described below creates foreign\nkeys.\n\nFrom MariaDB 10.5, MariaDB will attempt to apply the constraint. See Foreign\nKeys examples.\n\nEach definition either creates a column in the table or specifies and index or\nconstraint on one or more columns. See Indexes below for details on creating\nindexes.\n\nCreate a column by specifying a column name and a data type, optionally\nfollowed by column options. See Data Types for a full list of data types\nallowed in MariaDB.\n\nNULL and NOT NULL\n-----------------\n\nUse the NULL or NOT NULL options to specify that values in the column may or\nmay not be NULL, respectively. By default, values may be NULL. See also NULL\nValues in MariaDB.\n\nDEFAULT Column Option\n---------------------\n\nMariaDB starting with 10.2.1\n----------------------------\nThe DEFAULT clause was enhanced in MariaDB 10.2.1. Some enhancements include\n\n* BLOB and TEXT columns now support DEFAULT.\n* The DEFAULT clause can now be used with an expression or function.\n\nSpecify a default value using the DEFAULT clause. If you don\'t specify DEFAULT\nthen the following rules apply:\n\n* If the column is not defined with NOT NULL, AUTO_INCREMENT or TIMESTAMP, an\nexplicit DEFAULT NULL will be added.\nNote that in MySQL and in MariaDB before 10.1.6, you may get an explicit\nDEFAULT for primary key parts, if not specified with NOT NULL.\n\nThe default value will be used if you INSERT a row without specifying a value\nfor that column, or if you specify DEFAULT for that column. Before MariaDB\n10.2.1 you couldn\'t usually provide an expression or function to evaluate at\ninsertion time. You had to provide a constant default value instead. The one\nexception is that you may use CURRENT_TIMESTAMP as the default value for a\nTIMESTAMP column to use the current timestamp at insertion time.\n\nCURRENT_TIMESTAMP may also be used as the default value for a DATETIME\n\nFrom MariaDB 10.2.1 you can use most functions in DEFAULT. Expressions should\nhave parentheses around them. If you use a non deterministic function in\nDEFAULT then all inserts to the table will be replicated in row mode. You can\neven refer to earlier columns in the DEFAULT expression (excluding\nAUTO_INCREMENT columns):\n\nCREATE TABLE t1 (a int DEFAULT (1+1), b int DEFAULT (a+1));\nCREATE TABLE t2 (a bigint primary key DEFAULT UUID_SHORT());\n\nThe DEFAULT clause cannot contain any stored functions or subqueries, and a\ncolumn used in the clause must already have been defined earlier in the\nstatement.\n\nSince MariaDB 10.2.1, it is possible to assign BLOB or TEXT columns a DEFAULT\nvalue. In earlier versions, assigning a default to these columns was not\npossible.\n\nMariaDB starting with 10.3.3\n----------------------------\nStarting from 10.3.3 you can also use DEFAULT (NEXT VALUE FOR sequence)\n\nAUTO_INCREMENT Column Option\n----------------------------\n\nUse AUTO_INCREMENT to create a column whose value can can be set automatically\nfrom a simple counter. You can only use AUTO_INCREMENT on a column with an\ninteger type. The column must be a key, and there can only be one\nAUTO_INCREMENT column in a table. If you insert a row without specifying a\nvalue for that column (or if you specify 0, NULL, or DEFAULT as the value),\nthe actual value will be taken from the counter, with each insertion\nincrementing the counter by one. You can still insert a value explicitly. If\nyou insert a value that is greater than the current counter value, the counter\nis set based on the new value. An AUTO_INCREMENT column is implicitly NOT\nNULL. Use LAST_INSERT_ID to get the AUTO_INCREMENT value most recently used by\nan INSERT statement.\n\nZEROFILL Column Option\n----------------------\n\nIf the ZEROFILL column option is specified for a column using a numeric data\ntype, then the column will be set to UNSIGNED and the spaces used by default\nto pad the field are replaced with zeros. ZEROFILL is ignored in expressions\nor as part of a UNION. ZEROFILL is a non-standard MySQL and MariaDB\nenhancement.\n\nPRIMARY KEY Column Option\n-------------------------\n\nUse PRIMARY KEY to make a column a primary key. A primary key is a special\ntype of a unique key. There can be at most one primary key per table, and it\nis implicitly NOT NULL.\n\nSpecifying a column as a unique key creates a unique index on that column. See\nthe Index Definitions section below for more information.\n\nUNIQUE KEY Column Option\n------------------------\n\nUse UNIQUE KEY (or just UNIQUE) to specify that all values in the column must\nbe distinct from each other. Unless the column is NOT NULL, there may be\nmultiple rows with NULL in the column.\n\nSpecifying a column as a unique key creates a unique index on that column. See\nthe Index Definitions section below for more information.\n\nCOMMENT Column Option\n---------------------\n\nYou can provide a comment for each column using the COMMENT clause. The\nmaximum length is 1024 characters. Use the SHOW FULL COLUMNS statement to see\ncolumn comments.\n\nREF_SYSTEM_ID\n-------------\n\nREF_SYSTEM_ID can be used to specify Spatial Reference System IDs for spatial\ndata type columns.\n\nGenerated Columns\n-----------------\n\nA generated column is a column in a table that cannot explicitly be set to a\nspecific value in a DML query. Instead, its value is automatically generated\nbased on an expression. This expression might generate the value based on the\nvalues of other columns in the table, or it might generate the value by\ncalling built-in functions or user-defined functions (UDFs).\n\nThere are two types of generated columns:\n\n* PERSISTENT or STORED: This type\'s value is actually stored in the table.\n* VIRTUAL: This type\'s value is not stored at all. Instead, the value is','','https://mariadb.com/kb/en/create-table/'); -update help_topic set description = CONCAT(description, '\ngenerated dynamically when the table is queried. This type is the default.\n\nGenerated columns are also sometimes called computed columns or virtual\ncolumns.\n\nFor a complete description about generated columns and their limitations, see\nGenerated (Virtual and Persistent/Stored) Columns.\n\nCOMPRESSED\n----------\n\nMariaDB starting with 10.3.3\n----------------------------\nCertain columns may be compressed. See Storage-Engine Independent Column\nCompression.\n\nINVISIBLE\n---------\n\nMariaDB starting with 10.3.3\n----------------------------\nColumns may be made invisible, and hidden in certain contexts. See Invisible\nColumns.\n\nWITH SYSTEM VERSIONING Column Option\n------------------------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nColumns may be explicitly marked as included from system versioning. See\nSystem-versioned tables for details.\n\nWITHOUT SYSTEM VERSIONING Column Option\n---------------------------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nColumns may be explicitly marked as excluded from system versioning. See\nSystem-versioned tables for details.\n\nIndex Definitions\n-----------------\n\nindex_definition:\n {INDEX|KEY} [index_name] [index_type] (index_col_name,...) [index_option]\n...\n {{{|}}} {FULLTEXT|SPATIAL} [INDEX|KEY] [index_name] (index_col_name,...)\n[index_option] ...\n {{{|}}} [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (index_col_name,...)\n[index_option] ...\n {{{|}}} [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY] [index_name] [index_type]\n(index_col_name,...) [index_option] ...\n {{{|}}} [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,...)\nreference_definition\n\nindex_col_name:\n col_name [(length)] [ASC | DESC]\n\nindex_type:\n USING {BTREE | HASH | RTREE}\n\nindex_option:\n [ KEY_BLOCK_SIZE [=] value\n {{{|}}} index_type\n {{{|}}} WITH PARSER parser_name\n {{{|}}} COMMENT \'string\'\n {{{|}}} CLUSTERING={YES| NO} ]\n [ IGNORED | NOT IGNORED ]\n\nreference_definition:\n REFERENCES tbl_name (index_col_name,...)\n [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]\n [ON DELETE reference_option]\n [ON UPDATE reference_option]\n\nreference_option:\n RESTRICT | CASCADE | SET NULL | NO ACTION\n\nINDEX and KEY are synonyms.\n\nIndex names are optional, if not specified an automatic name will be assigned.\nIndex name are needed to drop indexes and appear in error messages when a\nconstraint is violated.\n\nIndex Categories\n----------------\n\nPlain Indexes\n-------------\n\nPlain indexes are regular indexes that are not unique, and are not acting as a\nprimary key or a foreign key. They are also not the \"specialized\" FULLTEXT or\nSPATIAL indexes.\n\nSee Getting Started with Indexes: Plain Indexes for more information.\n\nPRIMARY KEY\n-----------\n\nFor PRIMARY KEY indexes, you can specify a name for the index, but it is\nignored, and the name of the index is always PRIMARY. From MariaDB 10.3.18 and\nMariaDB 10.4.8, a warning is explicitly issued if a name is specified. Before\nthen, the name was silently ignored.\n\nSee Getting Started with Indexes: Primary Key for more information.\n\nUNIQUE\n------\n\nThe UNIQUE keyword means that the index will not accept duplicated values,\nexcept for NULLs. An error will raise if you try to insert duplicate values in\na UNIQUE index.\n\nFor UNIQUE indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nSee Getting Started with Indexes: Unique Index for more information.\n\nFOREIGN KEY\n-----------\n\nFor FOREIGN KEY indexes, a reference definition must be provided.\n\nFor FOREIGN KEY indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nFirst, you have to specify the name of the target (parent) table and a column\nor a column list which must be indexed and whose values must match to the\nforeign key\'s values. The MATCH clause is accepted to improve the\ncompatibility with other DBMS\'s, but has no meaning in MariaDB. The ON DELETE\nand ON UPDATE clauses specify what must be done when a DELETE (or a REPLACE)\nstatements attempts to delete a referenced row from the parent table, and when\nan UPDATE statement attempts to modify the referenced foreign key columns in a\nparent table row, respectively. The following options are allowed:\n\n* RESTRICT: The delete/update operation is not performed. The statement\nterminates with a 1451 error (SQLSTATE \'2300\').\n* NO ACTION: Synonym for RESTRICT.\n* CASCADE: The delete/update operation is performed in both tables.\n* SET NULL: The update or delete goes ahead in the parent table, and the\ncorresponding foreign key fields in the child table are set to NULL. (They\nmust not be defined as NOT NULL for this to succeed).\n* SET DEFAULT: This option is currently implemented only for the PBXT storage\nengine, which is disabled by default and no longer maintained. It sets the\nchild table\'s foreign key fields to their DEFAULT values when the referenced\nparent table key entries are updated or deleted.\n\nIf either clause is omitted, the default behavior for the omitted clause is\nRESTRICT.\n\nSee Foreign Keys for more information.\n\nFULLTEXT\n--------\n\nUse the FULLTEXT keyword to create full-text indexes.\n\nSee Full-Text Indexes for more information.\n\nSPATIAL\n-------\n\nUse the SPATIAL keyword to create geometric indexes.\n\nSee SPATIAL INDEX for more information.\n\nIndex Options\n-------------\n\nKEY_BLOCK_SIZE Index Option\n---------------------------\n\nThe KEY_BLOCK_SIZE index option is similar to the KEY_BLOCK_SIZE table option.\n\nWith the InnoDB storage engine, if you specify a non-zero value for the\nKEY_BLOCK_SIZE table option for the whole table, then the table will\nimplicitly be created with the ROW_FORMAT table option set to COMPRESSED.\nHowever, this does not happen if you just set the KEY_BLOCK_SIZE index option\nfor one or more indexes in the table. The InnoDB storage engine ignores the\nKEY_BLOCK_SIZE index option. However, the SHOW CREATE TABLE statement may\nstill report it for the index.\n\nFor information about the KEY_BLOCK_SIZE index option, see the KEY_BLOCK_SIZE\ntable option below.\n\nIndex Types\n-----------\n\nEach storage engine supports some or all index types. See Storage Engine Index\nTypes for details on permitted index types for each storage engine.\n\nDifferent index types are optimized for different kind of operations:\n\n* BTREE is the default type, and normally is the best choice. It is supported\nby all storage engines. It can be used to compare a column\'s value with a\nvalue using the =, >, >=, <, <=, BETWEEN, and LIKE operators. BTREE can also\nbe used to find NULL values. Searches against an index prefix are possible.\n* HASH is only supported by the MEMORY storage engine. HASH indexes can only\nbe used for =, <=, and >= comparisons. It can not be used for the ORDER BY\nclause. Searches against an index prefix are not possible.\n* RTREE is the default for SPATIAL indexes, but if the storage engine does not\nsupport it BTREE can be used.\n\nIndex columns names are listed between parenthesis. After each column, a\nprefix length can be specified. If no length is specified, the whole column\nwill be indexed. ASC and DESC can be specified for compatibility with are\nDBMS\'s, but have no meaning in MariaDB.\n\nWITH PARSER Index Option\n------------------------\n\nThe WITH PARSER index option only applies to FULLTEXT indexes and contains the\nfulltext parser name. The fulltext parser must be an installed plugin.\n\nCOMMENT Index Option\n--------------------\n\nA comment of up to 1024 characters is permitted with the COMMENT index option.\n\nThe COMMENT index option allows you to specify a comment with user-readable\ntext describing what the index is for. This information is not used by the\nserver itself.\n\nCLUSTERING Index Option\n-----------------------\n\nThe CLUSTERING index option is only valid for tables using the TokuDB storage\nengine.\n\nIGNORED / NOT IGNORED\n---------------------\n\nMariaDB starting with 10.6.0\n----------------------------\nFrom MariaDB 10.6.0, indexes can be specified to be ignored by the optimizer.\nSee Ignored Indexes.\n\nPeriods\n-------\n\nMariaDB starting with 10.3.4\n----------------------------\n\nperiod_definition:\n PERIOD FOR SYSTEM_TIME (start_column_name, end_column_name)\nMariaDB supports a subset of the standard syntax for periods. At the moment\nit\'s only used for creating System-versioned tables. Both columns must be\ncreated, must be either of a TIMESTAMP(6) or BIGINT UNSIGNED type, and be\ngenerated as ROW START and ROW END accordingly. See System-versioned tables\nfor details.\n\nThe table must also have the WITH SYSTEM VERSIONING clause.\n\nConstraint Expressions\n----------------------\n\nMariaDB starting with 10.2.1\n----------------------------\nMariaDB 10.2.1 introduced new ways to define a constraint.\n\nNote: Before MariaDB 10.2.1, constraint expressions were accepted in the\nsyntax but ignored.\n\nMariaDB 10.2.1 introduced two ways to define a constraint:\n\n* CHECK(expression) given as part of a column definition.\n* CONSTRAINT [constraint_name] CHECK (expression)\n\nBefore a row is inserted or updated, all constraints are evaluated in the\norder they are defined. If any constraints fails, then the row will not be\nupdated. One can use most deterministic functions in a constraint, including\nUDFs.\n\ncreate table t1 (a int check(a>0) ,b int check (b> 0), constraint abc check\n(a>b));\n\nIf you use the second format and you don\'t give a name to the constraint, then\nthe constraint will get a auto generated name. This is done so that you can\nlater delete the constraint with ALTER TABLE DROP constraint_name.\n\nOne can disable all constraint expression checks by setting the variable\ncheck_constraint_checks to OFF. This is useful for example when loading a\ntable that violates some constraints that you want to later find and fix in\nSQL.\n\nSee CONSTRAINT for more information.\n\nTable Options\n-------------\n\nFor each individual table you create (or alter), you can set some table\noptions. The general syntax for setting options is:\n\n = , [ = ...]\n\nThe equal sign is optional.\n\nSome options are supported by the server and can be used for all tables, no\nmatter what storage engine they use; other options can be specified for all\nstorage engines, but have a meaning only for some engines. Also, engines can\nextend CREATE TABLE with new options.\n\nIf the IGNORE_BAD_TABLE_OPTIONS SQL_MODE is enabled, wrong table options\ngenerate a warning; otherwise, they generate an error.\n\ntable_option: \n [STORAGE] ENGINE [=] engine_name\n | AUTO_INCREMENT [=] value\n | AVG_ROW_LENGTH [=] value\n | [DEFAULT] CHARACTER SET [=] charset_name\n | CHECKSUM [=] {0 | 1}\n | [DEFAULT] COLLATE [=] collation_name\n | COMMENT [=] \'string\'\n | CONNECTION [=] \'connect_string\'\n | DATA DIRECTORY [=] \'absolute path to directory\'\n | DELAY_KEY_WRITE [=] {0 | 1}\n | ENCRYPTED [=] {YES | NO}\n | ENCRYPTION_KEY_ID [=] value\n | IETF_QUOTES [=] {YES | NO}\n | INDEX DIRECTORY [=] \'absolute path to directory\'\n | INSERT_METHOD [=] { NO | FIRST | LAST }\n | KEY_BLOCK_SIZE [=] value\n | MAX_ROWS [=] value\n | MIN_ROWS [=] value\n | PACK_KEYS [=] {0 | 1 | DEFAULT}\n | PAGE_CHECKSUM [=] {0 | 1}\n | PAGE_COMPRESSED [=] {0 | 1}\n | PAGE_COMPRESSION_LEVEL [=] {0 .. 9}\n | PASSWORD [=] \'string\'\n | ROW_FORMAT [=] {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT|PAGE}\n | SEQUENCE [=] {0|1}\n | STATS_AUTO_RECALC [=] {DEFAULT|0|1}\n | STATS_PERSISTENT [=] {DEFAULT|0|1}\n | STATS_SAMPLE_PAGES [=] {DEFAULT|value}\n | TABLESPACE tablespace_name\n | TRANSACTIONAL [=] {0 | 1}\n | UNION [=] (tbl_name[,tbl_name]...)\n | WITH SYSTEM VERSIONING\n\n[STORAGE] ENGINE\n----------------\n\n[STORAGE] ENGINE specifies a storage engine for the table. If this option is\nnot used, the default storage engine is used instead. That is, the\ndefault_storage_engine session option value if it is set, or the value\nspecified for the --default-storage-engine mysqld startup option, or the\ndefault storage engine, InnoDB. If the specified storage engine is not\ninstalled and active, the default value will be used, unless the\nNO_ENGINE_SUBSTITUTION SQL MODE is set (default). This is only true for CREATE\nTABLE, not for ALTER TABLE. For a list of storage engines that are present in\nyour server, issue a SHOW ENGINES.\n\nAUTO_INCREMENT\n--------------\n\nAUTO_INCREMENT specifies the initial value for the AUTO_INCREMENT primary key.\nThis works for MyISAM, Aria, InnoDB, MEMORY, and ARCHIVE tables. You can\nchange this option with ALTER TABLE, but in that case the new value must be\nhigher than the highest value which is present in the AUTO_INCREMENT column.\nIf the storage engine does not support this option, you can insert (and then\ndelete) a row having the wanted value - 1 in the AUTO_INCREMENT column.\n\nAVG_ROW_LENGTH\n--------------\n\nAVG_ROW_LENGTH is the average rows size. It only applies to tables using\nMyISAM and Aria storage engines that have the ROW_FORMAT table option set to\nFIXED format.\n\nMyISAM uses MAX_ROWS and AVG_ROW_LENGTH to decide the maximum size of a table\n(default: 256TB, or the maximum file size allowed by the system).\n\n[DEFAULT] CHARACTER SET/CHARSET\n-------------------------------\n\n[DEFAULT] CHARACTER SET (or [DEFAULT] CHARSET) is used to set a default\ncharacter set for the table. This is the character set used for all columns\nwhere an explicit character set is not specified. If this option is omitted or\nDEFAULT is specified, database\'s default character set will be used. See\nSetting Character Sets and Collations for details on setting the character\nsets.\n\nCHECKSUM/TABLE_CHECKSUM\n-----------------------\n\nCHECKSUM (or TABLE_CHECKSUM) can be set to 1 to maintain a live checksum for\nall table\'s rows. This makes write operations slower, but CHECKSUM TABLE will\nbe very fast. This option is only supported for MyISAM and Aria tables.\n\n[DEFAULT] COLLATE\n-----------------\n\n[DEFAULT] COLLATE is used to set a default collation for the table. This is\nthe collation used for all columns where an explicit character set is not\nspecified. If this option is omitted or DEFAULT is specified, database\'s\ndefault option will be used. See Setting Character Sets and Collations for\ndetails on setting the collations\n\nCOMMENT\n-------\n') WHERE help_topic_id = 700; -update help_topic set description = CONCAT(description, '\nCOMMENT is a comment for the table. The maximum length is 2048 characters.\nAlso used to define table parameters when creating a Spider table.\n\nCONNECTION\n----------\n\nCONNECTION is used to specify a server name or a connection string for a\nSpider, CONNECT, Federated or FederatedX table.\n\nDATA DIRECTORY/INDEX DIRECTORY\n------------------------------\n\nDATA DIRECTORY and INDEX DIRECTORY are supported for MyISAM and Aria, and DATA\nDIRECTORY is also supported by InnoDB if the innodb_file_per_table server\nsystem variable is enabled, but only in CREATE TABLE, not in ALTER TABLE. So,\ncarefully choose a path for InnoDB tables at creation time, because it cannot\nbe changed without dropping and re-creating the table. These options specify\nthe paths for data files and index files, respectively. If these options are\nomitted, the database\'s directory will be used to store data files and index\nfiles. Note that these table options do not work for partitioned tables (use\nthe partition options instead), or if the server has been invoked with the\n--skip-symbolic-links startup option. To avoid the overwriting of old files\nwith the same name that could be present in the directories, you can use the\n--keep_files_on_create option (an error will be issued if files already\nexist). These options are ignored if the NO_DIR_IN_CREATE SQL_MODE is enabled\n(useful for replication slaves). Also note that symbolic links cannot be used\nfor InnoDB tables.\n\nDATA DIRECTORY works by creating symlinks from where the table would normally\nhave been (inside the datadir) to where the option specifies. For security\nreasons, to avoid bypassing the privilege system, the server does not permit\nsymlinks inside the datadir. Therefore, DATA DIRECTORY cannot be used to\nspecify a location inside the datadir. An attempt to do so will result in an\nerror 1210 (HY000) Incorrect arguments to DATA DIRECTORY.\n\nDELAY_KEY_WRITE\n---------------\n\nDELAY_KEY_WRITE is supported by MyISAM and Aria, and can be set to 1 to speed\nup write operations. In that case, when data are modified, the indexes are not\nupdated until the table is closed. Writing the changes to the index file\naltogether can be much faster. However, note that this option is applied only\nif the delay_key_write server variable is set to \'ON\'. If it is \'OFF\' the\ndelayed index writes are always disabled, and if it is \'ALL\' the delayed index\nwrites are always used, disregarding the value of DELAY_KEY_WRITE.\n\nENCRYPTED\n---------\n\nThe ENCRYPTED table option can be used to manually set the encryption status\nof an InnoDB table. See InnoDB Encryption for more information.\n\nAria does not support the ENCRYPTED table option. See MDEV-18049.\n\nSee Data-at-Rest Encryption for more information.\n\nENCRYPTION_KEY_ID\n-----------------\n\nThe ENCRYPTION_KEY_ID table option can be used to manually set the encryption\nkey of an InnoDB table. See InnoDB Encryption for more information.\n\nAria does not support the ENCRYPTION_KEY_ID table option. See MDEV-18049.\n\nSee Data-at-Rest Encryption for more information.\n\nIETF_QUOTES\n-----------\n\nFor the CSV storage engine, the IETF_QUOTES option, when set to YES, enables\nIETF-compatible parsing of embedded quote and comma characters. Enabling this\noption for a table improves compatibility with other tools that use CSV, but\nis not compatible with MySQL CSV tables, or MariaDB CSV tables created without\nthis option. Disabled by default.\n\nINSERT_METHOD\n-------------\n\nINSERT_METHOD is only used with MERGE tables. This option determines in which\nunderlying table the new rows should be inserted. If you set it to \'NO\' (which\nis the default) no new rows can be added to the table (but you will still be\nable to perform INSERTs directly against the underlying tables). FIRST means\nthat the rows are inserted into the first table, and LAST means that thet are\ninserted into the last table.\n\nKEY_BLOCK_SIZE\n--------------\n\nKEY_BLOCK_SIZE is used to determine the size of key blocks, in bytes or\nkilobytes. However, this value is just a hint, and the storage engine could\nmodify or ignore it. If KEY_BLOCK_SIZE is set to 0, the storage engine\'s\ndefault value will be used.\n\nWith the InnoDB storage engine, if you specify a non-zero value for the\nKEY_BLOCK_SIZE table option for the whole table, then the table will\nimplicitly be created with the ROW_FORMAT table option set to COMPRESSED.\n\nMIN_ROWS/MAX_ROWS\n-----------------\n\nMIN_ROWS and MAX_ROWS let the storage engine know how many rows you are\nplanning to store as a minimum and as a maximum. These values will not be used\nas real limits, but they help the storage engine to optimize the table.\nMIN_ROWS is only used by MEMORY storage engine to decide the minimum memory\nthat is always allocated. MAX_ROWS is used to decide the minimum size for\nindexes.\n\nPACK_KEYS\n---------\n\nPACK_KEYS can be used to determine whether the indexes will be compressed. Set\nit to 1 to compress all keys. With a value of 0, compression will not be used.\nWith the DEFAULT value, only long strings will be compressed. Uncompressed\nkeys are faster.\n\nPAGE_CHECKSUM\n-------------\n\nPAGE_CHECKSUM is only applicable to Aria tables, and determines whether\nindexes and data should use page checksums for extra safety.\n\nPAGE_COMPRESSED\n---------------\n\nPAGE_COMPRESSED is used to enable InnoDB page compression for InnoDB tables.\n\nPAGE_COMPRESSION_LEVEL\n----------------------\n\nPAGE_COMPRESSION_LEVEL is used to set the compression level for InnoDB page\ncompression for InnoDB tables. The table must also have the PAGE_COMPRESSED\ntable option set to 1.\n\nValid values for PAGE_COMPRESSION_LEVEL are 1 (the best speed) through 9 (the\nbest compression), .\n\nPASSWORD\n--------\n\nPASSWORD is unused.\n\nRAID_TYPE\n---------\n\nRAID_TYPE is an obsolete option, as the raid support has been disabled since\nMySQL 5.0.\n\nROW_FORMAT\n----------\n\nThe ROW_FORMAT table option specifies the row format for the data file.\nPossible values are engine-dependent.\n\nSupported MyISAM Row Formats\n----------------------------\n\nFor MyISAM, the supported row formats are:\n\n* FIXED\n* DYNAMIC\n* COMPRESSED\n\nThe COMPRESSED row format can only be set by the myisampack command line tool.\n\nSee MyISAM Storage Formats for more information.\n\nSupported Aria Row Formats\n--------------------------\n\nFor Aria, the supported row formats are:\n\n* PAGE\n* FIXED\n* DYNAMIC.\n\nSee Aria Storage Formats for more information.\n\nSupported InnoDB Row Formats\n----------------------------\n\nFor InnoDB, the supported row formats are:\n\n* COMPACT\n* REDUNDANT\n* COMPRESSED\n* DYNAMIC.\n\nIf the ROW_FORMAT table option is set to FIXED for an InnoDB table, then the\nserver will either return an error or a warning depending on the value of the\ninnodb_strict_mode system variable. If the innodb_strict_mode system variable\nis set to OFF, then a warning is issued, and MariaDB will create the table\nusing the default row format for the specific MariaDB server version. If the\ninnodb_strict_mode system variable is set to ON, then an error will be raised.\n\nSee InnoDB Storage Formats for more information.\n\nOther Storage Engines and ROW_FORMAT\n------------------------------------\n\nOther storage engines do not support the ROW_FORMAT table option.\n\nSEQUENCE\n--------\n\nMariaDB starting with 10.3\n--------------------------\nIf the table is a sequence, then it will have the SEQUENCE set to 1.\n\nSTATS_AUTO_RECALC\n-----------------\n\nSTATS_AUTO_RECALC indicates whether to automatically recalculate persistent\nstatistics (see STATS_PERSISTENT, below) for an InnoDB table. If set to 1,\nstatistics will be recalculated when more than 10% of the data has changed.\nWhen set to 0, stats will be recalculated only when an ANALYZE TABLE is run.\nIf set to DEFAULT, or left out, the value set by the innodb_stats_auto_recalc\nsystem variable applies. See InnoDB Persistent Statistics.\n\nSTATS_PERSISTENT\n----------------\n\nSTATS_PERSISTENT indicates whether the InnoDB statistics created by ANALYZE\nTABLE will remain on disk or not. It can be set to 1 (on disk), 0 (not on\ndisk, the pre-MariaDB 10 behavior), or DEFAULT (the same as leaving out the\noption), in which case the value set by the innodb_stats_persistent system\nvariable will apply. Persistent statistics stored on disk allow the statistics\nto survive server restarts, and provide better query plan stability. See\nInnoDB Persistent Statistics.\n\nSTATS_SAMPLE_PAGES\n------------------\n\nSTATS_SAMPLE_PAGES indicates how many pages are used to sample index\nstatistics. If 0 or DEFAULT, the default value, the innodb_stats_sample_pages\nvalue is used. See InnoDB Persistent Statistics.\n\nTRANSACTIONAL\n-------------\n\nTRANSACTIONAL is only applicable for Aria tables. In future Aria tables\ncreated with this option will be fully transactional, but currently this\nprovides a form of crash protection. See Aria Storage Engine for more details.\n\nUNION\n-----\n\nUNION must be specified when you create a MERGE table. This option contains a\ncomma-separated list of MyISAM tables which are accessed by the new table. The\nlist is enclosed between parenthesis. Example: UNION = (t1,t2)\n\nWITH SYSTEM VERSIONING\n----------------------\n\nWITH SYSTEM VERSIONING is used for creating System-versioned tables.\n\nPartitions\n----------\n\npartition_options:\n PARTITION BY\n { [LINEAR] HASH(expr)\n | [LINEAR] KEY(column_list)\n | RANGE(expr)\n | LIST(expr)\n | SYSTEM_TIME [INTERVAL time_quantity time_unit] [LIMIT num] }\n [PARTITIONS num]\n [SUBPARTITION BY\n { [LINEAR] HASH(expr)\n | [LINEAR] KEY(column_list) }\n [SUBPARTITIONS num]\n ]\n [(partition_definition [, partition_definition] ...)]\npartition_definition:\n PARTITION partition_name\n [VALUES {LESS THAN {(expr) | MAXVALUE} | IN (value_list)}]\n [[STORAGE] ENGINE [=] engine_name]\n [COMMENT [=] \'comment_text\' ]\n [DATA DIRECTORY [=] \'data_dir\']\n [INDEX DIRECTORY [=] \'index_dir\']\n [MAX_ROWS [=] max_number_of_rows]\n [MIN_ROWS [=] min_number_of_rows]\n [TABLESPACE [=] tablespace_name]\n [NODEGROUP [=] node_group_id]\n [(subpartition_definition [, subpartition_definition] ...)]\nsubpartition_definition:\n SUBPARTITION logical_name\n [[STORAGE] ENGINE [=] engine_name]\n [COMMENT [=] \'comment_text\' ]\n [DATA DIRECTORY [=] \'data_dir\']\n [INDEX DIRECTORY [=] \'index_dir\']\n [MAX_ROWS [=] max_number_of_rows]\n [MIN_ROWS [=] min_number_of_rows]\n [TABLESPACE [=] tablespace_name]\n [NODEGROUP [=] node_group_id]\nIf the PARTITION BY clause is used, the table will be partitioned. A partition\nmethod must be explicitly indicated for partitions and subpartitions.\nPartition methods are:\n\n* [LINEAR] HASH creates a hash key which will be used to read and write rows.\nThe partition function can be any valid SQL expression which returns an\nINTEGER number. Thus, it is possible to use the HASH method on an integer\ncolumn, or on functions which accept integer columns as an argument. However,\nVALUES LESS THAN and VALUES IN clauses can not be used with HASH. An example:\n\nCREATE TABLE t1 (a INT, b CHAR(5), c DATETIME)\n PARTITION BY HASH ( YEAR(c) );\n\n[LINEAR] HASH can be used for subpartitions, too.\n\n* [LINEAR] KEY is similar to HASH, but the index has an even distribution of\ndata. Also, the expression can only be a column or a list of columns. VALUES\nLESS THAN and VALUES IN clauses can not be used with KEY.\n* RANGE partitions the rows using on a range of values, using the VALUES LESS\nTHAN operator. VALUES IN is not allowed with RANGE. The partition function can\nbe any valid SQL expression which returns a single value.\n* LIST assigns partitions based on a table\'s column with a restricted set of\npossible values. It is similar to RANGE, but VALUES IN must be used for at\nleast 1 columns, and VALUES LESS THAN is disallowed.\n* SYSTEM_TIME partitioning is used for System-versioned tables to store\nhistorical data separately from current data.\n\nOnly HASH and KEY can be used for subpartitions, and they can be [LINEAR].\n\nIt is possible to define up to 1024 partitions and subpartitions.\n\nThe number of defined partitions can be optionally specified as PARTITION\ncount. This can be done to avoid specifying all partitions individually. But\nyou can also declare each individual partition and, additionally, specify a\nPARTITIONS count clause; in the case, the number of PARTITIONs must equal\ncount.\n\nAlso see Partitioning Types Overview.\n\nSequences\n---------\n\nMariaDB starting with 10.3\n--------------------------\nCREATE TABLE can also be used to create a SEQUENCE. See CREATE SEQUENCE and\nSequence Overview.\n\nAtomic DDL\n----------\n\nMariaDB starting with 10.6.1\n----------------------------\nMariaDB 10.6.1 supports Atomic DDL. CREATE TABLE is atomic, except for CREATE\nOR REPLACE, which is only crash safe.\n\nExamples\n--------\n\ncreate table if not exists test (\na bigint auto_increment primary key,\nname varchar(128) charset utf8,\nkey name (name(32))\n) engine=InnoDB default charset latin1;\n\nThis example shows a couple of things:\n\n* Usage of IF NOT EXISTS; If the table already existed, it will not be\ncreated. There will not be any error for the client, just a warning.\n* How to create a PRIMARY KEY that is automatically generated.\n* How to specify a table-specific character set and another for a column.\n* How to create an index (name) that is only partly indexed (to save space).\n\nThe following clauses will work from MariaDB 10.2.1 only.\n\nCREATE TABLE t1(\n a int DEFAULT (1+1),\n b int DEFAULT (a+1),\n expires DATETIME DEFAULT(NOW() + INTERVAL 1 YEAR),\n x BLOB DEFAULT USER()\n);\n\nURL: https://mariadb.com/kb/en/create-table/') WHERE help_topic_id = 700; +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (700,38,'CREATE TABLE','Syntax\n------\n\nCREATE [OR REPLACE] [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name\n (create_definition,...) [table_options ]... [partition_options]\nCREATE [OR REPLACE] [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name\n [(create_definition,...)] [table_options ]... [partition_options]\n select_statement\nCREATE [OR REPLACE] [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name\n { LIKE old_table_name | (LIKE old_table_name) }\nselect_statement:\n [IGNORE | REPLACE] [AS] SELECT ... (Some legal select statement)\n\nDescription\n-----------\n\nUse the CREATE TABLE statement to create a table with the given name.\n\nIn its most basic form, the CREATE TABLE statement provides a table name\nfollowed by a list of columns, indexes, and constraints. By default, the table\nis created in the default database. Specify a database with db_name.tbl_name.\nIf you quote the table name, you must quote the database name and table name\nseparately as `db_name`.`tbl_name`. This is particularly useful for CREATE\nTABLE ... SELECT, because it allows to create a table into a database, which\ncontains data from other databases. See Identifier Qualifiers.\n\nIf a table with the same name exists, error 1050 results. Use IF NOT EXISTS to\nsuppress this error and issue a note instead. Use SHOW WARNINGS to see notes.\n\nThe CREATE TABLE statement automatically commits the current transaction,\nexcept when using the TEMPORARY keyword.\n\nFor valid identifiers to use as table names, see Identifier Names.\n\nNote: if the default_storage_engine is set to ColumnStore then it needs\nsetting on all UMs. Otherwise when the tables using the default engine are\nreplicated across UMs they will use the wrong engine. You should therefore not\nuse this option as a session variable with ColumnStore.\n\nMicrosecond precision can be between 0-6. If no precision is specified it is\nassumed to be 0, for backward compatibility reasons.\n\nPrivileges\n----------\n\nExecuting the CREATE TABLE statement requires the CREATE privilege for the\ntable or the database.\n\nCREATE OR REPLACE\n-----------------\n\nIf the OR REPLACE clause is used and the table already exists, then instead of\nreturning an error, the server will drop the existing table and replace it\nwith the newly defined table.\n\nThis syntax was originally added to make replication more robust if it has to\nrollback and repeat statements such as CREATE ... SELECT on replicas.\n\nCREATE OR REPLACE TABLE table_name (a int);\n\nis basically the same as:\n\nDROP TABLE IF EXISTS table_name;\nCREATE TABLE table_name (a int);\n\nwith the following exceptions:\n\n* If table_name was locked with LOCK TABLES it will continue to be locked\nafter the statement.\n* Temporary tables are only dropped if the TEMPORARY keyword was used. (With\nDROP TABLE, temporary tables are preferred to be dropped before normal\ntables).\n\nThings to be Aware of With CREATE OR REPLACE\n--------------------------------------------\n\n* The table is dropped first (if it existed), after that the CREATE is done.\nBecause of this, if the CREATE fails, then the table will not exist anymore\nafter the statement. If the table was used with LOCK TABLES it will be\nunlocked.\n* One can\'t use OR REPLACE together with IF EXISTS.\n* Slaves in replication will by default use CREATE OR REPLACE when replicating\nCREATE statements that don\'\'t use IF EXISTS. This can be changed by setting\nthe variable slave-ddl-exec-mode to STRICT.\n\nCREATE TABLE IF NOT EXISTS\n--------------------------\n\nIf the IF NOT EXISTS clause is used, then the table will only be created if a\ntable with the same name does not already exist. If the table already exists,\nthen a warning will be triggered by default.\n\nCREATE TEMPORARY TABLE\n----------------------\n\nUse the TEMPORARY keyword to create a temporary table that is only available\nto the current session. Temporary tables are dropped when the session ends.\nTemporary table names are specific to the session. They will not conflict with\nother temporary tables from other sessions even if they share the same name.\nThey will shadow names of non-temporary tables or views, if they are\nidentical. A temporary table can have the same name as a non-temporary table\nwhich is located in the same database. In that case, their name will reference\nthe temporary table when used in SQL statements. You must have the CREATE\nTEMPORARY TABLES privilege on the database to create temporary tables. If no\nstorage engine is specified, the default_tmp_storage_engine setting will\ndetermine the engine.\n\nROCKSDB temporary tables cannot be created by setting the\ndefault_tmp_storage_engine system variable, or using CREATE TEMPORARY TABLE\nLIKE. Before MariaDB 10.7, they could be specified, but would silently fail,\nand a MyISAM table would be created instead. From MariaDB 10.7 an error is\nreturned. Explicitly creating a temporary table with ENGINE=ROCKSDB has never\nbeen permitted.\n\nCREATE TABLE ... LIKE\n---------------------\n\nUse the LIKE clause instead of a full table definition to create a table with\nthe same definition as another table, including columns, indexes, and table\noptions. Foreign key definitions, as well as any DATA DIRECTORY or INDEX\nDIRECTORY table options specified on the original table, will not be created.\n\nCREATE TABLE ... SELECT\n-----------------------\n\nYou can create a table containing data from other tables using the CREATE ...\nSELECT statement. Columns will be created in the table for each field returned\nby the SELECT query.\n\nYou can also define some columns normally and add other columns from a SELECT.\nYou can also create columns in the normal way and assign them some values\nusing the query, this is done to force a certain type or other field\ncharacteristics. The columns that are not named in the query will be placed\nbefore the others. For example:\n\nCREATE TABLE test (a INT NOT NULL, b CHAR(10)) ENGINE=MyISAM\n SELECT 5 AS b, c, d FROM another_table;\n\nRemember that the query just returns data. If you want to use the same\nindexes, or the same columns attributes ([NOT] NULL, DEFAULT, AUTO_INCREMENT)\nin the new table, you need to specify them manually. Types and sizes are not\nautomatically preserved if no data returned by the SELECT requires the full\nsize, and VARCHAR could be converted into CHAR. The CAST() function can be\nused to forcee the new table to use certain types.\n\nAliases (AS) are taken into account, and they should always be used when you\nSELECT an expression (function, arithmetical operation, etc).\n\nIf an error occurs during the query, the table will not be created at all.\n\nIf the new table has a primary key or UNIQUE indexes, you can use the IGNORE\nor REPLACE keywords to handle duplicate key errors during the query. IGNORE\nmeans that the newer values must not be inserted an identical value exists in\nthe index. REPLACE means that older values must be overwritten.\n\nIf the columns in the new table are more than the rows returned by the query,\nthe columns populated by the query will be placed after other columns. Note\nthat if the strict SQL_MODE is on, and the columns that are not names in the\nquery do not have a DEFAULT value, an error will raise and no rows will be\ncopied.\n\nConcurrent inserts are not used during the execution of a CREATE ... SELECT.\n\nIf the table already exists, an error similar to the following will be\nreturned:\n\nERROR 1050 (42S01): Table \'t\' already exists\n\nIf the IF NOT EXISTS clause is used and the table exists, a note will be\nproduced instead of an error.\n\nTo insert rows from a query into an existing table, INSERT ... SELECT can be\nused.\n\nColumn Definitions\n------------------\n\ncreate_definition:\n { col_name column_definition | index_definition | period_definition | CHECK\n(expr) }\ncolumn_definition:\n data_type\n [NOT NULL | NULL] [DEFAULT default_value | (expression)]\n [ON UPDATE [NOW | CURRENT_TIMESTAMP] [(precision)]]\n [AUTO_INCREMENT] [ZEROFILL] [UNIQUE [KEY] | [PRIMARY] KEY]\n [INVISIBLE] [{WITH|WITHOUT} SYSTEM VERSIONING]\n [COMMENT \'string\'] [REF_SYSTEM_ID = value]\n [reference_definition]\n | data_type [GENERATED ALWAYS]\n AS { { ROW {START|END} } | { (expression) [VIRTUAL | PERSISTENT | STORED] } }\n [UNIQUE [KEY]] [COMMENT \'string\']\nconstraint_definition:\n CONSTRAINT [constraint_name] CHECK (expression)\nNote: Until MariaDB 10.4, MariaDB accepts the shortcut format with a\nREFERENCES clause only in ALTER TABLE and CREATE TABLE statements, but that\nsyntax does nothing. For example:\n\nCREATE TABLE b(for_key INT REFERENCES a(not_key));\n\nMariaDB simply parses it without returning any error or warning, for\ncompatibility with other DBMS\'s. Before MariaDB 10.2.1 this was also true for\nCHECK constraints. However, only the syntax described below creates foreign\nkeys.\n\nFrom MariaDB 10.5, MariaDB will attempt to apply the constraint. See Foreign\nKeys examples.\n\nEach definition either creates a column in the table or specifies and index or\nconstraint on one or more columns. See Indexes below for details on creating\nindexes.\n\nCreate a column by specifying a column name and a data type, optionally\nfollowed by column options. See Data Types for a full list of data types\nallowed in MariaDB.\n\nNULL and NOT NULL\n-----------------\n\nUse the NULL or NOT NULL options to specify that values in the column may or\nmay not be NULL, respectively. By default, values may be NULL. See also NULL\nValues in MariaDB.\n\nDEFAULT Column Option\n---------------------\n\nMariaDB starting with 10.2.1\n----------------------------\nThe DEFAULT clause was enhanced in MariaDB 10.2.1. Some enhancements include\n\n* BLOB and TEXT columns now support DEFAULT.\n* The DEFAULT clause can now be used with an expression or function.\n\nSpecify a default value using the DEFAULT clause. If you don\'t specify DEFAULT\nthen the following rules apply:\n\n* If the column is not defined with NOT NULL, AUTO_INCREMENT or TIMESTAMP, an\nexplicit DEFAULT NULL will be added.\nNote that in MySQL and in MariaDB before 10.1.6, you may get an explicit\nDEFAULT for primary key parts, if not specified with NOT NULL.\n\nThe default value will be used if you INSERT a row without specifying a value\nfor that column, or if you specify DEFAULT for that column. Before MariaDB\n10.2.1 you couldn\'t usually provide an expression or function to evaluate at\ninsertion time. You had to provide a constant default value instead. The one\nexception is that you may use CURRENT_TIMESTAMP as the default value for a\nTIMESTAMP column to use the current timestamp at insertion time.\n\nCURRENT_TIMESTAMP may also be used as the default value for a DATETIME\n\nFrom MariaDB 10.2.1 you can use most functions in DEFAULT. Expressions should\nhave parentheses around them. If you use a non deterministic function in\nDEFAULT then all inserts to the table will be replicated in row mode. You can\neven refer to earlier columns in the DEFAULT expression (excluding\nAUTO_INCREMENT columns):\n\nCREATE TABLE t1 (a int DEFAULT (1+1), b int DEFAULT (a+1));\nCREATE TABLE t2 (a bigint primary key DEFAULT UUID_SHORT());\n\nThe DEFAULT clause cannot contain any stored functions or subqueries, and a\ncolumn used in the clause must already have been defined earlier in the\nstatement.\n\nSince MariaDB 10.2.1, it is possible to assign BLOB or TEXT columns a DEFAULT\nvalue. In earlier versions, assigning a default to these columns was not\npossible.\n\nMariaDB starting with 10.3.3\n----------------------------\nStarting from 10.3.3 you can also use DEFAULT (NEXT VALUE FOR sequence)\n\nAUTO_INCREMENT Column Option\n----------------------------\n\nUse AUTO_INCREMENT to create a column whose value can can be set automatically\nfrom a simple counter. You can only use AUTO_INCREMENT on a column with an\ninteger type. The column must be a key, and there can only be one\nAUTO_INCREMENT column in a table. If you insert a row without specifying a\nvalue for that column (or if you specify 0, NULL, or DEFAULT as the value),\nthe actual value will be taken from the counter, with each insertion\nincrementing the counter by one. You can still insert a value explicitly. If\nyou insert a value that is greater than the current counter value, the counter\nis set based on the new value. An AUTO_INCREMENT column is implicitly NOT\nNULL. Use LAST_INSERT_ID to get the AUTO_INCREMENT value most recently used by\nan INSERT statement.\n\nZEROFILL Column Option\n----------------------\n\nIf the ZEROFILL column option is specified for a column using a numeric data\ntype, then the column will be set to UNSIGNED and the spaces used by default\nto pad the field are replaced with zeros. ZEROFILL is ignored in expressions\nor as part of a UNION. ZEROFILL is a non-standard MySQL and MariaDB\nenhancement.\n\nPRIMARY KEY Column Option\n-------------------------\n\nUse PRIMARY KEY to make a column a primary key. A primary key is a special\ntype of a unique key. There can be at most one primary key per table, and it\nis implicitly NOT NULL.\n\nSpecifying a column as a unique key creates a unique index on that column. See\nthe Index Definitions section below for more information.\n\nUNIQUE KEY Column Option\n------------------------\n\nUse UNIQUE KEY (or just UNIQUE) to specify that all values in the column must\nbe distinct from each other. Unless the column is NOT NULL, there may be\nmultiple rows with NULL in the column.\n\nSpecifying a column as a unique key creates a unique index on that column. See\nthe Index Definitions section below for more information.\n\nCOMMENT Column Option\n---------------------\n\nYou can provide a comment for each column using the COMMENT clause. The\nmaximum length is 1024 characters. Use the SHOW FULL COLUMNS statement to see\ncolumn comments.\n\nREF_SYSTEM_ID\n-------------\n\nREF_SYSTEM_ID can be used to specify Spatial Reference System IDs for spatial\ndata type columns. For example:\n\nCREATE TABLE t1(g GEOMETRY(9,4) REF_SYSTEM_ID=101);\n\nGenerated Columns\n-----------------\n\nA generated column is a column in a table that cannot explicitly be set to a\nspecific value in a DML query. Instead, its value is automatically generated\nbased on an expression. This expression might generate the value based on the\nvalues of other columns in the table, or it might generate the value by\ncalling built-in functions or user-defined functions (UDFs).\n\nThere are two types of generated columns:\n\n* PERSISTENT or STORED: This type\'s value is actually stored in the table.','','https://mariadb.com/kb/en/create-table/'); +update help_topic set description = CONCAT(description, '\n* VIRTUAL: This type\'s value is not stored at all. Instead, the value is\ngenerated dynamically when the table is queried. This type is the default.\n\nGenerated columns are also sometimes called computed columns or virtual\ncolumns.\n\nFor a complete description about generated columns and their limitations, see\nGenerated (Virtual and Persistent/Stored) Columns.\n\nCOMPRESSED\n----------\n\nMariaDB starting with 10.3.3\n----------------------------\nCertain columns may be compressed. See Storage-Engine Independent Column\nCompression.\n\nINVISIBLE\n---------\n\nMariaDB starting with 10.3.3\n----------------------------\nColumns may be made invisible, and hidden in certain contexts. See Invisible\nColumns.\n\nWITH SYSTEM VERSIONING Column Option\n------------------------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nColumns may be explicitly marked as included from system versioning. See\nSystem-versioned tables for details.\n\nWITHOUT SYSTEM VERSIONING Column Option\n---------------------------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nColumns may be explicitly marked as excluded from system versioning. See\nSystem-versioned tables for details.\n\nIndex Definitions\n-----------------\n\nindex_definition:\n {INDEX|KEY} [index_name] [index_type] (index_col_name,...) [index_option]\n...\n {{{|}}} {FULLTEXT|SPATIAL} [INDEX|KEY] [index_name] (index_col_name,...)\n[index_option] ...\n {{{|}}} [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (index_col_name,...)\n[index_option] ...\n {{{|}}} [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY] [index_name] [index_type]\n(index_col_name,...) [index_option] ...\n {{{|}}} [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,...)\nreference_definition\n\nindex_col_name:\n col_name [(length)] [ASC | DESC]\n\nindex_type:\n USING {BTREE | HASH | RTREE}\n\nindex_option:\n [ KEY_BLOCK_SIZE [=] value\n {{{|}}} index_type\n {{{|}}} WITH PARSER parser_name\n {{{|}}} COMMENT \'string\'\n {{{|}}} CLUSTERING={YES| NO} ]\n [ IGNORED | NOT IGNORED ]\n\nreference_definition:\n REFERENCES tbl_name (index_col_name,...)\n [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]\n [ON DELETE reference_option]\n [ON UPDATE reference_option]\n\nreference_option:\n RESTRICT | CASCADE | SET NULL | NO ACTION\n\nINDEX and KEY are synonyms.\n\nIndex names are optional, if not specified an automatic name will be assigned.\nIndex name are needed to drop indexes and appear in error messages when a\nconstraint is violated.\n\nIndex Categories\n----------------\n\nPlain Indexes\n-------------\n\nPlain indexes are regular indexes that are not unique, and are not acting as a\nprimary key or a foreign key. They are also not the \"specialized\" FULLTEXT or\nSPATIAL indexes.\n\nSee Getting Started with Indexes: Plain Indexes for more information.\n\nPRIMARY KEY\n-----------\n\nFor PRIMARY KEY indexes, you can specify a name for the index, but it is\nignored, and the name of the index is always PRIMARY. From MariaDB 10.3.18 and\nMariaDB 10.4.8, a warning is explicitly issued if a name is specified. Before\nthen, the name was silently ignored.\n\nSee Getting Started with Indexes: Primary Key for more information.\n\nUNIQUE\n------\n\nThe UNIQUE keyword means that the index will not accept duplicated values,\nexcept for NULLs. An error will raise if you try to insert duplicate values in\na UNIQUE index.\n\nFor UNIQUE indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nSee Getting Started with Indexes: Unique Index for more information.\n\nFOREIGN KEY\n-----------\n\nFor FOREIGN KEY indexes, a reference definition must be provided.\n\nFor FOREIGN KEY indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nFirst, you have to specify the name of the target (parent) table and a column\nor a column list which must be indexed and whose values must match to the\nforeign key\'s values. The MATCH clause is accepted to improve the\ncompatibility with other DBMS\'s, but has no meaning in MariaDB. The ON DELETE\nand ON UPDATE clauses specify what must be done when a DELETE (or a REPLACE)\nstatements attempts to delete a referenced row from the parent table, and when\nan UPDATE statement attempts to modify the referenced foreign key columns in a\nparent table row, respectively. The following options are allowed:\n\n* RESTRICT: The delete/update operation is not performed. The statement\nterminates with a 1451 error (SQLSTATE \'2300\').\n* NO ACTION: Synonym for RESTRICT.\n* CASCADE: The delete/update operation is performed in both tables.\n* SET NULL: The update or delete goes ahead in the parent table, and the\ncorresponding foreign key fields in the child table are set to NULL. (They\nmust not be defined as NOT NULL for this to succeed).\n* SET DEFAULT: This option is currently implemented only for the PBXT storage\nengine, which is disabled by default and no longer maintained. It sets the\nchild table\'s foreign key fields to their DEFAULT values when the referenced\nparent table key entries are updated or deleted.\n\nIf either clause is omitted, the default behavior for the omitted clause is\nRESTRICT.\n\nSee Foreign Keys for more information.\n\nFULLTEXT\n--------\n\nUse the FULLTEXT keyword to create full-text indexes.\n\nSee Full-Text Indexes for more information.\n\nSPATIAL\n-------\n\nUse the SPATIAL keyword to create geometric indexes.\n\nSee SPATIAL INDEX for more information.\n\nIndex Options\n-------------\n\nKEY_BLOCK_SIZE Index Option\n---------------------------\n\nThe KEY_BLOCK_SIZE index option is similar to the KEY_BLOCK_SIZE table option.\n\nWith the InnoDB storage engine, if you specify a non-zero value for the\nKEY_BLOCK_SIZE table option for the whole table, then the table will\nimplicitly be created with the ROW_FORMAT table option set to COMPRESSED.\nHowever, this does not happen if you just set the KEY_BLOCK_SIZE index option\nfor one or more indexes in the table. The InnoDB storage engine ignores the\nKEY_BLOCK_SIZE index option. However, the SHOW CREATE TABLE statement may\nstill report it for the index.\n\nFor information about the KEY_BLOCK_SIZE index option, see the KEY_BLOCK_SIZE\ntable option below.\n\nIndex Types\n-----------\n\nEach storage engine supports some or all index types. See Storage Engine Index\nTypes for details on permitted index types for each storage engine.\n\nDifferent index types are optimized for different kind of operations:\n\n* BTREE is the default type, and normally is the best choice. It is supported\nby all storage engines. It can be used to compare a column\'s value with a\nvalue using the =, >, >=, <, <=, BETWEEN, and LIKE operators. BTREE can also\nbe used to find NULL values. Searches against an index prefix are possible.\n* HASH is only supported by the MEMORY storage engine. HASH indexes can only\nbe used for =, <=, and >= comparisons. It can not be used for the ORDER BY\nclause. Searches against an index prefix are not possible.\n* RTREE is the default for SPATIAL indexes, but if the storage engine does not\nsupport it BTREE can be used.\n\nIndex columns names are listed between parenthesis. After each column, a\nprefix length can be specified. If no length is specified, the whole column\nwill be indexed. ASC and DESC can be specified for compatibility with are\nDBMS\'s, but have no meaning in MariaDB.\n\nWITH PARSER Index Option\n------------------------\n\nThe WITH PARSER index option only applies to FULLTEXT indexes and contains the\nfulltext parser name. The fulltext parser must be an installed plugin.\n\nCOMMENT Index Option\n--------------------\n\nA comment of up to 1024 characters is permitted with the COMMENT index option.\n\nThe COMMENT index option allows you to specify a comment with user-readable\ntext describing what the index is for. This information is not used by the\nserver itself.\n\nCLUSTERING Index Option\n-----------------------\n\nThe CLUSTERING index option is only valid for tables using the TokuDB storage\nengine.\n\nIGNORED / NOT IGNORED\n---------------------\n\nMariaDB starting with 10.6.0\n----------------------------\nFrom MariaDB 10.6.0, indexes can be specified to be ignored by the optimizer.\nSee Ignored Indexes.\n\nPeriods\n-------\n\nMariaDB starting with 10.3.4\n----------------------------\n\nperiod_definition:\n PERIOD FOR SYSTEM_TIME (start_column_name, end_column_name)\nMariaDB supports a subset of the standard syntax for periods. At the moment\nit\'s only used for creating System-versioned tables. Both columns must be\ncreated, must be either of a TIMESTAMP(6) or BIGINT UNSIGNED type, and be\ngenerated as ROW START and ROW END accordingly. See System-versioned tables\nfor details.\n\nThe table must also have the WITH SYSTEM VERSIONING clause.\n\nConstraint Expressions\n----------------------\n\nMariaDB starting with 10.2.1\n----------------------------\nMariaDB 10.2.1 introduced new ways to define a constraint.\n\nNote: Before MariaDB 10.2.1, constraint expressions were accepted in the\nsyntax but ignored.\n\nMariaDB 10.2.1 introduced two ways to define a constraint:\n\n* CHECK(expression) given as part of a column definition.\n* CONSTRAINT [constraint_name] CHECK (expression)\n\nBefore a row is inserted or updated, all constraints are evaluated in the\norder they are defined. If any constraints fails, then the row will not be\nupdated. One can use most deterministic functions in a constraint, including\nUDFs.\n\ncreate table t1 (a int check(a>0) ,b int check (b> 0), constraint abc check\n(a>b));\n\nIf you use the second format and you don\'t give a name to the constraint, then\nthe constraint will get a auto generated name. This is done so that you can\nlater delete the constraint with ALTER TABLE DROP constraint_name.\n\nOne can disable all constraint expression checks by setting the variable\ncheck_constraint_checks to OFF. This is useful for example when loading a\ntable that violates some constraints that you want to later find and fix in\nSQL.\n\nSee CONSTRAINT for more information.\n\nTable Options\n-------------\n\nFor each individual table you create (or alter), you can set some table\noptions. The general syntax for setting options is:\n\n = , [ = ...]\n\nThe equal sign is optional.\n\nSome options are supported by the server and can be used for all tables, no\nmatter what storage engine they use; other options can be specified for all\nstorage engines, but have a meaning only for some engines. Also, engines can\nextend CREATE TABLE with new options.\n\nIf the IGNORE_BAD_TABLE_OPTIONS SQL_MODE is enabled, wrong table options\ngenerate a warning; otherwise, they generate an error.\n\ntable_option: \n [STORAGE] ENGINE [=] engine_name\n | AUTO_INCREMENT [=] value\n | AVG_ROW_LENGTH [=] value\n | [DEFAULT] CHARACTER SET [=] charset_name\n | CHECKSUM [=] {0 | 1}\n | [DEFAULT] COLLATE [=] collation_name\n | COMMENT [=] \'string\'\n | CONNECTION [=] \'connect_string\'\n | DATA DIRECTORY [=] \'absolute path to directory\'\n | DELAY_KEY_WRITE [=] {0 | 1}\n | ENCRYPTED [=] {YES | NO}\n | ENCRYPTION_KEY_ID [=] value\n | IETF_QUOTES [=] {YES | NO}\n | INDEX DIRECTORY [=] \'absolute path to directory\'\n | INSERT_METHOD [=] { NO | FIRST | LAST }\n | KEY_BLOCK_SIZE [=] value\n | MAX_ROWS [=] value\n | MIN_ROWS [=] value\n | PACK_KEYS [=] {0 | 1 | DEFAULT}\n | PAGE_CHECKSUM [=] {0 | 1}\n | PAGE_COMPRESSED [=] {0 | 1}\n | PAGE_COMPRESSION_LEVEL [=] {0 .. 9}\n | PASSWORD [=] \'string\'\n | ROW_FORMAT [=] {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT|PAGE}\n | SEQUENCE [=] {0|1}\n | STATS_AUTO_RECALC [=] {DEFAULT|0|1}\n | STATS_PERSISTENT [=] {DEFAULT|0|1}\n | STATS_SAMPLE_PAGES [=] {DEFAULT|value}\n | TABLESPACE tablespace_name\n | TRANSACTIONAL [=] {0 | 1}\n | UNION [=] (tbl_name[,tbl_name]...)\n | WITH SYSTEM VERSIONING\n\n[STORAGE] ENGINE\n----------------\n\n[STORAGE] ENGINE specifies a storage engine for the table. If this option is\nnot used, the default storage engine is used instead. That is, the\ndefault_storage_engine session option value if it is set, or the value\nspecified for the --default-storage-engine mysqld startup option, or the\ndefault storage engine, InnoDB. If the specified storage engine is not\ninstalled and active, the default value will be used, unless the\nNO_ENGINE_SUBSTITUTION SQL MODE is set (default). This is only true for CREATE\nTABLE, not for ALTER TABLE. For a list of storage engines that are present in\nyour server, issue a SHOW ENGINES.\n\nAUTO_INCREMENT\n--------------\n\nAUTO_INCREMENT specifies the initial value for the AUTO_INCREMENT primary key.\nThis works for MyISAM, Aria, InnoDB, MEMORY, and ARCHIVE tables. You can\nchange this option with ALTER TABLE, but in that case the new value must be\nhigher than the highest value which is present in the AUTO_INCREMENT column.\nIf the storage engine does not support this option, you can insert (and then\ndelete) a row having the wanted value - 1 in the AUTO_INCREMENT column.\n\nAVG_ROW_LENGTH\n--------------\n\nAVG_ROW_LENGTH is the average rows size. It only applies to tables using\nMyISAM and Aria storage engines that have the ROW_FORMAT table option set to\nFIXED format.\n\nMyISAM uses MAX_ROWS and AVG_ROW_LENGTH to decide the maximum size of a table\n(default: 256TB, or the maximum file size allowed by the system).\n\n[DEFAULT] CHARACTER SET/CHARSET\n-------------------------------\n\n[DEFAULT] CHARACTER SET (or [DEFAULT] CHARSET) is used to set a default\ncharacter set for the table. This is the character set used for all columns\nwhere an explicit character set is not specified. If this option is omitted or\nDEFAULT is specified, database\'s default character set will be used. See\nSetting Character Sets and Collations for details on setting the character\nsets.\n\nCHECKSUM/TABLE_CHECKSUM\n-----------------------\n\nCHECKSUM (or TABLE_CHECKSUM) can be set to 1 to maintain a live checksum for\nall table\'s rows. This makes write operations slower, but CHECKSUM TABLE will\nbe very fast. This option is only supported for MyISAM and Aria tables.\n\n[DEFAULT] COLLATE\n-----------------\n\n[DEFAULT] COLLATE is used to set a default collation for the table. This is\nthe collation used for all columns where an explicit character set is not\nspecified. If this option is omitted or DEFAULT is specified, database\'s') WHERE help_topic_id = 700; +update help_topic set description = CONCAT(description, '\ndefault option will be used. See Setting Character Sets and Collations for\ndetails on setting the collations\n\nCOMMENT\n-------\n\nCOMMENT is a comment for the table. The maximum length is 2048 characters.\nAlso used to define table parameters when creating a Spider table.\n\nCONNECTION\n----------\n\nCONNECTION is used to specify a server name or a connection string for a\nSpider, CONNECT, Federated or FederatedX table.\n\nDATA DIRECTORY/INDEX DIRECTORY\n------------------------------\n\nDATA DIRECTORY and INDEX DIRECTORY are supported for MyISAM and Aria, and DATA\nDIRECTORY is also supported by InnoDB if the innodb_file_per_table server\nsystem variable is enabled, but only in CREATE TABLE, not in ALTER TABLE. So,\ncarefully choose a path for InnoDB tables at creation time, because it cannot\nbe changed without dropping and re-creating the table. These options specify\nthe paths for data files and index files, respectively. If these options are\nomitted, the database\'s directory will be used to store data files and index\nfiles. Note that these table options do not work for partitioned tables (use\nthe partition options instead), or if the server has been invoked with the\n--skip-symbolic-links startup option. To avoid the overwriting of old files\nwith the same name that could be present in the directories, you can use the\n--keep_files_on_create option (an error will be issued if files already\nexist). These options are ignored if the NO_DIR_IN_CREATE SQL_MODE is enabled\n(useful for replication slaves). Also note that symbolic links cannot be used\nfor InnoDB tables.\n\nDATA DIRECTORY works by creating symlinks from where the table would normally\nhave been (inside the datadir) to where the option specifies. For security\nreasons, to avoid bypassing the privilege system, the server does not permit\nsymlinks inside the datadir. Therefore, DATA DIRECTORY cannot be used to\nspecify a location inside the datadir. An attempt to do so will result in an\nerror 1210 (HY000) Incorrect arguments to DATA DIRECTORY.\n\nDELAY_KEY_WRITE\n---------------\n\nDELAY_KEY_WRITE is supported by MyISAM and Aria, and can be set to 1 to speed\nup write operations. In that case, when data are modified, the indexes are not\nupdated until the table is closed. Writing the changes to the index file\naltogether can be much faster. However, note that this option is applied only\nif the delay_key_write server variable is set to \'ON\'. If it is \'OFF\' the\ndelayed index writes are always disabled, and if it is \'ALL\' the delayed index\nwrites are always used, disregarding the value of DELAY_KEY_WRITE.\n\nENCRYPTED\n---------\n\nThe ENCRYPTED table option can be used to manually set the encryption status\nof an InnoDB table. See InnoDB Encryption for more information.\n\nAria does not support the ENCRYPTED table option. See MDEV-18049.\n\nSee Data-at-Rest Encryption for more information.\n\nENCRYPTION_KEY_ID\n-----------------\n\nThe ENCRYPTION_KEY_ID table option can be used to manually set the encryption\nkey of an InnoDB table. See InnoDB Encryption for more information.\n\nAria does not support the ENCRYPTION_KEY_ID table option. See MDEV-18049.\n\nSee Data-at-Rest Encryption for more information.\n\nIETF_QUOTES\n-----------\n\nFor the CSV storage engine, the IETF_QUOTES option, when set to YES, enables\nIETF-compatible parsing of embedded quote and comma characters. Enabling this\noption for a table improves compatibility with other tools that use CSV, but\nis not compatible with MySQL CSV tables, or MariaDB CSV tables created without\nthis option. Disabled by default.\n\nINSERT_METHOD\n-------------\n\nINSERT_METHOD is only used with MERGE tables. This option determines in which\nunderlying table the new rows should be inserted. If you set it to \'NO\' (which\nis the default) no new rows can be added to the table (but you will still be\nable to perform INSERTs directly against the underlying tables). FIRST means\nthat the rows are inserted into the first table, and LAST means that thet are\ninserted into the last table.\n\nKEY_BLOCK_SIZE\n--------------\n\nKEY_BLOCK_SIZE is used to determine the size of key blocks, in bytes or\nkilobytes. However, this value is just a hint, and the storage engine could\nmodify or ignore it. If KEY_BLOCK_SIZE is set to 0, the storage engine\'s\ndefault value will be used.\n\nWith the InnoDB storage engine, if you specify a non-zero value for the\nKEY_BLOCK_SIZE table option for the whole table, then the table will\nimplicitly be created with the ROW_FORMAT table option set to COMPRESSED.\n\nMIN_ROWS/MAX_ROWS\n-----------------\n\nMIN_ROWS and MAX_ROWS let the storage engine know how many rows you are\nplanning to store as a minimum and as a maximum. These values will not be used\nas real limits, but they help the storage engine to optimize the table.\nMIN_ROWS is only used by MEMORY storage engine to decide the minimum memory\nthat is always allocated. MAX_ROWS is used to decide the minimum size for\nindexes.\n\nPACK_KEYS\n---------\n\nPACK_KEYS can be used to determine whether the indexes will be compressed. Set\nit to 1 to compress all keys. With a value of 0, compression will not be used.\nWith the DEFAULT value, only long strings will be compressed. Uncompressed\nkeys are faster.\n\nPAGE_CHECKSUM\n-------------\n\nPAGE_CHECKSUM is only applicable to Aria tables, and determines whether\nindexes and data should use page checksums for extra safety.\n\nPAGE_COMPRESSED\n---------------\n\nPAGE_COMPRESSED is used to enable InnoDB page compression for InnoDB tables.\n\nPAGE_COMPRESSION_LEVEL\n----------------------\n\nPAGE_COMPRESSION_LEVEL is used to set the compression level for InnoDB page\ncompression for InnoDB tables. The table must also have the PAGE_COMPRESSED\ntable option set to 1.\n\nValid values for PAGE_COMPRESSION_LEVEL are 1 (the best speed) through 9 (the\nbest compression), .\n\nPASSWORD\n--------\n\nPASSWORD is unused.\n\nRAID_TYPE\n---------\n\nRAID_TYPE is an obsolete option, as the raid support has been disabled since\nMySQL 5.0.\n\nROW_FORMAT\n----------\n\nThe ROW_FORMAT table option specifies the row format for the data file.\nPossible values are engine-dependent.\n\nSupported MyISAM Row Formats\n----------------------------\n\nFor MyISAM, the supported row formats are:\n\n* FIXED\n* DYNAMIC\n* COMPRESSED\n\nThe COMPRESSED row format can only be set by the myisampack command line tool.\n\nSee MyISAM Storage Formats for more information.\n\nSupported Aria Row Formats\n--------------------------\n\nFor Aria, the supported row formats are:\n\n* PAGE\n* FIXED\n* DYNAMIC.\n\nSee Aria Storage Formats for more information.\n\nSupported InnoDB Row Formats\n----------------------------\n\nFor InnoDB, the supported row formats are:\n\n* COMPACT\n* REDUNDANT\n* COMPRESSED\n* DYNAMIC.\n\nIf the ROW_FORMAT table option is set to FIXED for an InnoDB table, then the\nserver will either return an error or a warning depending on the value of the\ninnodb_strict_mode system variable. If the innodb_strict_mode system variable\nis set to OFF, then a warning is issued, and MariaDB will create the table\nusing the default row format for the specific MariaDB server version. If the\ninnodb_strict_mode system variable is set to ON, then an error will be raised.\n\nSee InnoDB Storage Formats for more information.\n\nOther Storage Engines and ROW_FORMAT\n------------------------------------\n\nOther storage engines do not support the ROW_FORMAT table option.\n\nSEQUENCE\n--------\n\nMariaDB starting with 10.3\n--------------------------\nIf the table is a sequence, then it will have the SEQUENCE set to 1.\n\nSTATS_AUTO_RECALC\n-----------------\n\nSTATS_AUTO_RECALC indicates whether to automatically recalculate persistent\nstatistics (see STATS_PERSISTENT, below) for an InnoDB table. If set to 1,\nstatistics will be recalculated when more than 10% of the data has changed.\nWhen set to 0, stats will be recalculated only when an ANALYZE TABLE is run.\nIf set to DEFAULT, or left out, the value set by the innodb_stats_auto_recalc\nsystem variable applies. See InnoDB Persistent Statistics.\n\nSTATS_PERSISTENT\n----------------\n\nSTATS_PERSISTENT indicates whether the InnoDB statistics created by ANALYZE\nTABLE will remain on disk or not. It can be set to 1 (on disk), 0 (not on\ndisk, the pre-MariaDB 10 behavior), or DEFAULT (the same as leaving out the\noption), in which case the value set by the innodb_stats_persistent system\nvariable will apply. Persistent statistics stored on disk allow the statistics\nto survive server restarts, and provide better query plan stability. See\nInnoDB Persistent Statistics.\n\nSTATS_SAMPLE_PAGES\n------------------\n\nSTATS_SAMPLE_PAGES indicates how many pages are used to sample index\nstatistics. If 0 or DEFAULT, the default value, the innodb_stats_sample_pages\nvalue is used. See InnoDB Persistent Statistics.\n\nTRANSACTIONAL\n-------------\n\nTRANSACTIONAL is only applicable for Aria tables. In future Aria tables\ncreated with this option will be fully transactional, but currently this\nprovides a form of crash protection. See Aria Storage Engine for more details.\n\nUNION\n-----\n\nUNION must be specified when you create a MERGE table. This option contains a\ncomma-separated list of MyISAM tables which are accessed by the new table. The\nlist is enclosed between parenthesis. Example: UNION = (t1,t2)\n\nWITH SYSTEM VERSIONING\n----------------------\n\nWITH SYSTEM VERSIONING is used for creating System-versioned tables.\n\nPartitions\n----------\n\npartition_options:\n PARTITION BY\n { [LINEAR] HASH(expr)\n | [LINEAR] KEY(column_list)\n | RANGE(expr)\n | LIST(expr)\n | SYSTEM_TIME [INTERVAL time_quantity time_unit] [LIMIT num] }\n [PARTITIONS num]\n [SUBPARTITION BY\n { [LINEAR] HASH(expr)\n | [LINEAR] KEY(column_list) }\n [SUBPARTITIONS num]\n ]\n [(partition_definition [, partition_definition] ...)]\npartition_definition:\n PARTITION partition_name\n [VALUES {LESS THAN {(expr) | MAXVALUE} | IN (value_list)}]\n [[STORAGE] ENGINE [=] engine_name]\n [COMMENT [=] \'comment_text\' ]\n [DATA DIRECTORY [=] \'data_dir\']\n [INDEX DIRECTORY [=] \'index_dir\']\n [MAX_ROWS [=] max_number_of_rows]\n [MIN_ROWS [=] min_number_of_rows]\n [TABLESPACE [=] tablespace_name]\n [NODEGROUP [=] node_group_id]\n [(subpartition_definition [, subpartition_definition] ...)]\nsubpartition_definition:\n SUBPARTITION logical_name\n [[STORAGE] ENGINE [=] engine_name]\n [COMMENT [=] \'comment_text\' ]\n [DATA DIRECTORY [=] \'data_dir\']\n [INDEX DIRECTORY [=] \'index_dir\']\n [MAX_ROWS [=] max_number_of_rows]\n [MIN_ROWS [=] min_number_of_rows]\n [TABLESPACE [=] tablespace_name]\n [NODEGROUP [=] node_group_id]\nIf the PARTITION BY clause is used, the table will be partitioned. A partition\nmethod must be explicitly indicated for partitions and subpartitions.\nPartition methods are:\n\n* [LINEAR] HASH creates a hash key which will be used to read and write rows.\nThe partition function can be any valid SQL expression which returns an\nINTEGER number. Thus, it is possible to use the HASH method on an integer\ncolumn, or on functions which accept integer columns as an argument. However,\nVALUES LESS THAN and VALUES IN clauses can not be used with HASH. An example:\n\nCREATE TABLE t1 (a INT, b CHAR(5), c DATETIME)\n PARTITION BY HASH ( YEAR(c) );\n\n[LINEAR] HASH can be used for subpartitions, too.\n\n* [LINEAR] KEY is similar to HASH, but the index has an even distribution of\ndata. Also, the expression can only be a column or a list of columns. VALUES\nLESS THAN and VALUES IN clauses can not be used with KEY.\n* RANGE partitions the rows using on a range of values, using the VALUES LESS\nTHAN operator. VALUES IN is not allowed with RANGE. The partition function can\nbe any valid SQL expression which returns a single value.\n* LIST assigns partitions based on a table\'s column with a restricted set of\npossible values. It is similar to RANGE, but VALUES IN must be used for at\nleast 1 columns, and VALUES LESS THAN is disallowed.\n* SYSTEM_TIME partitioning is used for System-versioned tables to store\nhistorical data separately from current data.\n\nOnly HASH and KEY can be used for subpartitions, and they can be [LINEAR].\n\nIt is possible to define up to 1024 partitions and subpartitions.\n\nThe number of defined partitions can be optionally specified as PARTITION\ncount. This can be done to avoid specifying all partitions individually. But\nyou can also declare each individual partition and, additionally, specify a\nPARTITIONS count clause; in the case, the number of PARTITIONs must equal\ncount.\n\nAlso see Partitioning Types Overview.\n\nSequences\n---------\n\nMariaDB starting with 10.3\n--------------------------\nCREATE TABLE can also be used to create a SEQUENCE. See CREATE SEQUENCE and\nSequence Overview.\n\nAtomic DDL\n----------\n\nMariaDB starting with 10.6.1\n----------------------------\nMariaDB 10.6.1 supports Atomic DDL. CREATE TABLE is atomic, except for CREATE\nOR REPLACE, which is only crash safe.\n\nExamples\n--------\n\ncreate table if not exists test (\na bigint auto_increment primary key,\nname varchar(128) charset utf8,\nkey name (name(32))\n) engine=InnoDB default charset latin1;\n\nThis example shows a couple of things:\n\n* Usage of IF NOT EXISTS; If the table already existed, it will not be\ncreated. There will not be any error for the client, just a warning.\n* How to create a PRIMARY KEY that is automatically generated.\n* How to specify a table-specific character set and another for a column.\n* How to create an index (name) that is only partly indexed (to save space).\n\nThe following clauses will work from MariaDB 10.2.1 only.\n\nCREATE TABLE t1(\n a int DEFAULT (1+1),\n b int DEFAULT (a+1),\n expires DATETIME DEFAULT(NOW() + INTERVAL 1 YEAR),\n x BLOB DEFAULT USER()\n);\n\nURL: https://mariadb.com/kb/en/create-table/') WHERE help_topic_id = 700; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (701,38,'DROP TABLE','Syntax\n------\n\nDROP [TEMPORARY] TABLE [IF EXISTS] [/*COMMENT TO SAVE*/]\n tbl_name [, tbl_name] ...\n [WAIT n|NOWAIT]\n [RESTRICT | CASCADE]\n\nDescription\n-----------\n\nDROP TABLE removes one or more tables. You must have the DROP privilege for\neach table. All table data and the table definition are removed, as well as\ntriggers associated to the table, so be careful with this statement! If any of\nthe tables named in the argument list do not exist, MariaDB returns an error\nindicating by name which non-existing tables it was unable to drop, but it\nalso drops all of the tables in the list that do exist.\n\nImportant: When a table is dropped, user privileges on the table are not\nautomatically dropped. See GRANT.\n\nIf another thread is using the table in an explicit transaction or an\nautocommit transaction, then the thread acquires a metadata lock (MDL) on the\ntable. The DROP TABLE statement will wait in the \"Waiting for table metadata\nlock\" thread state until the MDL is released. MDLs are released in the\nfollowing cases:\n\n* If an MDL is acquired in an explicit transaction, then the MDL will be\nreleased when the transaction ends.\n* If an MDL is acquired in an autocommit transaction, then the MDL will be\nreleased when the statement ends.\n* Transactional and non-transactional tables are handled the same.\n\nNote that for a partitioned table, DROP TABLE permanently removes the table\ndefinition, all of its partitions, and all of the data which was stored in\nthose partitions. It also removes the partitioning definition (.par) file\nassociated with the dropped table.\n\nFor each referenced table, DROP TABLE drops a temporary table with that name,\nif it exists. If it does not exist, and the TEMPORARY keyword is not used, it\ndrops a non-temporary table with the same name, if it exists. The TEMPORARY\nkeyword ensures that a non-temporary table will not accidentally be dropped.\n\nUse IF EXISTS to prevent an error from occurring for tables that do not exist.\nA NOTE is generated for each non-existent table when using IF EXISTS. See SHOW\nWARNINGS.\n\nIf a foreign key references this table, the table cannot be dropped. In this\ncase, it is necessary to drop the foreign key first.\n\nRESTRICT and CASCADE are allowed to make porting from other database systems\neasier. In MariaDB, they do nothing.\n\nThe comment before the table names (/*COMMENT TO SAVE*/) is stored in the\nbinary log. That feature can be used by replication tools to send their\ninternal messages.\n\nIt is possible to specify table names as db_name.tab_name. This is useful to\ndelete tables from multiple databases with one statement. See Identifier\nQualifiers for details.\n\nThe DROP privilege is required to use DROP TABLE on non-temporary tables. For\ntemporary tables, no privilege is required, because such tables are only\nvisible for the current session.\n\nNote: DROP TABLE automatically commits the current active transaction, unless\nyou use the TEMPORARY keyword.\n\nMariaDB starting with 10.5.4\n----------------------------\nFrom MariaDB 10.5.4, DROP TABLE reliably deletes table remnants inside a\nstorage engine even if the .frm file is missing. Before then, a missing .frm\nfile would result in the statement failing.\n\nMariaDB starting with 10.3.1\n----------------------------\n\nWAIT/NOWAIT\n-----------\n\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nDROP TABLE in replication\n-------------------------\n\nDROP TABLE has the following characteristics in replication:\n\n* DROP TABLE IF EXISTS are always logged.\n* DROP TABLE without IF EXISTS for tables that don\'t exist are not written to\nthe binary log.\n* Dropping of TEMPORARY tables are prefixed in the log with TEMPORARY. These\ndrops are only logged when running statement or mixed mode replication.\n* One DROP TABLE statement can be logged with up to 3 different DROP\nstatements:\nDROP TEMPORARY TABLE list_of_non_transactional_temporary_tables\nDROP TEMPORARY TABLE list_of_transactional_temporary_tables\nDROP TABLE list_of_normal_tables\n\nDROP TABLE on the primary is treated on the replica as DROP TABLE IF EXISTS.\nYou can change that by setting slave-ddl-exec-mode to STRICT.\n\nDropping an Internal #sql-... Table\n-----------------------------------\n\nFrom MariaDB 10.6, DROP TABLE is atomic and the following does not apply.\nUntil MariaDB 10.5, if the mariadbd/mysqld process is killed during an ALTER\nTABLE you may find a table named #sql-... in your data directory. In MariaDB\n10.3, InnoDB tables with this prefix will be deleted automatically during\nstartup. From MariaDB 10.4, these temporary tables will always be deleted\nautomatically.\n\nIf you want to delete one of these tables explicitly you can do so by using\nthe following syntax:\n\nDROP TABLE `#mysql50##sql-...`;\n\nWhen running an ALTER TABLE…ALGORITHM=INPLACE that rebuilds the table, InnoDB\nwill create an internal #sql-ib table. Until MariaDB 10.3.2, for these tables,\nthe .frm file will be called something else. In order to drop such a table\nafter a server crash, you must rename the #sql*.frm file to match the\n#sql-ib*.ibd file.\n\nFrom MariaDB 10.3.3, the same name as the .frm file is used for the\nintermediate copy of the table. The #sql-ib names are used by TRUNCATE and\ndelayed DROP.\n\nFrom MariaDB 10.2.19 and MariaDB 10.3.10, the #sql-ib tables will be deleted\nautomatically.\n\nDropping All Tables in a Database\n---------------------------------\n\nThe best way to drop all tables in a database is by executing DROP DATABASE,\nwhich will drop the database itself, and all tables in it.\n\nHowever, if you want to drop all tables in the database, but you also want to\nkeep the database itself and any other non-table objects in it, then you would\nneed to execute DROP TABLE to drop each individual table. You can construct\nthese DROP TABLE commands by querying the TABLES table in the\ninformation_schema database. For example:\n\nSELECT CONCAT(\'DROP TABLE IF EXISTS `\', TABLE_SCHEMA, \'`.`\', TABLE_NAME, \'`;\')\nFROM information_schema.TABLES\nWHERE TABLE_SCHEMA = \'mydb\';\n\nAtomic DROP TABLE\n-----------------\n\nMariaDB starting with 10.6.1\n----------------------------\nFrom MariaDB 10.6, DROP TABLE for a single table is atomic (MDEV-25180) for\nmost engines, including InnoDB, MyRocks, MyISAM and Aria.\n\nThis means that if there is a crash (server down or power outage) during DROP\nTABLE, all tables that have been processed so far will be completely dropped,\nincluding related trigger files and status entries, and the binary log will\ninclude a DROP TABLE statement for the dropped tables. Tables for which the\ndrop had not started will be left intact.\n\nIn older MariaDB versions, there was a small chance that, during a server\ncrash happening in the middle of DROP TABLE, some storage engines that were\nusing multiple storage files, like MyISAM, could have only a part of its\ninternal files dropped.\n\nIn MariaDB 10.5, DROP TABLE was extended to be able to delete a table that was\nonly partly dropped (MDEV-11412) as explained above. Atomic DROP TABLE is the\nfinal piece to make DROP TABLE fully reliable.\n\nDropping multiple tables is crash-safe.\n\nSee Atomic DDL for more information.\n\nExamples\n--------\n\nDROP TABLE Employees, Customers;\n\nNotes\n-----\n\nBeware that DROP TABLE can drop both tables and sequences. This is mainly done\nto allow old tools like mysqldump to work with sequences.\n\nURL: https://mariadb.com/kb/en/drop-table/','','https://mariadb.com/kb/en/drop-table/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (702,38,'RENAME TABLE','Syntax\n------\n\nRENAME TABLE[S] [IF EXISTS] tbl_name \n [WAIT n | NOWAIT]\n TO new_tbl_name\n [, tbl_name2 TO new_tbl_name2] ...\n\nDescription\n-----------\n\nThis statement renames one or more tables or views, but not the privileges\nassociated with them.\n\nIF EXISTS\n---------\n\nMariaDB starting with 10.5.2\n----------------------------\nIf this directive is used, one will not get an error if the table to be\nrenamed doesn\'t exist.\n\nThe rename operation is done atomically, which means that no other session can\naccess any of the tables while the rename is running. For example, if you have\nan existing table old_table, you can create another table new_table that has\nthe same structure but is empty, and then replace the existing table with the\nempty one as follows (assuming that backup_table does not already exist):\n\nCREATE TABLE new_table (...);\nRENAME TABLE old_table TO backup_table, new_table TO old_table;\n\ntbl_name can optionally be specified as db_name.tbl_name. See Identifier\nQualifiers. This allows to use RENAME to move a table from a database to\nanother (as long as they are on the same filesystem):\n\nRENAME TABLE db1.t TO db2.t;\n\nNote that moving a table to another database is not possible if it has some\ntriggers. Trying to do so produces the following error:\n\nERROR 1435 (HY000): Trigger in wrong schema\n\nAlso, views cannot be moved to another database:\n\nERROR 1450 (HY000): Changing schema from \'old_db\' to \'new_db\' is not allowed.\n\nMultiple tables can be renamed in a single statement. The presence or absence\nof the optional S (RENAME TABLE or RENAME TABLES) has no impact, whether a\nsingle or multiple tables are being renamed.\n\nIf a RENAME TABLE renames more than one table and one renaming fails, all\nrenames executed by the same statement are rolled back.\n\nRenames are always executed in the specified order. Knowing this, it is also\npossible to swap two tables\' names:\n\nRENAME TABLE t1 TO tmp_table,\n t2 TO t1,\n tmp_table TO t2;\n\nWAIT/NOWAIT\n-----------\n\nMariaDB starting with 10.3.0\n----------------------------\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nPrivileges\n----------\n\nExecuting the RENAME TABLE statement requires the DROP, CREATE and INSERT\nprivileges for the table or the database.\n\nAtomic RENAME TABLE\n-------------------\n\nMariaDB starting with 10.6.1\n----------------------------\nFrom MariaDB 10.6, RENAME TABLE is atomic for most engines, including InnoDB,\nMyRocks, MyISAM and Aria (MDEV-23842). This means that if there is a crash\n(server down or power outage) during RENAME TABLE, all tables will revert to\ntheir original names and any changes to trigger files will be reverted.\n\nIn older MariaDB version there was a small chance that, during a server crash\nhappening in the middle of RENAME TABLE, some tables could have been renamed\n(in the worst case partly) while others would not be renamed.\n\nSee Atomic DDL for more information.\n\nURL: https://mariadb.com/kb/en/rename-table/','','https://mariadb.com/kb/en/rename-table/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (703,38,'TRUNCATE TABLE','Syntax\n------\n\nTRUNCATE [TABLE] tbl_name\n [WAIT n | NOWAIT]\n\nDescription\n-----------\n\nTRUNCATE TABLE empties a table completely. It requires the DROP privilege. See\nGRANT.\n\ntbl_name can also be specified in the form db_name.tbl_name (see Identifier\nQualifiers).\n\nLogically, TRUNCATE TABLE is equivalent to a DELETE statement that deletes all\nrows, but there are practical differences under some circumstances.\n\nTRUNCATE TABLE will fail for an InnoDB table if any FOREIGN KEY constraints\nfrom other tables reference the table, returning the error:\n\nERROR 1701 (42000): Cannot truncate a table referenced in a foreign key\nconstraint\n\nForeign Key constraints between columns in the same table are permitted.\n\nFor an InnoDB table, if there are no FOREIGN KEY constraints, InnoDB performs\nfast truncation by dropping the original table and creating an empty one with\nthe same definition, which is much faster than deleting rows one by one. The\nAUTO_INCREMENT counter is reset by TRUNCATE TABLE, regardless of whether there\nis a FOREIGN KEY constraint.\n\nThe count of rows affected by TRUNCATE TABLE is accurate only when it is\nmapped to a DELETE statement.\n\nFor other storage engines, TRUNCATE TABLE differs from DELETE in the following\nways:\n\n* Truncate operations drop and re-create the table, which is much\n faster than deleting rows one by one, particularly for large tables.\n* Truncate operations cause an implicit commit.\n* Truncation operations cannot be performed if the session holds an\n active table lock.\n* Truncation operations do not return a meaningful value for the number\n of deleted rows. The usual result is \"0 rows affected,\" which should\n be interpreted as \"no information.\"\n* As long as the table format file tbl_name.frm is valid, the\n table can be re-created as an empty table\n with TRUNCATE TABLE, even if the data or index files have become\n corrupted.\n* The table handler does not remember the last\n used AUTO_INCREMENT value, but starts counting\n from the beginning. This is true even for MyISAM and InnoDB, which normally\n do not reuse sequence values.\n* When used with partitioned tables, TRUNCATE TABLE preserves\n the partitioning; that is, the data and index files are dropped and\n re-created, while the partition definitions (.par) file is\n unaffected.\n* Since truncation of a table does not make any use of DELETE,\n the TRUNCATE statement does not invoke ON DELETE triggers.\n* TRUNCATE TABLE will only reset the values in the Performance Schema summary\ntables to zero or null, and will not remove the rows.\n\nFor the purposes of binary logging and replication, TRUNCATE TABLE is treated\nas DROP TABLE followed by CREATE TABLE (DDL rather than DML).\n\nTRUNCATE TABLE does not work on views. Currently, TRUNCATE TABLE drops all\nhistorical records from a system-versioned table.\n\nMariaDB starting with 10.3.0\n----------------------------\n\nWAIT/NOWAIT\n-----------\n\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nOracle-mode\n-----------\n\nOracle-mode from MariaDB 10.3 permits the optional keywords REUSE STORAGE or\nDROP STORAGE to be used.\n\nTRUNCATE [TABLE] tbl_name [{DROP | REUSE} STORAGE] [WAIT n | NOWAIT]\n\nThese have no effect on the operation.\n\nPerformance\n-----------\n\nTRUNCATE TABLE is faster than DELETE, because it drops and re-creates a table.\n\nWith InnoDB, TRUNCATE TABLE is slower if innodb_file_per_table=ON is set (the\ndefault). This is because TRUNCATE TABLE unlinks the underlying tablespace\nfile, which can be an expensive operation. See MDEV-8069 for more details.\n\nThe performance issues with innodb_file_per_table=ON can be exacerbated in\ncases where the InnoDB buffer pool is very large and\ninnodb_adaptive_hash_index=ON is set. In that case, using DROP TABLE followed\nby CREATE TABLE instead of TRUNCATE TABLE may perform better. Setting\ninnodb_adaptive_hash_index=OFF (it defaults to ON before MariaDB 10.5) can\nalso help. In MariaDB 10.2 only, from MariaDB 10.2.19, this performance can\nalso be improved by setting innodb_safe_truncate=OFF. See MDEV-9459 for more\ndetails.\n\nSetting innodb_adaptive_hash_index=OFF can also improve TRUNCATE TABLE\nperformance in general. See MDEV-16796 for more details.\n\nURL: https://mariadb.com/kb/en/truncate-table/','','https://mariadb.com/kb/en/truncate-table/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (704,38,'CREATE DATABASE','Syntax\n------\n\nCREATE [OR REPLACE] {DATABASE | SCHEMA} [IF NOT EXISTS] db_name\n [create_specification] ...\n\ncreate_specification:\n [DEFAULT] CHARACTER SET [=] charset_name\n | [DEFAULT] COLLATE [=] collation_name\n | COMMENT [=] \'comment\'\n\nDescription\n-----------\n\nCREATE DATABASE creates a database with the given name. To use this statement,\nyou need the CREATE privilege for the database. CREATE SCHEMA is a synonym for\nCREATE DATABASE.\n\nFor valid identifiers to use as database names, see Identifier Names.\n\nOR REPLACE\n----------\n\nMariaDB starting with 10.1.3\n----------------------------\nThe OR REPLACE clause was added in MariaDB 10.1.3\n\nIf the optional OR REPLACE clause is used, it acts as a shortcut for:\n\nDROP DATABASE IF EXISTS db_name;\nCREATE DATABASE db_name ...;\n\nIF NOT EXISTS\n-------------\n\nWhen the IF NOT EXISTS clause is used, MariaDB will return a warning instead\nof an error if the specified database already exists.\n\nCOMMENT\n-------\n\nMariaDB starting with 10.5.0\n----------------------------\nFrom MariaDB 10.5.0, it is possible to add a comment of a maximum of 1024\nbytes. If the comment length exceeds this length, a error/warning code 4144 is\nthrown. The database comment is also added to the db.opt file, as well as to\nthe information_schema.schemata table.\n\nExamples\n--------\n\nCREATE DATABASE db1;\nQuery OK, 1 row affected (0.18 sec)\n\nCREATE DATABASE db1;\nERROR 1007 (HY000): Can\'t create database \'db1\'; database exists\n\nCREATE OR REPLACE DATABASE db1;\nQuery OK, 2 rows affected (0.00 sec)\n\nCREATE DATABASE IF NOT EXISTS db1;\nQuery OK, 1 row affected, 1 warning (0.01 sec)\n\nSHOW WARNINGS;\n+-------+------+----------------------------------------------+\n| Level | Code | Message |\n+-------+------+----------------------------------------------+\n| Note | 1007 | Can\'t create database \'db1\'; database exists |\n+-------+------+----------------------------------------------+\n\nSetting the character sets and collation. See Setting Character Sets and\nCollations for more details.\n\nCREATE DATABASE czech_slovak_names \n CHARACTER SET = \'keybcs2\'\n COLLATE = \'keybcs2_bin\';\n\nComments, from MariaDB 10.5.0:\n\nCREATE DATABASE presentations COMMENT \'Presentations for conferences\';\n\nURL: https://mariadb.com/kb/en/create-database/','','https://mariadb.com/kb/en/create-database/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (705,38,'CREATE EVENT','Syntax\n------\n\nCREATE [OR REPLACE]\n [DEFINER = { user | CURRENT_USER | role | CURRENT_ROLE }]\n EVENT\n [IF NOT EXISTS]\n event_name\n ON SCHEDULE schedule\n [ON COMPLETION [NOT] PRESERVE]\n [ENABLE | DISABLE | DISABLE ON SLAVE]\n [COMMENT \'comment\']\n DO sql_statement;\n\nschedule:\n AT timestamp [+ INTERVAL interval] ...\n | EVERY interval\n [STARTS timestamp [+ INTERVAL interval] ...]\n [ENDS timestamp [+ INTERVAL interval] ...]\n\ninterval:\n quantity {YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE |\n WEEK | SECOND | YEAR_MONTH | DAY_HOUR | DAY_MINUTE |\n DAY_SECOND | HOUR_MINUTE | HOUR_SECOND | MINUTE_SECOND}\n\nDescription\n-----------\n\nThis statement creates and schedules a new event. It requires the EVENT\nprivilege for the schema in which the event is to be created.\n\nThe minimum requirements for a valid CREATE EVENT statement are as follows:\n\n* The keywords CREATE EVENT plus an event name, which uniquely identifies\n the event in the current schema. (Prior to MySQL 5.1.12, the event name\n needed to be unique only among events created by the same user on a given\n database.)\n* An ON SCHEDULE clause, which determines when and how often the event\n executes.\n* A DO clause, which contains the SQL statement to be executed by an\n event.\n\nHere is an example of a minimal CREATE EVENT statement:\n\nCREATE EVENT myevent\n ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR\n DO\n UPDATE myschema.mytable SET mycol = mycol + 1;\n\nThe previous statement creates an event named myevent. This event executes\nonce — one hour following its creation — by running an SQL statement that\nincrements the value of the myschema.mytable table\'s mycol column by 1.\n\nThe event_name must be a valid MariaDB identifier with a maximum length of 64\ncharacters. It may be delimited using back ticks, and may be qualified with\nthe name of a database schema. An event is associated with both a MariaDB user\n(the definer) and a schema, and its name must be unique among names of events\nwithin that schema. In general, the rules governing event names are the same\nas those for names of stored routines. See Identifier Names.\n\nIf no schema is indicated as part of event_name, the default (current) schema\nis assumed.\n\nFor valid identifiers to use as event names, see Identifier Names.\n\nOR REPLACE\n----------\n\nThe OR REPLACE clause was included in MariaDB 10.1.4. If used and the event\nalready exists, instead of an error being returned, the existing event will be\ndropped and replaced by the newly defined event.\n\nIF NOT EXISTS\n-------------\n\nIf the IF NOT EXISTS clause is used, MariaDB will return a warning instead of\nan error if the event already exists. Cannot be used together with OR REPLACE.\n\nON SCHEDULE\n-----------\n\nThe ON SCHEDULE clause can be used to specify when the event must be triggered.\n\nAT\n--\n\nIf you want to execute the event only once (one time event), you can use the\nAT keyword, followed by a timestamp. If you use CURRENT_TIMESTAMP, the event\nacts as soon as it is created. As a convenience, you can add one or more\nintervals to that timestamp. You can also specify a timestamp in the past, so\nthat the event is stored but not triggered, until you modify it via ALTER\nEVENT.\n\nThe following example shows how to create an event that will be triggered\ntomorrow at a certain time:\n\nCREATE EVENT example\nON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 DAY + INTERVAL 3 HOUR\nDO something;\n\nYou can also specify that an event must be triggered at a regular interval\n(recurring event). In such cases, use the EVERY clause followed by the\ninterval.\n\nIf an event is recurring, you can specify when the first execution must happen\nvia the STARTS clause and a maximum time for the last execution via the ENDS\nclause. STARTS and ENDS clauses are followed by a timestamp and, optionally,\none or more intervals. The ENDS clause can specify a timestamp in the past, so\nthat the event is stored but not executed until you modify it via ALTER EVENT.\n\nIn the following example, next month a recurring event will be triggered\nhourly for a week:\n\nCREATE EVENT example\nON SCHEDULE EVERY 1 HOUR\nSTARTS CURRENT_TIMESTAMP + INTERVAL 1 MONTH\nENDS CURRENT_TIMESTAMP + INTERVAL 1 MONTH + INTERVAL 1 WEEK\nDO some_task;\n\nIntervals consist of a quantity and a time unit. The time units are the same\nused for other staments and time functions, except that you can\'t use\nmicroseconds for events. For simple time units, like HOUR or MINUTE, the\nquantity is an integer number, for example \'10 MINUTE\'. For composite time\nunits, like HOUR_MINUTE or HOUR_SECOND, the quantity must be a string with all\ninvolved simple values and their separators, for example \'2:30\' or \'2:30:30\'.\n\nON COMPLETION [NOT] PRESERVE\n----------------------------\n\nThe ON COMPLETION clause can be used to specify if the event must be deleted\nafter its last execution (that is, after its AT or ENDS timestamp is past). By\ndefault, events are dropped when they are expired. To explicitly state that\nthis is the desired behaviour, you can use ON COMPLETION NOT PRESERVE.\nInstead, if you want the event to be preserved, you can use ON COMPLETION\nPRESERVE.\n\nIn you specify ON COMPLETION NOT PRESERVE, and you specify a timestamp in the\npast for AT or ENDS clause, the event will be immediatly dropped. In such\ncases, you will get a Note 1558: \"Event execution time is in the past and ON\nCOMPLETION NOT PRESERVE is set. The event was dropped immediately after\ncreation\".\n\nENABLE/DISABLE/DISABLE ON SLAVE\n-------------------------------\n\nEvents are ENABLEd by default. If you want to stop MariaDB from executing an\nevent, you may specify DISABLE. When it is ready to be activated, you may\nenable it using ALTER EVENT. Another option is DISABLE ON SLAVE, which\nindicates that an event was created on a master and has been replicated to the\nslave, which is prevented from executing the event. If DISABLE ON SLAVE is\nspecifically set, the event will be disabled everywhere. It will not be\nexecuted on the mater or the slaves.\n\nCOMMENT\n-------\n\nThe COMMENT clause may be used to set a comment for the event. Maximum length\nfor comments is 64 characters. The comment is a string, so it must be quoted.\nTo see events comments, you can query the INFORMATION_SCHEMA.EVENTS table (the\ncolumn is named EVENT_COMMENT).\n\nExamples\n--------\n\nMinimal CREATE EVENT statement:\n\nCREATE EVENT myevent\n ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR\n DO\n UPDATE myschema.mytable SET mycol = mycol + 1;\n\nAn event that will be triggered tomorrow at a certain time:\n\nCREATE EVENT example\nON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 DAY + INTERVAL 3 HOUR\nDO something;\n\nNext month a recurring event will be triggered hourly for a week:\n\nCREATE EVENT example\nON SCHEDULE EVERY 1 HOUR\nSTARTS CURRENT_TIMESTAMP + INTERVAL 1 MONTH\nENDS CURRENT_TIMESTAMP + INTERVAL 1 MONTH + INTERVAL 1 WEEK\nDO some_task;\n\nOR REPLACE and IF NOT EXISTS:\n\nCREATE EVENT myevent\n ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR\n DO\n UPDATE myschema.mytable SET mycol = mycol + 1;\nERROR 1537 (HY000): Event \'myevent\' already exists\n\nCREATE OR REPLACE EVENT myevent\n ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR\n DO\n UPDATE myschema.mytable SET mycol = mycol + 1;;\nQuery OK, 0 rows affected (0.00 sec)\n\nCREATE EVENT IF NOT EXISTS myevent\n ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR\n DO\n UPDATE myschema.mytable SET mycol = mycol + 1;\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+--------------------------------+\n| Level | Code | Message |\n+-------+------+--------------------------------+\n| Note | 1537 | Event \'myevent\' already exists |\n+-------+------+--------------------------------+\n\nURL: https://mariadb.com/kb/en/create-event/','','https://mariadb.com/kb/en/create-event/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (706,38,'CREATE FUNCTION','Syntax\n------\n\nCREATE [OR REPLACE]\n [DEFINER = {user | CURRENT_USER | role | CURRENT_ROLE }]\n [AGGREGATE] FUNCTION [IF NOT EXISTS] func_name ([func_parameter[,...]])\n RETURNS type\n [characteristic ...]\n RETURN func_body\nfunc_parameter:\n [ IN | OUT | INOUT | IN OUT ] param_name type\ntype:\n Any valid MariaDB data type\ncharacteristic:\n LANGUAGE SQL\n | [NOT] DETERMINISTIC\n | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }\n | SQL SECURITY { DEFINER | INVOKER }\n | COMMENT \'string\'\nfunc_body:\n Valid SQL procedure statement\n\nDescription\n-----------\n\nUse the CREATE FUNCTION statement to create a new stored function. You must\nhave the CREATE ROUTINE database privilege to use CREATE FUNCTION. A function\ntakes any number of arguments and returns a value from the function body. The\nfunction body can be any valid SQL expression as you would use, for example,\nin any select expression. If you have the appropriate privileges, you can call\nthe function exactly as you would any built-in function. See Security below\nfor details on privileges.\n\nYou can also use a variant of the CREATE FUNCTION statement to install a\nuser-defined function (UDF) defined by a plugin. See CREATE FUNCTION (UDF) for\ndetails.\n\nYou can use a SELECT statement for the function body by enclosing it in\nparentheses, exactly as you would to use a subselect for any other expression.\nThe SELECT statement must return a single value. If more than one column is\nreturned when the function is called, error 1241 results. If more than one row\nis returned when the function is called, error 1242 results. Use a LIMIT\nclause to ensure only one row is returned.\n\nYou can also replace the RETURN clause with a BEGIN...END compound statement.\nThe compound statement must contain a RETURN statement. When the function is\ncalled, the RETURN statement immediately returns its result, and any\nstatements after RETURN are effectively ignored.\n\nBy default, a function is associated with the current database. To associate\nthe function explicitly with a given database, specify the fully-qualified\nname as db_name.func_name when you create it. If the function name is the same\nas the name of a built-in function, you must use the fully qualified name when\nyou call it.\n\nThe parameter list enclosed within parentheses must always be present. If\nthere are no parameters, an empty parameter list of () should be used.\nParameter names are not case sensitive.\n\nEach parameter can be declared to use any valid data type, except that the\nCOLLATE attribute cannot be used.\n\nFor valid identifiers to use as function names, see Identifier Names.\n\nIN | OUT | INOUT | IN OUT\n-------------------------\n\nMariaDB starting with 10.8.0\n----------------------------\nThe function parameter qualifiers for IN, OUT, INOUT, and IN OUT were added in\na 10.8.0 preview release. Prior to 10.8.0 quantifiers were supported only in\nprocedures.\n\nOUT, INOUT and its equivalent IN OUT, are only valid if called from SET and\nnot SELECT. These quantifiers are especially useful for creating functions\nwith more than one return value. This allows functions to be more complex and\nnested.\n\nDELIMITER $$\nCREATE FUNCTION add_func3(IN a INT, IN b INT, OUT c INT) RETURNS INT\nBEGIN\n SET c = 100;\n RETURN a + b;\nEND;\n$$\nDELIMITER ;\n\nSET @a = 2;\nSET @b = 3;\nSET @c = 0;\nSET @res= add_func3(@a, @b, @c);\n\nSELECT add_func3(@a, @b, @c);\nERROR 4186 (HY000): OUT or INOUT argument 3 for function add_func3 is not\nallowed here\n\nDELIMITER $$\nCREATE FUNCTION add_func4(IN a INT, IN b INT, d INT) RETURNS INT\nBEGIN\n DECLARE c, res INT;\n SET res = add_func3(a, b, c) + d;\n if (c > 99) then\n return 3;\n else\n return res;\n end if;\nEND;\n$$\n\nDELIMITER ;\n\nSELECT add_func4(1,2,3);\n+------------------+\n| add_func4(1,2,3) |\n+------------------+\n| 3 |\n+------------------+\n\nAGGREGATE\n---------\n\nMariaDB starting with 10.3.3\n----------------------------\nFrom MariaDB 10.3.3, it is possible to create stored aggregate functions as\nwell. See Stored Aggregate Functions for details.\n\nRETURNS\n-------\n\nThe RETURNS clause specifies the return type of the function. NULL values are\npermitted with all return types.\n\nWhat happens if the RETURN clause returns a value of a different type? It\ndepends on the SQL_MODE in effect at the moment of the function creation.\n\nIf the SQL_MODE is strict (STRICT_ALL_TABLES or STRICT_TRANS_TABLES flags are\nspecified), a 1366 error will be produced.\n\nOtherwise, the value is coerced to the proper type. For example, if a function\nspecifies an ENUM or SET value in the RETURNS clause, but the RETURN clause\nreturns an integer, the value returned from the function is the string for the\ncorresponding ENUM member of set of SET members.\n\nMariaDB stores the SQL_MODE system variable setting that is in effect at the\ntime a routine is created, and always executes the routine with this setting\nin force, regardless of the server SQL mode in effect when the routine is\ninvoked.\n\nLANGUAGE SQL\n------------\n\nLANGUAGE SQL is a standard SQL clause, and it can be used in MariaDB for\nportability. However that clause has no meaning, because SQL is the only\nsupported language for stored functions.\n\nA function is deterministic if it can produce only one result for a given list\nof parameters. If the result may be affected by stored data, server variables,\nrandom numbers or any value that is not explicitly passed, then the function\nis not deterministic. Also, a function is non-deterministic if it uses\nnon-deterministic functions like NOW() or CURRENT_TIMESTAMP(). The optimizer\nmay choose a faster execution plan if it known that the function is\ndeterministic. In such cases, you should declare the routine using the\nDETERMINISTIC keyword. If you want to explicitly state that the function is\nnot deterministic (which is the default) you can use the NOT DETERMINISTIC\nkeywords.\n\nIf you declare a non-deterministic function as DETERMINISTIC, you may get\nincorrect results. If you declare a deterministic function as NOT\nDETERMINISTIC, in some cases the queries will be slower.\n\nOR REPLACE\n----------\n\nMariaDB starting with 10.1.3\n----------------------------\nIf the optional OR REPLACE clause is used, it acts as a shortcut for:\n\nDROP FUNCTION IF EXISTS function_name;\nCREATE FUNCTION function_name ...;\n\nwith the exception that any existing privileges for the function are not\ndropped.\n\nIF NOT EXISTS\n-------------\n\nMariaDB starting with 10.1.3\n----------------------------\nIf the IF NOT EXISTS clause is used, MariaDB will return a warning instead of\nan error if the function already exists. Cannot be used together with OR\nREPLACE.\n\n[NOT] DETERMINISTIC\n-------------------\n\nThe [NOT] DETERMINISTIC clause also affects binary logging, because the\nSTATEMENT format can not be used to store or replicate non-deterministic\nstatements.\n\nCONTAINS SQL, NO SQL, READS SQL DATA, and MODIFIES SQL DATA are informative\nclauses that tell the server what the function does. MariaDB does not check in\nany way whether the specified clause is correct. If none of these clauses are\nspecified, CONTAINS SQL is used by default.\n\nMODIFIES SQL DATA\n-----------------\n\nMODIFIES SQL DATA means that the function contains statements that may modify\ndata stored in databases. This happens if the function contains statements\nlike DELETE, UPDATE, INSERT, REPLACE or DDL.\n\nREADS SQL DATA\n--------------\n\nREADS SQL DATA means that the function reads data stored in databases, but\ndoes not modify any data. This happens if SELECT statements are used, but\nthere no write operations are executed.\n\nCONTAINS SQL\n------------\n\nCONTAINS SQL means that the function contains at least one SQL statement, but\nit does not read or write any data stored in a database. Examples include SET\nor DO.\n\nNO SQL\n------\n\nNO SQL means nothing, because MariaDB does not currently support any language\nother than SQL.\n\nOracle Mode\n-----------\n\nMariaDB starting with 10.3\n--------------------------\nFrom MariaDB 10.3, a subset of Oracle\'s PL/SQL language has been supported in\naddition to the traditional SQL/PSM-based MariaDB syntax. See Oracle mode from\nMariaDB 10.3 for details on changes when running Oracle mode.\n\nSecurity\n--------\n\nYou must have the EXECUTE privilege on a function to call it. MariaDB\nautomatically grants the EXECUTE and ALTER ROUTINE privileges to the account\nthat called CREATE FUNCTION, even if the DEFINER clause was used.\n\nEach function has an account associated as the definer. By default, the\ndefiner is the account that created the function. Use the DEFINER clause to\nspecify a different account as the definer. You must have the SUPER privilege,\nor, from MariaDB 10.5.2, the SET USER privilege, to use the DEFINER clause.\nSee Account Names for details on specifying accounts.\n\nThe SQL SECURITY clause specifies what privileges are used when a function is\ncalled. If SQL SECURITY is INVOKER, the function body will be evaluated using\nthe privileges of the user calling the function. If SQL SECURITY is DEFINER,\nthe function body is always evaluated using the privileges of the definer\naccount. DEFINER is the default.\n\nThis allows you to create functions that grant limited access to certain data.\nFor example, say you have a table that stores some employee information, and\nthat you\'ve granted SELECT privileges only on certain columns to the user\naccount roger.\n\nCREATE TABLE employees (name TINYTEXT, dept TINYTEXT, salary INT);\nGRANT SELECT (name, dept) ON employees TO roger;\n\nTo allow the user the get the maximum salary for a department, define a\nfunction and grant the EXECUTE privilege:\n\nCREATE FUNCTION max_salary (dept TINYTEXT) RETURNS INT RETURN\n (SELECT MAX(salary) FROM employees WHERE employees.dept = dept);\nGRANT EXECUTE ON FUNCTION max_salary TO roger;\n\nSince SQL SECURITY defaults to DEFINER, whenever the user roger calls this\nfunction, the subselect will execute with your privileges. As long as you have\nprivileges to select the salary of each employee, the caller of the function\nwill be able to get the maximum salary for each department without being able\nto see individual salaries.\n\nCharacter sets and collations\n-----------------------------\n\nFunction return types can be declared to use any valid character set and\ncollation. If used, the COLLATE attribute needs to be preceded by a CHARACTER\nSET attribute.\n\nIf the character set and collation are not specifically set in the statement,\nthe database defaults at the time of creation will be used. If the database\ndefaults change at a later stage, the stored function character set/collation\nwill not be changed at the same time; the stored function needs to be dropped\nand recreated to ensure the same character set/collation as the database is\nused.\n\nExamples\n--------\n\nThe following example function takes a parameter, performs an operation using\nan SQL function, and returns the result.\n\nCREATE FUNCTION hello (s CHAR(20))\n RETURNS CHAR(50) DETERMINISTIC\n RETURN CONCAT(\'Hello, \',s,\'!\');\n\nSELECT hello(\'world\');\n+----------------+\n| hello(\'world\') |\n+----------------+\n| Hello, world! |\n+----------------+\n\nYou can use a compound statement in a function to manipulate data with\nstatements like INSERT and UPDATE. The following example creates a counter\nfunction that uses a temporary table to store the current value. Because the\ncompound statement contains statements terminated with semicolons, you have to\nfirst change the statement delimiter with the DELIMITER statement to allow the\nsemicolon to be used in the function body. See Delimiters in the mysql client\nfor more.\n\nCREATE TEMPORARY TABLE counter (c INT);\nINSERT INTO counter VALUES (0);\nDELIMITER //\nCREATE FUNCTION counter () RETURNS INT\n BEGIN\n UPDATE counter SET c = c + 1;\n RETURN (SELECT c FROM counter LIMIT 1);\n END //\nDELIMITER ;\n\nCharacter set and collation:\n\nCREATE FUNCTION hello2 (s CHAR(20))\n RETURNS CHAR(50) CHARACTER SET \'utf8\' COLLATE \'utf8_bin\' DETERMINISTIC\n RETURN CONCAT(\'Hello, \',s,\'!\');\n\nURL: https://mariadb.com/kb/en/create-function/','','https://mariadb.com/kb/en/create-function/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (707,38,'CREATE INDEX','Syntax\n------\n\nCREATE [OR REPLACE] [UNIQUE|FULLTEXT|SPATIAL] INDEX \n [IF NOT EXISTS] index_name\n [index_type]\n ON tbl_name (index_col_name,...)\n [WAIT n | NOWAIT]\n [index_option]\n [algorithm_option | lock_option] ...\n\nindex_col_name:\n col_name [(length)] [ASC | DESC]\n\nindex_type:\n USING {BTREE | HASH | RTREE}\n\nindex_option:\n [ KEY_BLOCK_SIZE [=] value\n | index_type\n | WITH PARSER parser_name\n | COMMENT \'string\'\n | CLUSTERING={YES| NO} ]\n [ IGNORED | NOT IGNORED ]\n\nalgorithm_option:\n ALGORITHM [=] {DEFAULT|INPLACE|COPY|NOCOPY|INSTANT}\n\nlock_option:\n LOCK [=] {DEFAULT|NONE|SHARED|EXCLUSIVE}\n\nDescription\n-----------\n\nCREATE INDEX is mapped to an ALTER TABLE statement to create indexes. See\nALTER TABLE. CREATE INDEX cannot be used to create a PRIMARY KEY; use ALTER\nTABLE instead.\n\nIf another connection is using the table, a metadata lock is active, and this\nstatement will wait until the lock is released. This is also true for\nnon-transactional tables.\n\nAnother shortcut, DROP INDEX, allows the removal of an index.\n\nFor valid identifiers to use as index names, see Identifier Names.\n\nNote that KEY_BLOCK_SIZE is currently ignored in CREATE INDEX, although it is\nincluded in the output of SHOW CREATE TABLE.\n\nPrivileges\n----------\n\nExecuting the CREATE INDEX statement requires the INDEX privilege for the\ntable or the database.\n\nOnline DDL\n----------\n\nOnline DDL is supported with the ALGORITHM and LOCK clauses.\n\nSee InnoDB Online DDL Overview for more information on online DDL with InnoDB.\n\nCREATE OR REPLACE INDEX\n-----------------------\n\nMariaDB starting with 10.1.4\n----------------------------\nThe OR REPLACE clause was added in MariaDB 10.1.4.\n\nIf the OR REPLACE clause is used and if the index already exists, then instead\nof returning an error, the server will drop the existing index and replace it\nwith the newly defined index.\n\nCREATE INDEX IF NOT EXISTS\n--------------------------\n\nIf the IF NOT EXISTS clause is used, then the index will only be created if an\nindex with the same name does not already exist. If the index already exists,\nthen a warning will be triggered by default.\n\nIndex Definitions\n-----------------\n\nSee CREATE TABLE: Index Definitions for information about index definitions.\n\nWAIT/NOWAIT\n-----------\n\nMariaDB starting with 10.3.0\n----------------------------\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nALGORITHM\n---------\n\nSee ALTER TABLE: ALGORITHM for more information.\n\nLOCK\n----\n\nSee ALTER TABLE: LOCK for more information.\n\nProgress Reporting\n------------------\n\nMariaDB provides progress reporting for CREATE INDEX statement for clients\nthat support the new progress reporting protocol. For example, if you were\nusing the mysql client, then the progress report might look like this::\n\nCREATE INDEX ON tab (num);;\nStage: 1 of 2 \'copy to tmp table\' 46% of stage\n\nThe progress report is also shown in the output of the SHOW PROCESSLIST\nstatement and in the contents of the information_schema.PROCESSLIST table.\n\nSee Progress Reporting for more information.\n\nWITHOUT OVERLAPS\n----------------\n\nMariaDB starting with 10.5.3\n----------------------------\nThe WITHOUT OVERLAPS clause allows one to constrain a primary or unique index\nsuch that application-time periods cannot overlap.\n\nExamples\n--------\n\nCreating a unique index:\n\nCREATE UNIQUE INDEX HomePhone ON Employees(Home_Phone);\n\nOR REPLACE and IF NOT EXISTS:\n\nCREATE INDEX xi ON xx5 (x);\nQuery OK, 0 rows affected (0.03 sec)\n\nCREATE INDEX xi ON xx5 (x);\nERROR 1061 (42000): Duplicate key name \'xi\'\n\nCREATE OR REPLACE INDEX xi ON xx5 (x);\nQuery OK, 0 rows affected (0.03 sec)\n\nCREATE INDEX IF NOT EXISTS xi ON xx5 (x);\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+-------------------------+\n| Level | Code | Message |\n+-------+------+-------------------------+\n| Note | 1061 | Duplicate key name \'xi\' |\n+-------+------+-------------------------+\n\nFrom MariaDB 10.5.3, creating a unique index for an application-time period\ntable with a WITHOUT OVERLAPS constraint:\n\nCREATE UNIQUE INDEX u ON rooms (room_number, p WITHOUT OVERLAPS);\n\nURL: https://mariadb.com/kb/en/create-index/','','https://mariadb.com/kb/en/create-index/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (707,38,'CREATE INDEX','Syntax\n------\n\nCREATE [OR REPLACE] [UNIQUE|FULLTEXT|SPATIAL] INDEX \n [IF NOT EXISTS] index_name\n [index_type]\n ON tbl_name (index_col_name,...)\n [WAIT n | NOWAIT]\n [index_option]\n [algorithm_option | lock_option] ...\n\nindex_col_name:\n col_name [(length)] [ASC | DESC]\n\nindex_type:\n USING {BTREE | HASH | RTREE}\n\nindex_option:\n [ KEY_BLOCK_SIZE [=] value\n | index_type\n | WITH PARSER parser_name\n | COMMENT \'string\'\n | CLUSTERING={YES| NO} ]\n [ IGNORED | NOT IGNORED ]\n\nalgorithm_option:\n ALGORITHM [=] {DEFAULT|INPLACE|COPY|NOCOPY|INSTANT}\n\nlock_option:\n LOCK [=] {DEFAULT|NONE|SHARED|EXCLUSIVE}\n\nDescription\n-----------\n\nCREATE INDEX is mapped to an ALTER TABLE statement to create indexes. See\nALTER TABLE. CREATE INDEX cannot be used to create a PRIMARY KEY; use ALTER\nTABLE instead.\n\nIf another connection is using the table, a metadata lock is active, and this\nstatement will wait until the lock is released. This is also true for\nnon-transactional tables.\n\nAnother shortcut, DROP INDEX, allows the removal of an index.\n\nFor valid identifiers to use as index names, see Identifier Names.\n\nNote that KEY_BLOCK_SIZE is currently ignored in CREATE INDEX, although it is\nincluded in the output of SHOW CREATE TABLE.\n\nPrivileges\n----------\n\nExecuting the CREATE INDEX statement requires the INDEX privilege for the\ntable or the database.\n\nOnline DDL\n----------\n\nOnline DDL is supported with the ALGORITHM and LOCK clauses.\n\nSee InnoDB Online DDL Overview for more information on online DDL with InnoDB.\n\nCREATE OR REPLACE INDEX\n-----------------------\n\nIf the OR REPLACE clause is used and if the index already exists, then instead\nof returning an error, the server will drop the existing index and replace it\nwith the newly defined index.\n\nCREATE INDEX IF NOT EXISTS\n--------------------------\n\nIf the IF NOT EXISTS clause is used, then the index will only be created if an\nindex with the same name does not already exist. If the index already exists,\nthen a warning will be triggered by default.\n\nIndex Definitions\n-----------------\n\nSee CREATE TABLE: Index Definitions for information about index definitions.\n\nWAIT/NOWAIT\n-----------\n\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nALGORITHM\n---------\n\nSee ALTER TABLE: ALGORITHM for more information.\n\nLOCK\n----\n\nSee ALTER TABLE: LOCK for more information.\n\nProgress Reporting\n------------------\n\nMariaDB provides progress reporting for CREATE INDEX statement for clients\nthat support the new progress reporting protocol. For example, if you were\nusing the mysql client, then the progress report might look like this::\n\nCREATE INDEX i ON tab (num);\nStage: 1 of 2 \'copy to tmp table\' 46% of stage\n\nThe progress report is also shown in the output of the SHOW PROCESSLIST\nstatement and in the contents of the information_schema.PROCESSLIST table.\n\nSee Progress Reporting for more information.\n\nWITHOUT OVERLAPS\n----------------\n\nMariaDB starting with 10.5.3\n----------------------------\nThe WITHOUT OVERLAPS clause allows one to constrain a primary or unique index\nsuch that application-time periods cannot overlap.\n\nExamples\n--------\n\nCreating a unique index:\n\nCREATE UNIQUE INDEX HomePhone ON Employees(Home_Phone);\n\nOR REPLACE and IF NOT EXISTS:\n\nCREATE INDEX xi ON xx5 (x);\nQuery OK, 0 rows affected (0.03 sec)\n\nCREATE INDEX xi ON xx5 (x);\nERROR 1061 (42000): Duplicate key name \'xi\'\n\nCREATE OR REPLACE INDEX xi ON xx5 (x);\nQuery OK, 0 rows affected (0.03 sec)\n\nCREATE INDEX IF NOT EXISTS xi ON xx5 (x);\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+-------------------------+\n| Level | Code | Message |\n+-------+------+-------------------------+\n| Note | 1061 | Duplicate key name \'xi\' |\n+-------+------+-------------------------+\n\nFrom MariaDB 10.5.3, creating a unique index for an application-time period\ntable with a WITHOUT OVERLAPS constraint:\n\nCREATE UNIQUE INDEX u ON rooms (room_number, p WITHOUT OVERLAPS);\n\nURL: https://mariadb.com/kb/en/create-index/','','https://mariadb.com/kb/en/create-index/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (708,38,'CREATE PACKAGE','MariaDB starting with 10.3.5\n----------------------------\nOracle-style packages were introduced in MariaDB 10.3.5.\n\nSyntax\n------\n\nCREATE\n [ OR REPLACE]\n [DEFINER = { user | CURRENT_USER | role | CURRENT_ROLE }]\n PACKAGE [ IF NOT EXISTS ]\n [ db_name . ] package_name\n [ package_characteristic ... ]\n{ AS | IS }\n [ package_specification_element ... ]\nEND [ package_name ]\n\npackage_characteristic:\n COMMENT \'string\'\n | SQL SECURITY { DEFINER | INVOKER }\n\npackage_specification_element:\n FUNCTION_SYM package_specification_function ;\n | PROCEDURE_SYM package_specification_procedure ;\n\npackage_specification_function:\n func_name [ ( func_param [, func_param]... ) ]\n RETURNS func_return_type\n [ package_routine_characteristic... ]\n\npackage_specification_procedure:\n proc_name [ ( proc_param [, proc_param]... ) ]\n [ package_routine_characteristic... ]\n\nfunc_return_type:\n type\n\nfunc_param:\n param_name [ IN | OUT | INOUT | IN OUT ] type\n\nproc_param:\n param_name [ IN | OUT | INOUT | IN OUT ] type\n\ntype:\n Any valid MariaDB explicit or anchored data type\n\npackage_routine_characteristic:\n COMMENT \'string\'\n | LANGUAGE SQL\n | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }\n | SQL SECURITY { DEFINER | INVOKER }\n\nDescription\n-----------\n\nThe CREATE PACKAGE statement can be used when Oracle SQL_MODE is set.\n\nThe CREATE PACKAGE creates the specification for a stored package (a\ncollection of logically related stored objects). A stored package\nspecification declares public routines (procedures and functions) of the\npackage, but does not implement these routines.\n\nA package whose specification was created by the CREATE PACKAGE statement,\nshould later be implemented using the CREATE PACKAGE BODY statement.\n\nFunction parameter quantifiers IN | OUT | INOUT | IN OUT\n--------------------------------------------------------\n\nMariaDB starting with 10.8.0\n----------------------------\nThe function parameter quantifiers for IN, OUT, INOUT, and IN OUT where added\nin a 10.8.0 preview release. Prior to 10.8.0 quantifiers were supported only\nin procedures.\n\nOUT, INOUT and its equivalent IN OUT, are only valid if called from SET and\nnot SELECT. These quantifiers are especially useful for creating functions and\nprocedures with more than one return value. This allows functions and\nprocedures to be more complex and nested.\n\nExamples\n--------\n\nSET sql_mode=ORACLE;\nDELIMITER $$\nCREATE OR REPLACE PACKAGE employee_tools AS\n FUNCTION getSalary(eid INT) RETURN DECIMAL(10,2);\n PROCEDURE raiseSalary(eid INT, amount DECIMAL(10,2));\n PROCEDURE raiseSalaryStd(eid INT);\n PROCEDURE hire(ename TEXT, esalary DECIMAL(10,2));\nEND;\n$$\nDELIMITER ;\n\nURL: https://mariadb.com/kb/en/create-package/','','https://mariadb.com/kb/en/create-package/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (709,38,'CREATE PACKAGE BODY','MariaDB starting with 10.3.5\n----------------------------\nOracle-style packages were introduced in MariaDB 10.3.5.\n\nSyntax\n------\n\nCREATE [ OR REPLACE ]\n [DEFINER = { user | CURRENT_USER | role | CURRENT_ROLE }]\n PACKAGE BODY\n [ IF NOT EXISTS ]\n [ db_name . ] package_name\n [ package_characteristic... ]\n{ AS | IS }\n package_implementation_declare_section\n package_implementation_executable_section\nEND [ package_name]\n\npackage_implementation_declare_section:\n package_implementation_item_declaration\n [ package_implementation_item_declaration... ]\n [ package_implementation_routine_definition... ]\n | package_implementation_routine_definition\n [ package_implementation_routine_definition...]\n\npackage_implementation_item_declaration:\n variable_declaration ;\n\nvariable_declaration:\n variable_name[,...] type [:= expr ]\n\npackage_implementation_routine_definition:\n FUNCTION package_specification_function\n [ package_implementation_function_body ] ;\n | PROCEDURE package_specification_procedure\n [ package_implementation_procedure_body ] ;\n\npackage_implementation_function_body:\n { AS | IS } package_routine_body [func_name]\n\npackage_implementation_procedure_body:\n { AS | IS } package_routine_body [proc_name]\n\npackage_routine_body:\n [ package_routine_declarations ]\n BEGIN\n statements [ EXCEPTION exception_handlers ]\n END\n\npackage_routine_declarations:\n package_routine_declaration \';\' [package_routine_declaration \';\']...\n\npackage_routine_declaration:\n variable_declaration\n | condition_name CONDITION FOR condition_value\n | user_exception_name EXCEPTION\n | CURSOR_SYM cursor_name\n [ ( cursor_formal_parameters ) ]\n IS select_statement\n ;\n\npackage_implementation_executable_section:\n END\n | BEGIN\n statement ; [statement ; ]...\n [EXCEPTION exception_handlers]\n END\n\nexception_handlers:\n exception_handler [exception_handler...]\n\nexception_handler:\n WHEN_SYM condition_value [, condition_value]...\n THEN_SYM statement ; [statement ;]...\n\ncondition_value:\n condition_name\n | user_exception_name\n | SQLWARNING\n | SQLEXCEPTION\n | NOT FOUND\n | OTHERS_SYM\n | SQLSTATE [VALUE] sqlstate_value\n | mariadb_error_code\n\nDescription\n-----------\n\nThe CREATE PACKAGE BODY statement can be used when Oracle SQL_MODE is set.\n\nThe CREATE PACKAGE BODY statement creates the package body for a stored\npackage. The package specification must be previously created using the CREATE\nPACKAGE statement.\n\nA package body provides implementations of the package public routines and can\noptionally have:\n\n* package-wide private variables\n* package private routines\n* forward declarations for private routines\n* an executable initialization section\n\nExamples\n--------\n\nSET sql_mode=ORACLE;\nDELIMITER $$\nCREATE OR REPLACE PACKAGE employee_tools AS\n FUNCTION getSalary(eid INT) RETURN DECIMAL(10,2);\n PROCEDURE raiseSalary(eid INT, amount DECIMAL(10,2));\n PROCEDURE raiseSalaryStd(eid INT);\n PROCEDURE hire(ename TEXT, esalary DECIMAL(10,2));\nEND;\n$$\nCREATE PACKAGE BODY employee_tools AS\n -- package body variables\n stdRaiseAmount DECIMAL(10,2):=500;\n\n-- private routines\n PROCEDURE log (eid INT, ecmnt TEXT) AS\n BEGIN\n INSERT INTO employee_log (id, cmnt) VALUES (eid, ecmnt);\n END;\n\n-- public routines\n PROCEDURE hire(ename TEXT, esalary DECIMAL(10,2)) AS\n eid INT;\n BEGIN\n INSERT INTO employee (name, salary) VALUES (ename, esalary);\n eid:= last_insert_id();\n log(eid, \'hire \' || ename);\n END;\n\nFUNCTION getSalary(eid INT) RETURN DECIMAL(10,2) AS\n nSalary DECIMAL(10,2);\n BEGIN\n SELECT salary INTO nSalary FROM employee WHERE id=eid;\n log(eid, \'getSalary id=\' || eid || \' salary=\' || nSalary);\n RETURN nSalary;\n END;\n\nPROCEDURE raiseSalary(eid INT, amount DECIMAL(10,2)) AS\n BEGIN\n UPDATE employee SET salary=salary+amount WHERE id=eid;\n log(eid, \'raiseSalary id=\' || eid || \' amount=\' || amount);\n END;\n\nPROCEDURE raiseSalaryStd(eid INT) AS\n BEGIN\n raiseSalary(eid, stdRaiseAmount);\n log(eid, \'raiseSalaryStd id=\' || eid);\n END;\n\nBEGIN\n -- This code is executed when the current session\n -- accesses any of the package routines for the first time\n log(0, \'Session \' || connection_id() || \' \' || current_user || \' started\');\nEND;\n$$\n\nDELIMITER ;\n\nURL: https://mariadb.com/kb/en/create-package-body/','','https://mariadb.com/kb/en/create-package-body/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (710,38,'CREATE PROCEDURE','Syntax\n------\n\nCREATE\n [OR REPLACE]\n [DEFINER = { user | CURRENT_USER | role | CURRENT_ROLE }]\n PROCEDURE [IF NOT EXISTS] sp_name ([proc_parameter[,...]])\n [characteristic ...] routine_body\n\nproc_parameter:\n [ IN | OUT | INOUT ] param_name type\n\ntype:\n Any valid MariaDB data type\n\ncharacteristic:\n LANGUAGE SQL\n | [NOT] DETERMINISTIC\n | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }\n | SQL SECURITY { DEFINER | INVOKER }\n | COMMENT \'string\'\n\nroutine_body:\n Valid SQL procedure statement\n\nDescription\n-----------\n\nCreates a stored procedure. By default, a routine is associated with the\ndefault database. To associate the routine explicitly with a given database,\nspecify the name as db_name.sp_name when you create it.\n\nWhen the routine is invoked, an implicit USE db_name is performed (and undone\nwhen the routine terminates). The causes the routine to have the given default\ndatabase while it executes. USE statements within stored routines are\ndisallowed.\n\nWhen a stored procedure has been created, you invoke it by using the CALL\nstatement (see CALL).\n\nTo execute the CREATE PROCEDURE statement, it is necessary to have the CREATE\nROUTINE privilege. By default, MariaDB automatically grants the ALTER ROUTINE\nand EXECUTE privileges to the routine creator. See also Stored Routine\nPrivileges.\n\nThe DEFINER and SQL SECURITY clauses specify the security context to be used\nwhen checking access privileges at routine execution time, as described here.\nRequires the SUPER privilege, or, from MariaDB 10.5.2, the SET USER privilege.\n\nIf the routine name is the same as the name of a built-in SQL function, you\nmust use a space between the name and the following parenthesis when defining\nthe routine, or a syntax error occurs. This is also true when you invoke the\nroutine later. For this reason, we suggest that it is better to avoid re-using\nthe names of existing SQL functions for your own stored routines.\n\nThe IGNORE_SPACE SQL mode applies to built-in functions, not to stored\nroutines. It is always allowable to have spaces after a routine name,\nregardless of whether IGNORE_SPACE is enabled.\n\nThe parameter list enclosed within parentheses must always be present. If\nthere are no parameters, an empty parameter list of () should be used.\nParameter names are not case sensitive.\n\nEach parameter can be declared to use any valid data type, except that the\nCOLLATE attribute cannot be used.\n\nFor valid identifiers to use as procedure names, see Identifier Names.\n\nThings to be Aware of With CREATE OR REPLACE\n--------------------------------------------\n\n* One can\'t use OR REPLACE together with IF EXISTS.\n\nCREATE PROCEDURE IF NOT EXISTS\n------------------------------\n\nIf the IF NOT EXISTS clause is used, then the procedure will only be created\nif a procedure with the same name does not already exist. If the procedure\nalready exists, then a warning will be triggered by default.\n\nIN/OUT/INOUT\n------------\n\nEach parameter is an IN parameter by default. To specify otherwise for a\nparameter, use the keyword OUT or INOUT before the parameter name.\n\nAn IN parameter passes a value into a procedure. The procedure might modify\nthe value, but the modification is not visible to the caller when the\nprocedure returns. An OUT parameter passes a value from the procedure back to\nthe caller. Its initial value is NULL within the procedure, and its value is\nvisible to the caller when the procedure returns. An INOUT parameter is\ninitialized by the caller, can be modified by the procedure, and any change\nmade by the procedure is visible to the caller when the procedure returns.\n\nFor each OUT or INOUT parameter, pass a user-defined variable in the CALL\nstatement that invokes the procedure so that you can obtain its value when the\nprocedure returns. If you are calling the procedure from within another stored\nprocedure or function, you can also pass a routine parameter or local routine\nvariable as an IN or INOUT parameter.\n\nDETERMINISTIC/NOT DETERMINISTIC\n-------------------------------\n\nDETERMINISTIC and NOT DETERMINISTIC apply only to functions. Specifying\nDETERMINISTC or NON-DETERMINISTIC in procedures has no effect. The default\nvalue is NOT DETERMINISTIC. Functions are DETERMINISTIC when they always\nreturn the same value for the same input. For example, a truncate or substring\nfunction. Any function involving data, therefore, is always NOT DETERMINISTIC.\n\nCONTAINS SQL/NO SQL/READS SQL DATA/MODIFIES SQL DATA\n----------------------------------------------------\n\nCONTAINS SQL, NO SQL, READS SQL DATA, and MODIFIES SQL DATA are informative\nclauses that tell the server what the function does. MariaDB does not check in\nany way whether the specified clause is correct. If none of these clauses are\nspecified, CONTAINS SQL is used by default.\n\nMODIFIES SQL DATA means that the function contains statements that may modify\ndata stored in databases. This happens if the function contains statements\nlike DELETE, UPDATE, INSERT, REPLACE or DDL.\n\nREADS SQL DATA means that the function reads data stored in databases, but\ndoes not modify any data. This happens if SELECT statements are used, but\nthere no write operations are executed.\n\nCONTAINS SQL means that the function contains at least one SQL statement, but\nit does not read or write any data stored in a database. Examples include SET\nor DO.\n\nNO SQL means nothing, because MariaDB does not currently support any language\nother than SQL.\n\nThe routine_body consists of a valid SQL procedure statement. This can be a\nsimple statement such as SELECT or INSERT, or it can be a compound statement\nwritten using BEGIN and END. Compound statements can contain declarations,\nloops, and other control structure statements. See Programmatic and Compound\nStatements for syntax details.\n\nMariaDB allows routines to contain DDL statements, such as CREATE and DROP.\nMariaDB also allows stored procedures (but not stored functions) to contain\nSQL transaction statements such as COMMIT.\n\nFor additional information about statements that are not allowed in stored\nroutines, see Stored Routine Limitations.\n\nInvoking stored procedure from within programs\n----------------------------------------------\n\nFor information about invoking stored procedures from within programs written\nin a language that has a MariaDB/MySQL interface, see CALL.\n\nOR REPLACE\n----------\n\nIf the optional OR REPLACE clause is used, it acts as a shortcut for:\n\nDROP PROCEDURE IF EXISTS name;\nCREATE PROCEDURE name ...;\n\nwith the exception that any existing privileges for the procedure are not\ndropped.\n\nsql_mode\n--------\n\nMariaDB stores the sql_mode system variable setting that is in effect at the\ntime a routine is created, and always executes the routine with this setting\nin force, regardless of the server SQL mode in effect when the routine is\ninvoked.\n\nCharacter Sets and Collations\n-----------------------------\n\nProcedure parameters can be declared with any character set/collation. If the\ncharacter set and collation are not specifically set, the database defaults at\nthe time of creation will be used. If the database defaults change at a later\nstage, the stored procedure character set/collation will not be changed at the\nsame time; the stored procedure needs to be dropped and recreated to ensure\nthe same character set/collation as the database is used.\n\nOracle Mode\n-----------\n\nMariaDB starting with 10.3\n--------------------------\nFrom MariaDB 10.3, a subset of Oracle\'s PL/SQL language has been supported in\naddition to the traditional SQL/PSM-based MariaDB syntax. See Oracle mode from\nMariaDB 10.3 for details on changes when running Oracle mode.\n\nExamples\n--------\n\nThe following example shows a simple stored procedure that uses an OUT\nparameter. It uses the DELIMITER command to set a new delimiter for the\nduration of the process — see Delimiters in the mysql client.\n\nDELIMITER //\n\nCREATE PROCEDURE simpleproc (OUT param1 INT)\n BEGIN\n SELECT COUNT(*) INTO param1 FROM t;\n END;\n//\n\nDELIMITER ;\n\nCALL simpleproc(@a);\n\nSELECT @a;\n+------+\n| @a |\n+------+\n| 1 |\n+------+\n\nCharacter set and collation:\n\nDELIMITER //\n\nCREATE PROCEDURE simpleproc2 (\n OUT param1 CHAR(10) CHARACTER SET \'utf8\' COLLATE \'utf8_bin\'\n)\n BEGIN\n SELECT CONCAT(\'a\'),f1 INTO param1 FROM t;\n END;\n//\n\nDELIMITER ;\n\nCREATE OR REPLACE:\n\nDELIMITER //\n\nCREATE PROCEDURE simpleproc2 (\n OUT param1 CHAR(10) CHARACTER SET \'utf8\' COLLATE \'utf8_bin\'\n)\n BEGIN\n SELECT CONCAT(\'a\'),f1 INTO param1 FROM t;\n END;\n//\nERROR 1304 (42000): PROCEDURE simpleproc2 already exists\n\nDELIMITER ;\n\nDELIMITER //\n\nCREATE OR REPLACE PROCEDURE simpleproc2 (\n OUT param1 CHAR(10) CHARACTER SET \'utf8\' COLLATE \'utf8_bin\'\n)\n BEGIN\n SELECT CONCAT(\'a\'),f1 INTO param1 FROM t;\n END;\n//\nERROR 1304 (42000): PROCEDURE simpleproc2 already exists\n\nDELIMITER ;\nQuery OK, 0 rows affected (0.03 sec)\n\nURL: https://mariadb.com/kb/en/create-procedure/','','https://mariadb.com/kb/en/create-procedure/'); @@ -821,8 +821,8 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (713,38,'CREATE TRIGGER','Syntax\n------\n\nCREATE [OR REPLACE]\n [DEFINER = { user | CURRENT_USER | role | CURRENT_ROLE }]\n TRIGGER [IF NOT EXISTS] trigger_name trigger_time trigger_event\n ON tbl_name FOR EACH ROW\n [{ FOLLOWS | PRECEDES } other_trigger_name ]\n trigger_stmt;\n\nDescription\n-----------\n\nThis statement creates a new trigger. A trigger is a named database object\nthat is associated with a table, and that activates when a particular event\noccurs for the table. The trigger becomes associated with the table named\ntbl_name, which must refer to a permanent table. You cannot associate a\ntrigger with a TEMPORARY table or a view.\n\nCREATE TRIGGER requires the TRIGGER privilege for the table associated with\nthe trigger.\n\nMariaDB starting with 10.2.3\n----------------------------\nYou can have multiple triggers for the same trigger_time and trigger_event.\n\nFor valid identifiers to use as trigger names, see Identifier Names.\n\nOR REPLACE\n----------\n\nMariaDB starting with 10.1.4\n----------------------------\nIf used and the trigger already exists, instead of an error being returned,\nthe existing trigger will be dropped and replaced by the newly defined trigger.\n\nDEFINER\n-------\n\nThe DEFINER clause determines the security context to be used when checking\naccess privileges at trigger activation time. Usage requires the SUPER\nprivilege, or, from MariaDB 10.5.2, the SET USER privilege.\n\nIF NOT EXISTS\n-------------\n\nMariaDB starting with 10.1.4\n----------------------------\nIf the IF NOT EXISTS clause is used, the trigger will only be created if a\ntrigger of the same name does not exist. If the trigger already exists, by\ndefault a warning will be returned.\n\ntrigger_time\n------------\n\ntrigger_time is the trigger action time. It can be BEFORE or AFTER to indicate\nthat the trigger activates before or after each row to be modified.\n\ntrigger_event\n-------------\n\ntrigger_event indicates the kind of statement that activates the trigger. The\ntrigger_event can be one of the following:\n\n* INSERT: The trigger is activated whenever a new row is inserted into the\ntable; for example, through INSERT, LOAD DATA, and REPLACE statements.\n* UPDATE: The trigger is activated whenever a row is modified; for example,\nthrough UPDATE statements.\n* DELETE: The trigger is activated whenever a row is deleted from the table;\nfor example, through DELETE and REPLACE statements. However, DROP TABLE and\nTRUNCATE statements on the table do not activate this trigger, because they do\nnot use DELETE. Dropping a partition does not activate DELETE triggers, either.\n\nFOLLOWS/PRECEDES other_trigger_name\n-----------------------------------\n\nMariaDB starting with 10.2.3\n----------------------------\nThe FOLLOWS other_trigger_name and PRECEDES other_trigger_name options were\nadded in MariaDB 10.2.3 as part of supporting multiple triggers per action\ntime. This is the same syntax used by MySQL 5.7, although MySQL 5.7 does not\nhave multi-trigger support.\n\nFOLLOWS adds the new trigger after another trigger while PRECEDES adds the new\ntrigger before another trigger. If neither option is used, the new trigger is\nadded last for the given action and time.\n\nFOLLOWS and PRECEDES are not stored in the trigger definition. However the\ntrigger order is guaranteed to not change over time. mariadb-dump/mysqldump\nand other backup methods will not change trigger order. You can verify the\ntrigger order from the ACTION_ORDER column in INFORMATION_SCHEMA.TRIGGERS\ntable.\n\nSELECT trigger_name, action_order FROM information_schema.triggers \n WHERE event_object_table=\'t1\';\n\nAtomic DDL\n----------\n\nMariaDB starting with 10.6.1\n----------------------------\nMariaDB 10.6.1 supports Atomic DDL and CREATE TRIGGER is atomic.\n\nExamples\n--------\n\nCREATE DEFINER=`root`@`localhost` TRIGGER increment_animal\n AFTER INSERT ON animals FOR EACH ROW\n UPDATE animal_count SET animal_count.animals = animal_count.animals+1;\n\nOR REPLACE and IF NOT EXISTS\n\nCREATE DEFINER=`root`@`localhost` TRIGGER increment_animal\n AFTER INSERT ON animals FOR EACH ROW\n UPDATE animal_count SET animal_count.animals = animal_count.animals+1;\nERROR 1359 (HY000): Trigger already exists\n\nCREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER increment_animal\n AFTER INSERT ON animals FOR EACH ROW\n UPDATE animal_count SET animal_count.animals = animal_count.animals+1;\nQuery OK, 0 rows affected (0.12 sec)\n\nCREATE DEFINER=`root`@`localhost` TRIGGER IF NOT EXISTS increment_animal\n AFTER INSERT ON animals FOR EACH ROW\n UPDATE animal_count SET animal_count.animals = animal_count.animals+1;\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+------------------------+\n| Level | Code | Message |\n+-------+------+------------------------+\n| Note | 1359 | Trigger already exists |\n+-------+------+------------------------+\n1 row in set (0.00 sec)\n\nURL: https://mariadb.com/kb/en/create-trigger/','','https://mariadb.com/kb/en/create-trigger/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (714,38,'CREATE VIEW','Syntax\n------\n\nCREATE\n [OR REPLACE]\n [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]\n [DEFINER = { user | CURRENT_USER | role | CURRENT_ROLE }]\n [SQL SECURITY { DEFINER | INVOKER }]\n VIEW [IF NOT EXISTS] view_name [(column_list)]\n AS select_statement\n [WITH [CASCADED | LOCAL] CHECK OPTION]\n\nDescription\n-----------\n\nThe CREATE VIEW statement creates a new view, or replaces an existing one if\nthe OR REPLACE clause is given. If the view does not exist, CREATE OR REPLACE\nVIEW is the same as CREATE VIEW. If the view does exist, CREATE OR REPLACE\nVIEW is the same as ALTER VIEW.\n\nThe select_statement is a SELECT statement that provides the definition of the\nview. (When you select from the view, you select in effect using the SELECT\nstatement.) select_statement can select from base tables or other views.\n\nThe view definition is \"frozen\" at creation time, so changes to the underlying\ntables afterwards do not affect the view definition. For example, if a view is\ndefined as SELECT * on a table, new columns added to the table later do not\nbecome part of the view. A SHOW CREATE VIEW shows that such queries are\nrewritten and column names are included in the view definition.\n\nThe view definition must be a query that does not return errors at view\ncreation times. However, the base tables used by the views might be altered\nlater and the query may not be valid anymore. In this case, querying the view\nwill result in an error. CHECK TABLE helps in finding this kind of problems.\n\nThe ALGORITHM clause affects how MariaDB processes the view. The DEFINER and\nSQL SECURITY clauses specify the security context to be used when checking\naccess privileges at view invocation time. The WITH CHECK OPTION clause can be\ngiven to constrain inserts or updates to rows in tables referenced by the\nview. These clauses are described later in this section.\n\nThe CREATE VIEW statement requires the CREATE VIEW privilege for the view, and\nsome privilege for each column selected by the SELECT statement. For columns\nused elsewhere in the SELECT statement you must have the SELECT privilege. If\nthe OR REPLACE clause is present, you must also have the DROP privilege for\nthe view.\n\nA view belongs to a database. By default, a new view is created in the default\ndatabase. To create the view explicitly in a given database, specify the name\nas db_name.view_name when you create it.\n\nCREATE VIEW test.v AS SELECT * FROM t;\n\nBase tables and views share the same namespace within a database, so a\ndatabase cannot contain a base table and a view that have the same name.\n\nViews must have unique column names with no duplicates, just like base tables.\nBy default, the names of the columns retrieved by the SELECT statement are\nused for the view column names. To define explicit names for the view columns,\nthe optional column_list clause can be given as a list of comma-separated\nidentifiers. The number of names in column_list must be the same as the number\nof columns retrieved by the SELECT statement.\n\nMySQL until 5.1.28\n------------------\nPrior to MySQL 5.1.29, When you modify an existing view, the current view\ndefinition is backed up and saved. It is stored in that table\'s database\ndirectory, in a subdirectory named arc. The backup file for a view v is named\nv.frm-00001. If you alter the view again, the next backup is named\nv.frm-00002. The three latest view backup definitions are stored. Backed up\nview definitions are not preserved by mysqldump, or any other such programs,\nbut you can retain them using a file copy operation. However, they are not\nneeded for anything but to provide you with a backup of your previous view\ndefinition. It is safe to remove these backup definitions, but only while\nmysqld is not running. If you delete the arc subdirectory or its files while\nmysqld is running, you will receive an error the next time you try to alter\nthe view:\n\nMariaDB [test]> ALTER VIEW v AS SELECT * FROM t; \nERROR 6 (HY000): Error on delete of \'.\\test\\arc/v.frm-0004\' (Errcode: 2)\n\nColumns retrieved by the SELECT statement can be simple references to table\ncolumns. They can also be expressions that use functions, constant values,\noperators, and so forth.\n\nUnqualified table or view names in the SELECT statement are interpreted with\nrespect to the default database. A view can refer to tables or views in other\ndatabases by qualifying the table or view name with the proper database name.\n\nA view can be created from many kinds of SELECT statements. It can refer to\nbase tables or other views. It can use joins, UNION, and subqueries. The\nSELECT need not even refer to any tables. The following example defines a view\nthat selects two columns from another table, as well as an expression\ncalculated from those columns:\n\nCREATE TABLE t (qty INT, price INT);\n\nINSERT INTO t VALUES(3, 50);\n\nCREATE VIEW v AS SELECT qty, price, qty*price AS value FROM t;\n\nSELECT * FROM v;\n+------+-------+-------+\n| qty | price | value |\n+------+-------+-------+\n| 3 | 50 | 150 |\n+------+-------+-------+\n\nA view definition is subject to the following restrictions:\n\n* The SELECT statement cannot contain a subquery in the FROM clause.\n* The SELECT statement cannot refer to system or user variables.\n* Within a stored program, the definition cannot refer to program parameters\nor local variables.\n* The SELECT statement cannot refer to prepared statement parameters.\n* Any table or view referred to in the definition must exist. However, after a\nview has been created, it is possible to drop a table or view that the\ndefinition refers to. In this case, use of the view results in an error. To\ncheck a view definition for problems of this kind, use the CHECK TABLE\nstatement.\n* The definition cannot refer to a TEMPORARY table, and you cannot create a\nTEMPORARY view.\n* Any tables named in the view definition must exist at definition time.\n* You cannot associate a trigger with a view.\n* For valid identifiers to use as view names, see Identifier Names.\n\nORDER BY is allowed in a view definition, but it is ignored if you select from\na view using a statement that has its own ORDER BY.\n\nFor other options or clauses in the definition, they are added to the options\nor clauses of the statement that references the view, but the effect is\nundefined. For example, if a view definition includes a LIMIT clause, and you\nselect from the view using a statement that has its own LIMIT clause, it is\nundefined which limit applies. This same principle applies to options such as\nALL, DISTINCT, or SQL_SMALL_RESULT that follow the SELECT keyword, and to\nclauses such as INTO, FOR UPDATE, and LOCK IN SHARE MODE.\n\nThe PROCEDURE clause cannot be used in a view definition, and it cannot be\nused if a view is referenced in the FROM clause.\n\nIf you create a view and then change the query processing environment by\nchanging system variables, that may affect the results that you get from the\nview:\n\nCREATE VIEW v (mycol) AS SELECT \'abc\';\n\nSET sql_mode = \'\';\n\nSELECT \"mycol\" FROM v;\n+-------+\n| mycol |\n+-------+\n| mycol | \n+-------+\n\nSET sql_mode = \'ANSI_QUOTES\';\n\nSELECT \"mycol\" FROM v;\n+-------+\n| mycol |\n+-------+\n| abc | \n+-------+\n\nThe DEFINER and SQL SECURITY clauses determine which MariaDB account to use\nwhen checking access privileges for the view when a statement is executed that\nreferences the view. They were added in MySQL 5.1.2. The legal SQL SECURITY\ncharacteristic values are DEFINER and INVOKER. These indicate that the\nrequired privileges must be held by the user who defined or invoked the view,\nrespectively. The default SQL SECURITY value is DEFINER.\n\nIf a user value is given for the DEFINER clause, it should be a MariaDB\naccount in \'user_name\'@\'host_name\' format (the same format used in the GRANT\nstatement). The user_name and host_name values both are required. The definer\ncan also be given as CURRENT_USER or CURRENT_USER(). The default DEFINER value\nis the user who executes the CREATE VIEW statement. This is the same as\nspecifying DEFINER = CURRENT_USER explicitly.\n\nIf you specify the DEFINER clause, these rules determine the legal DEFINER\nuser values:\n\n* If you do not have the SUPER privilege, or, from MariaDB 10.5.2, the SET\nUSER privilege, the only legal user value is your own account, either\nspecified literally or by using CURRENT_USER. You cannot set the definer to\nsome other account.\n* If you have the SUPER privilege, or, from MariaDB 10.5.2, the SET USER\nprivilege, you can specify any syntactically legal account name. If the\naccount does not actually exist, a warning is generated.\n* If the SQL SECURITY value is DEFINER but the definer account does not exist\nwhen the view is referenced, an error occurs.\n\nWithin a view definition, CURRENT_USER returns the view\'s DEFINER value by\ndefault. For views defined with the SQL SECURITY INVOKER characteristic,\nCURRENT_USER returns the account for the view\'s invoker. For information about\nuser auditing within views, see\nhttp://dev.mysql.com/doc/refman/5.1/en/account-activity-auditing.html.\n\nWithin a stored routine that is defined with the SQL SECURITY DEFINER\ncharacteristic, CURRENT_USER returns the routine\'s DEFINER value. This also\naffects a view defined within such a program, if the view definition contains\na DEFINER value of CURRENT_USER.\n\nView privileges are checked like this:\n\n* At view definition time, the view creator must have the privileges needed to\nuse the top-level objects accessed by the view. For example, if the view\ndefinition refers to table columns, the creator must have privileges for the\ncolumns, as described previously. If the definition refers to a stored\nfunction, only the privileges needed to invoke the function can be checked.\nThe privileges required when the function runs can be checked only as it\nexecutes: For different invocations of the function, different execution paths\nwithin the function might be taken.\n* When a view is referenced, privileges for objects accessed by the view are\nchecked against the privileges held by the view creator or invoker, depending\non whether the SQL SECURITY characteristic is DEFINER or INVOKER, respectively.\n* If reference to a view causes execution of a stored function, privilege\nchecking for statements executed within the function depend on whether the\nfunction is defined with a SQL SECURITY characteristic of DEFINER or INVOKER.\nIf the security characteristic is DEFINER, the function runs with the\nprivileges of its creator. If the characteristic is INVOKER, the function runs\nwith the privileges determined by the view\'s SQL SECURITY characteristic.\n\nExample: A view might depend on a stored function, and that function might\ninvoke other stored routines. For example, the following view invokes a stored\nfunction f():\n\nCREATE VIEW v AS SELECT * FROM t WHERE t.id = f(t.name);\n\nSuppose that f() contains a statement such as this:\n\nIF name IS NULL then\n CALL p1();\nELSE\n CALL p2();\nEND IF;\n\nThe privileges required for executing statements within f() need to be checked\nwhen f() executes. This might mean that privileges are needed for p1() or\np2(), depending on the execution path within f(). Those privileges must be\nchecked at runtime, and the user who must possess the privileges is determined\nby the SQL SECURITY values of the view v and the function f().\n\nThe DEFINER and SQL SECURITY clauses for views are extensions to standard SQL.\nIn standard SQL, views are handled using the rules for SQL SECURITY INVOKER.\n\nIf you invoke a view that was created before MySQL 5.1.2, it is treated as\nthough it was created with a SQL SECURITY DEFINER clause and with a DEFINER\nvalue that is the same as your account. However, because the actual definer is\nunknown, MySQL issues a warning. To make the warning go away, it is sufficient\nto re-create the view so that the view definition includes a DEFINER clause.\n\nThe optional ALGORITHM clause is an extension to standard SQL. It affects how\nMariaDB processes the view. ALGORITHM takes three values: MERGE, TEMPTABLE, or\nUNDEFINED. The default algorithm is UNDEFINED if no ALGORITHM clause is\npresent. See View Algorithms for more information.\n\nSome views are updatable. That is, you can use them in statements such as\nUPDATE, DELETE, or INSERT to update the contents of the underlying table. For\na view to be updatable, there must be a one-to-one relationship between the\nrows in the view and the rows in the underlying table. There are also certain\nother constructs that make a view non-updatable. See Inserting and Updating\nwith Views.\n\nWITH CHECK OPTION\n-----------------\n\nThe WITH CHECK OPTION clause can be given for an updatable view to prevent\ninserts or updates to rows except those for which the WHERE clause in the\nselect_statement is true.\n\nIn a WITH CHECK OPTION clause for an updatable view, the LOCAL and CASCADED\nkeywords determine the scope of check testing when the view is defined in\nterms of another view. The LOCAL keyword restricts the CHECK OPTION only to\nthe view being defined. CASCADED causes the checks for underlying views to be\nevaluated as well. When neither keyword is given, the default is CASCADED.\n\nFor more information about updatable views and the WITH CHECK OPTION clause,\nsee Inserting and Updating with Views.\n\nIF NOT EXISTS\n-------------\n\nMariaDB starting with 10.1.3\n----------------------------\nThe IF NOT EXISTS clause was added in MariaDB 10.1.3\n\nWhen the IF NOT EXISTS clause is used, MariaDB will return a warning instead\nof an error if the specified view already exists. Cannot be used together with\nthe OR REPLACE clause.\n\nAtomic DDL\n----------\n\nMariaDB starting with 10.6.1\n----------------------------\nMariaDB 10.6.1 supports Atomic DDL and CREATE VIEW is atomic.\n\nExamples\n--------\n\nCREATE TABLE t (a INT, b INT) ENGINE = InnoDB;\n\nINSERT INTO t VALUES (1,1), (2,2), (3,3);\n\nCREATE VIEW v AS SELECT a, a*2 AS a2 FROM t;\n\nSELECT * FROM v;\n+------+------+\n| a | a2 |\n+------+------+\n| 1 | 2 |\n| 2 | 4 |\n| 3 | 6 |\n+------+------+\n\nOR REPLACE and IF NOT EXISTS:\n\nCREATE VIEW v AS SELECT a, a*2 AS a2 FROM t;\nERROR 1050 (42S01): Table \'v\' already exists\n\nCREATE OR REPLACE VIEW v AS SELECT a, a*2 AS a2 FROM t;\nQuery OK, 0 rows affected (0.04 sec)\n\nCREATE VIEW IF NOT EXISTS v AS SELECT a, a*2 AS a2 FROM t;\nQuery OK, 0 rows affected, 1 warning (0.01 sec)\n','','https://mariadb.com/kb/en/create-view/'); update help_topic set description = CONCAT(description, '\nSHOW WARNINGS;\n+-------+------+--------------------------+\n| Level | Code | Message |\n+-------+------+--------------------------+\n| Note | 1050 | Table \'v\' already exists |\n+-------+------+--------------------------+\n\nURL: https://mariadb.com/kb/en/create-view/') WHERE help_topic_id = 714; -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (715,38,'Generated (Virtual and Persistent/Stored) Columns','Syntax\n------\n\n [GENERATED ALWAYS] AS ( )\n[VIRTUAL | PERSISTENT | STORED] [UNIQUE] [UNIQUE KEY] [COMMENT ]\n\nMariaDB\'s generated columns syntax is designed to be similar to the syntax for\nMicrosoft SQL Server\'s computed columns and Oracle Database\'s virtual columns.\nIn MariaDB 10.2 and later, the syntax is also compatible with the syntax for\nMySQL\'s generated columns.\n\nDescription\n-----------\n\nA generated column is a column in a table that cannot explicitly be set to a\nspecific value in a DML query. Instead, its value is automatically generated\nbased on an expression. This expression might generate the value based on the\nvalues of other columns in the table, or it might generate the value by\ncalling built-in functions or user-defined functions (UDFs).\n\nThere are two types of generated columns:\n\n* PERSISTENT (a.k.a. STORED): This type\'s value is actually stored in the\ntable.\n* VIRTUAL: This type\'s value is not stored at all. Instead, the value is\ngenerated dynamically when the table is queried. This type is the default.\n\nGenerated columns are also sometimes called computed columns or virtual\ncolumns.\n\nSupported Features\n------------------\n\nStorage Engine Support\n----------------------\n\n* Generated columns can only be used with storage engines which support them.\nIf you try to use a storage engine that does not support them, then you will\nsee an error similar to the following:\n\nERROR 1910 (HY000): TokuDB storage engine does not support computed columns\n\n* InnoDB, Aria, MyISAM and CONNECT support generated columns.\n\n* A column in a MERGE table can be built on a PERSISTENT generated column.\nHowever, a column in a MERGE table can not be defined as a VIRTUAL and\nPERSISTENT generated column.\n\nData Type Support\n-----------------\n\n* All data types are supported when defining generated columns.\n\n* Using the ZEROFILL column option is supported when defining generated\ncolumns.\n\nMariaDB starting with 10.2.6\n----------------------------\nIn MariaDB 10.2.6 and later, the following statements apply to data types for\ngenerated columns:\n\n* Using the AUTO_INCREMENT column option is not supported when defining\ngenerated columns. Previously, it was supported, but this support was removed,\nbecause it would not work correctly. See MDEV-11117.\n\nIndex Support\n-------------\n\n* Using a generated column as a table\'s primary key is not supported. See\nMDEV-5590 for more information. If you try to use one as a primary key, then\nyou will see an error similar to the following:\n\nERROR 1903 (HY000): Primary key cannot be defined upon a computed column\n\n* Using PERSISTENT generated columns as part of a foreign key is supported.\n\n* Referencing PERSISTENT generated columns as part of a foreign key is also\nsupported.\nHowever, using the ON UPDATE CASCADE, ON UPDATE SET NULL, or ON DELETE SET\nNULL clauses is not supported. If you try to use an unsupported clause, then\nyou will see an error similar to the following:\n\nERROR 1905 (HY000): Cannot define foreign key with ON UPDATE SET NULL clause\non a computed column\n\nMariaDB starting with 10.2.3\n----------------------------\nIn MariaDB 10.2.3 and later, the following statements apply to indexes for\ngenerated columns:\n\n* Defining indexes on both VIRTUAL and PERSISTENT generated columns is\nsupported.\nIf an index is defined on a generated column, then the optimizer considers\nusing it in the same way as indexes based on \"real\" columns.\n\nMariaDB until 10.2.2\n--------------------\nIn MariaDB 10.2.2 and before, the following statements apply to indexes for\ngenerated columns:\n\n* Defining indexes on VIRTUAL generated columns is not supported.\n\n* Defining indexes on PERSISTENT generated columns is supported.\nIf an index is defined on a generated column, then the optimizer considers\nusing it in the same way as indexes based on \"real\" columns.\n\nStatement Support\n-----------------\n\n* Generated columns are used in DML queries just as if they were \"real\"\ncolumns.\nHowever, VIRTUAL and PERSISTENT generated columns differ in how their data is\nstored.\nValues for PERSISTENT generated columns are generated whenever a DML queries\ninserts or updates the row with the special DEFAULT value. This generates the\ncolumns value, and it is stored in the table like the other \"real\" columns.\nThis value can be read by other DML queries just like the other \"real\" columns.\nValues for VIRTUAL generated columns are not stored in the table. Instead, the\nvalue is generated dynamically whenever the column is queried. If other\ncolumns in a row are queried, but the VIRTUAL generated column is not one of\nthe queried columns, then the column\'s value is not generated.\n\n* The SELECT statement supports generated columns.\n\n* Generated columns can be referenced in the INSERT, UPDATE, and DELETE\nstatements.\nHowever, VIRTUAL or PERSISTENT generated columns cannot be explicitly set to\nany other values than NULL or DEFAULT. If a generated column is explicitly set\nto any other value, then the outcome depends on whether strict mode is enabled\nin sql_mode. If it is not enabled, then a warning will be raised and the\ndefault generated value will be used instead. If it is enabled, then an error\nwill be raised instead.\n\n* The CREATE TABLE statement has limited support for generated columns.\nIt supports defining generated columns in a new table.\nIt supports using generated columns to partition tables.\nIt does not support using the versioning clauses with generated columns.\n\n* The ALTER TABLE statement has limited support for generated columns.\nIt supports the MODIFY and CHANGE clauses for PERSISTENT generated columns.\nIt does not support the MODIFY clause for VIRTUAL generated columns if\nALGORITHM is not set to COPY. See MDEV-15476 for more information.\nIt does not support the CHANGE clause for VIRTUAL generated columns if\nALGORITHM is not set to COPY. See MDEV-17035 for more information.\nIt does not support altering a table if ALGORITHM is not set to COPY if the\ntable has a VIRTUAL generated column that is indexed. See MDEV-14046 for more\ninformation.\nIt does not support adding a VIRTUAL generated column with the ADD clause if\nthe same statement is also adding other columns if ALGORITHM is not set to\nCOPY. See MDEV-17468 for more information.\nIt also does not support altering an existing column into a VIRTUAL generated\ncolumn.\nIt supports using generated columns to partition tables.\nIt does not support using the versioning clauses with generated columns.\n\n* The SHOW CREATE TABLE statement supports generated columns.\n\n* The DESCRIBE statement can be used to check whether a table has generated\ncolumns.\nYou can tell which columns are generated by looking for the ones where the\nExtra column is set to either VIRTUAL or PERSISTENT. For example:\n\nDESCRIBE table1;\n+-------+-------------+------+-----+---------+------------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+-------------+------+-----+---------+------------+\n| a | int(11) | NO | | NULL | |\n| b | varchar(32) | YES | | NULL | |\n| c | int(11) | YES | | NULL | VIRTUAL |\n| d | varchar(5) | YES | | NULL | PERSISTENT |\n+-------+-------------+------+-----+---------+------------+\n\n* Generated columns can be properly referenced in the NEW and OLD rows in\ntriggers.\n\n* Stored procedures support generated columns.\n\n* The HANDLER statement supports generated columns.\n\nExpression Support\n------------------\n\n* Most legal, deterministic expressions which can be calculated are supported\nin expressions for generated columns.\n\n* Most built-in functions are supported in expressions for generated columns.\nHowever, some built-in functions can\'t be supported for technical reasons. For\nexample, If you try to use an unsupported function in an expression, an error\nis generated similar to the following:\n\nERROR 1901 (HY000): Function or expression \'dayname()\' cannot be used in the\nGENERATED ALWAYS AS clause of `v`\n\n* Subqueries are not supported in expressions for generated columns because\nthe underlying data can change.\n\n* Using anything that depends on data outside the row is not supported in\nexpressions for generated columns.\n\n* Stored functions are not supported in expressions for generated columns. See\nMDEV-17587 for more information.\n\nMariaDB starting with 10.2.1\n----------------------------\nIn MariaDB 10.2.1 and later, the following statements apply to expressions for\ngenerated columns:\n\n* Non-deterministic built-in functions are supported in expressions for not\nindexed VIRTUAL generated columns.\n\n* Non-deterministic built-in functions are not supported in expressions for\nPERSISTENT or indexed VIRTUAL generated columns.\n\n* User-defined functions (UDFs) are supported in expressions for generated\ncolumns.\nHowever, MariaDB can\'t check whether a UDF is deterministic, so it is up to\nthe user to be sure that they do not use non-deterministic UDFs with VIRTUAL\ngenerated columns.\n\n* Defining a generated column based on other generated columns defined before\nit in the table definition is supported. For example:\n\nCREATE TABLE t1 (a int as (1), b int as (a));\n\n* However, defining a generated column based on other generated columns\ndefined after in the table definition is not supported in expressions for\ngeneration columns because generated columns are calculated in the order they\nare defined.\n\n* Using an expression that exceeds 255 characters in length is supported in\nexpressions for generated columns. The new limit for the entire table\ndefinition, including all expressions for generated columns, is 65,535 bytes.\n\n* Using constant expressions is supported in expressions for generated\ncolumns. For example:\n\nCREATE TABLE t1 (a int as (1));\n\nMariaDB until 10.2.0\n--------------------\nIn MariaDB 10.2.0 and before, the following statements apply to expressions\nfor generated columns:\n\n* Non-deterministic built-in functions are not supported in expressions for\ngenerated columns.\n\n* User-defined functions (UDFs) are not supported in expressions for generated\ncolumns.\n\n* Defining a generated column based on other generated columns defined in the\ntable is not supported. Otherwise, it would generate errors like this:\n\nERROR 1900 (HY000): A computed column cannot be based on a computed column\n\n* Using an expression that exceeds 255 characters in length is not supported\nin expressions for generated columns.\n\n* Using constant expressions is not supported in expressions for generated\ncolumns. Otherwise, it would generate errors like this:\n\nERROR 1908 (HY000): Constant expression in computed column function is not\nallowed\n\nMaking Stored Values Consistent\n-------------------------------\n\nWhen a generated column is PERSISTENT or indexed, the value of the expression\nneeds to be consistent regardless of the SQL Mode flags in the current\nsession. If it is not, then the table will be seen as corrupted when the value\nthat should actually be returned by the computed expression and the value that\nwas previously stored and/or indexed using a different sql_mode setting\ndisagree.\n\nThere are currently two affected classes of inconsistencies: character padding\nand unsigned subtraction:\n\n* For a VARCHAR or TEXT generated column the length of the value returned can\nvary depending on the PAD_CHAR_TO_FULL_LENGTH sql_mode flag. To make the\nvalue consistent, create the generated column using an RTRIM() or RPAD()\nfunction. Alternately, create the generated column as a CHAR column so that\nits data is always fully padded.\n\n* If a SIGNED generated column is based on the subtraction of an UNSIGNED\nvalue, the resulting value can vary depending on how large the value is and\nthe NO_UNSIGNED_SUBTRACTION sql_mode flag. To make the value consistent, use\nCAST() to ensure that each UNSIGNED operand is SIGNED before the subtraction.\n\nMariaDB starting with 10.5\n--------------------------\nBeginning in MariaDB 10.5, there is a fatal error generated when trying to\ncreate a generated column whose value can change depending on the SQL Mode\nwhen its data is PERSISTENT or indexed.\n\nFor an existing generated column that has a potentially inconsistent value, a\nwarning about a bad expression is generated the first time it is used (if\nwarnings are enabled).\n\nBeginning in MariaDB 10.4.8, MariaDB 10.3.18, and MariaDB 10.2.27 a\npotentially inconsistent generated column outputs a warning when created or\nfirst used (without restricting their creation).\n\nHere is an example of two tables that would be rejected in MariaDB 10.5 and\nwarned about in the other listed versions:\n\nCREATE TABLE bad_pad (\n txt CHAR(5),\n -- CHAR -> VARCHAR or CHAR -> TEXT can\'t be persistent or indexed:\n vtxt VARCHAR(5) AS (txt) PERSISTENT\n);\n\nCREATE TABLE bad_sub (\n num1 BIGINT UNSIGNED,\n num2 BIGINT UNSIGNED,\n -- The resulting value can vary for some large values\n vnum BIGINT AS (num1 - num2) VIRTUAL,\n KEY(vnum)\n);\n\nThe warnings for the above tables look like this:\n\nWarning (Code 1901): Function or expression \'`txt`\' cannot be used in the\nGENERATED ALWAYS AS clause of `vtxt`\nWarning (Code 1105): Expression depends on the @@sql_mode value\nPAD_CHAR_TO_FULL_LENGTH\n\nWarning (Code 1901): Function or expression \'`num1` - `num2`\' cannot be used\nin the GENERATED ALWAYS AS clause of `vnum`\nWarning (Code 1105): Expression depends on the @@sql_mode value\nNO_UNSIGNED_SUBTRACTION\n\nTo work around the issue, force the padding or type to make the generated\ncolumn\'s expression return a consistent value. For example:\n\nCREATE TABLE good_pad (\n txt CHAR(5),\n -- Using RTRIM() or RPAD() makes the value consistent:\n vtxt VARCHAR(5) AS (RTRIM(txt)) PERSISTENT,\n -- When not persistent or indexed, it is OK for the value to vary by mode:\n vtxt2 VARCHAR(5) AS (txt) VIRTUAL,\n -- CHAR -> CHAR is always OK:\n txt2 CHAR(5) AS (txt) PERSISTENT\n);\n\nCREATE TABLE good_sub (\n num1 BIGINT UNSIGNED,\n num2 BIGINT UNSIGNED,\n -- The indexed value will always be consistent in this expression:\n vnum BIGINT AS (CAST(num1 AS SIGNED) - CAST(num2 AS SIGNED)) VIRTUAL,\n KEY(vnum)\n);\n\nMySQL Compatibility Support\n---------------------------\n\nMariaDB starting with 10.2.1\n----------------------------\nIn MariaDB 10.2.1 and later, the following statements apply to MySQL','','https://mariadb.com/kb/en/generated-columns/'); -update help_topic set description = CONCAT(description, '\ncompatibility for generated columns:\n\n* The STORED keyword is supported as an alias for the PERSISTENT keyword.\n\n* Tables created with MySQL 5.7 or later that contain MySQL\'s generated\ncolumns can be imported into MariaDB without a dump and restore.\n\nMariaDB until 10.2.0\n--------------------\nIn MariaDB 10.2.0 and before, the following statements apply to MySQL\ncompatibility for generated columns:\n\n* The STORED keyword is not supported as an alias for the PERSISTENT keyword.\n\n* Tables created with MySQL 5.7 or later that contain MySQL\'s generated\ncolumns can not be imported into MariaDB without a dump and restore.\n\nImplementation Differences\n--------------------------\n\nGenerated columns are subject to various constraints in other DBMSs that are\nnot present in MariaDB\'s implementation. Generated columns may also be called\ncomputed columns or virtual columns in different implementations. The various\ndetails for a specific implementation can be found in the documentation for\neach specific DBMS.\n\nImplementation Differences Compared to Microsoft SQL Server\n-----------------------------------------------------------\n\nMariaDB\'s generated columns implementation does not enforce the following\nrestrictions that are present in Microsoft SQL Server\'s computed columns\nimplementation:\n\n* MariaDB allows server variables in generated column expressions, including\nthose that change dynamically, such as warning_count.\n* MariaDB allows the CONVERT_TZ() function to be called with a named time zone\nas an argument, even though time zone names and time offsets are configurable.\n* MariaDB allows the CAST() function to be used with non-unicode character\nsets, even though character sets are configurable and differ between\nbinaries/versions.\n* MariaDB allows FLOAT expressions to be used in generated columns. Microsoft\nSQL Server considers these expressions to be \"imprecise\" due to potential\ncross-platform differences in floating-point implementations and precision.\n* Microsoft SQL Server requires the ARITHABORT mode to be set, so that\ndivision by zero returns an error, and not a NULL.\n* Microsoft SQL Server requires QUOTED_IDENTIFIER to be set in sql_mode. In\nMariaDB, if data is inserted without ANSI_QUOTES set in sql_mode, then it will\nbe processed and stored differently in a generated column that contains quoted\nidentifiers.\n* In MariaDB 10.2.0 and before, it does not allow user-defined functions\n(UDFs) to be used in expressions for generated columns.\n\nMicrosoft SQL Server enforces the above restrictions by doing one of the\nfollowing things:\n\n* Refusing to create computed columns.\n* Refusing to allow updates to a table containing them.\n* Refusing to use an index over such a column if it can not be guaranteed that\nthe expression is fully deterministic.\n\nIn MariaDB, as long as the sql_mode, language, and other settings that were in\neffect during the CREATE TABLE remain unchanged, the generated column\nexpression will always be evaluated the same. If any of these things change,\nthen please be aware that the generated column expression might not be\nevaluated the same way as it previously was.\n\nIn MariaDB 5.2, you will get a warning if you try to update a virtual column.\nIn MariaDB 5.3 and later, this warning will be converted to an error if strict\nmode is enabled in sql_mode.\n\nDevelopment History\n-------------------\n\nGenerated columns was originally developed by Andrey Zhakov. It was then\nmodified by Sanja Byelkin and Igor Babaev at Monty Program for inclusion in\nMariaDB. Monty did the work on MariaDB 10.2 to lift a some of the old\nlimitations.\n\nExamples\n--------\n\nHere is an example table that uses both VIRTUAL and PERSISTENT virtual columns:\n\nUSE TEST;\n\nCREATE TABLE table1 (\n a INT NOT NULL,\n b VARCHAR(32),\n c INT AS (a mod 10) VIRTUAL,\n d VARCHAR(5) AS (left(b,5)) PERSISTENT);\n\nIf you describe the table, you can easily see which columns are virtual by\nlooking in the \"Extra\" column:\n\nDESCRIBE table1;\n+-------+-------------+------+-----+---------+------------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+-------------+------+-----+---------+------------+\n| a | int(11) | NO | | NULL | |\n| b | varchar(32) | YES | | NULL | |\n| c | int(11) | YES | | NULL | VIRTUAL |\n| d | varchar(5) | YES | | NULL | PERSISTENT |\n+-------+-------------+------+-----+---------+------------+\n\nTo find out what function(s) generate the value of the virtual column you can\nuse SHOW CREATE TABLE:\n\nSHOW CREATE TABLE table1;\n\n| table1 | CREATE TABLE `table1` (\n `a` int(11) NOT NULL,\n `b` varchar(32) DEFAULT NULL,\n `c` int(11) AS (a mod 10) VIRTUAL,\n `d` varchar(5) AS (left(b,5)) PERSISTENT\n) ENGINE=MyISAM DEFAULT CHARSET=latin1 |\n\nIf you try to insert non-default values into a virtual column, you will\nreceive a warning and what you tried to insert will be ignored and the derived\nvalue inserted instead:\n\nWARNINGS;\nShow warnings enabled.\n\nINSERT INTO table1 VALUES (1, \'some text\',default,default);\nQuery OK, 1 row affected (0.00 sec)\n\nINSERT INTO table1 VALUES (2, \'more text\',5,default);\nQuery OK, 1 row affected, 1 warning (0.00 sec)\n\nWarning (Code 1645): The value specified for computed column \'c\' in table\n\'table1\' has been ignored.\n\nINSERT INTO table1 VALUES (123, \'even more text\',default,\'something\');\nQuery OK, 1 row affected, 2 warnings (0.00 sec)\n\nWarning (Code 1645): The value specified for computed column \'d\' in table\n\'table1\' has been ignored.\nWarning (Code 1265): Data truncated for column \'d\' at row 1\n\nSELECT * FROM table1;\n+-----+----------------+------+-------+\n| a | b | c | d |\n+-----+----------------+------+-------+\n| 1 | some text | 1 | some |\n| 2 | more text | 2 | more |\n| 123 | even more text | 3 | even |\n+-----+----------------+------+-------+\n3 rows in set (0.00 sec)\n\nIf the ZEROFILL clause is specified, it should be placed directly after the\ntype definition, before the AS ():\n\nCREATE TABLE table2 (a INT, b INT ZEROFILL AS (a*2) VIRTUAL);\nINSERT INTO table2 (a) VALUES (1);\n\nSELECT * FROM table2;\n+------+------------+\n| a | b |\n+------+------------+\n| 1 | 0000000002 |\n+------+------------+\n1 row in set (0.00 sec)\n\nYou can also use virtual columns to implement a \"poor man\'s partial index\".\nSee example at the end of Unique Index.\n\nURL: https://mariadb.com/kb/en/generated-columns/') WHERE help_topic_id = 715; +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (715,38,'Generated (Virtual and Persistent/Stored) Columns','Syntax\n------\n\n [GENERATED ALWAYS] AS ( )\n[VIRTUAL | PERSISTENT | STORED] [UNIQUE] [UNIQUE KEY] [COMMENT ]\n\nMariaDB\'s generated columns syntax is designed to be similar to the syntax for\nMicrosoft SQL Server\'s computed columns and Oracle Database\'s virtual columns.\nIn MariaDB 10.2 and later, the syntax is also compatible with the syntax for\nMySQL\'s generated columns.\n\nDescription\n-----------\n\nA generated column is a column in a table that cannot explicitly be set to a\nspecific value in a DML query. Instead, its value is automatically generated\nbased on an expression. This expression might generate the value based on the\nvalues of other columns in the table, or it might generate the value by\ncalling built-in functions or user-defined functions (UDFs).\n\nThere are two types of generated columns:\n\n* PERSISTENT (a.k.a. STORED): This type\'s value is actually stored in the\ntable.\n* VIRTUAL: This type\'s value is not stored at all. Instead, the value is\ngenerated dynamically when the table is queried. This type is the default.\n\nGenerated columns are also sometimes called computed columns or virtual\ncolumns.\n\nSupported Features\n------------------\n\nStorage Engine Support\n----------------------\n\n* Generated columns can only be used with storage engines which support them.\nIf you try to use a storage engine that does not support them, then you will\nsee an error similar to the following:\n\nERROR 1910 (HY000): TokuDB storage engine does not support computed columns\n\n* InnoDB, Aria, MyISAM and CONNECT support generated columns.\n\n* A column in a MERGE table can be built on a PERSISTENT generated column.\nHowever, a column in a MERGE table can not be defined as a VIRTUAL and\nPERSISTENT generated column.\n\nData Type Support\n-----------------\n\n* All data types are supported when defining generated columns.\n\n* Using the ZEROFILL column option is supported when defining generated\ncolumns.\n\n* Using the AUTO_INCREMENT column option is not supported when defining\ngenerated columns. Until MariaDB 10.2.25, it was supported, but this support\nwas removed, because it would not work correctly. See MDEV-11117.\n\nIndex Support\n-------------\n\n* Using a generated column as a table\'s primary key is not supported. See\nMDEV-5590 for more information. If you try to use one as a primary key, then\nyou will see an error similar to the following:\n\nERROR 1903 (HY000): Primary key cannot be defined upon a computed column\n\n* Using PERSISTENT generated columns as part of a foreign key is supported.\n\n* Referencing PERSISTENT generated columns as part of a foreign key is also\nsupported.\nHowever, using the ON UPDATE CASCADE, ON UPDATE SET NULL, or ON DELETE SET\nNULL clauses is not supported. If you try to use an unsupported clause, then\nyou will see an error similar to the following:\n\nERROR 1905 (HY000): Cannot define foreign key with ON UPDATE SET NULL clause\non a computed column\n\n* Defining indexes on both VIRTUAL and PERSISTENT generated columns is\nsupported.\nIf an index is defined on a generated column, then the optimizer considers\nusing it in the same way as indexes based on \"real\" columns.\n\nStatement Support\n-----------------\n\n* Generated columns are used in DML queries just as if they were \"real\"\ncolumns.\nHowever, VIRTUAL and PERSISTENT generated columns differ in how their data is\nstored.\nValues for PERSISTENT generated columns are generated whenever a DML queries\ninserts or updates the row with the special DEFAULT value. This generates the\ncolumns value, and it is stored in the table like the other \"real\" columns.\nThis value can be read by other DML queries just like the other \"real\" columns.\nValues for VIRTUAL generated columns are not stored in the table. Instead, the\nvalue is generated dynamically whenever the column is queried. If other\ncolumns in a row are queried, but the VIRTUAL generated column is not one of\nthe queried columns, then the column\'s value is not generated.\n\n* The SELECT statement supports generated columns.\n\n* Generated columns can be referenced in the INSERT, UPDATE, and DELETE\nstatements.\nHowever, VIRTUAL or PERSISTENT generated columns cannot be explicitly set to\nany other values than NULL or DEFAULT. If a generated column is explicitly set\nto any other value, then the outcome depends on whether strict mode is enabled\nin sql_mode. If it is not enabled, then a warning will be raised and the\ndefault generated value will be used instead. If it is enabled, then an error\nwill be raised instead.\n\n* The CREATE TABLE statement has limited support for generated columns.\nIt supports defining generated columns in a new table.\nIt supports using generated columns to partition tables.\nIt does not support using the versioning clauses with generated columns.\n\n* The ALTER TABLE statement has limited support for generated columns.\nIt supports the MODIFY and CHANGE clauses for PERSISTENT generated columns.\nIt does not support the MODIFY clause for VIRTUAL generated columns if\nALGORITHM is not set to COPY. See MDEV-15476 for more information.\nIt does not support the CHANGE clause for VIRTUAL generated columns if\nALGORITHM is not set to COPY. See MDEV-17035 for more information.\nIt does not support altering a table if ALGORITHM is not set to COPY if the\ntable has a VIRTUAL generated column that is indexed. See MDEV-14046 for more\ninformation.\nIt does not support adding a VIRTUAL generated column with the ADD clause if\nthe same statement is also adding other columns if ALGORITHM is not set to\nCOPY. See MDEV-17468 for more information.\nIt also does not support altering an existing column into a VIRTUAL generated\ncolumn.\nIt supports using generated columns to partition tables.\nIt does not support using the versioning clauses with generated columns.\n\n* The SHOW CREATE TABLE statement supports generated columns.\n\n* The DESCRIBE statement can be used to check whether a table has generated\ncolumns.\nYou can tell which columns are generated by looking for the ones where the\nExtra column is set to either VIRTUAL or PERSISTENT. For example:\n\nDESCRIBE table1;\n+-------+-------------+------+-----+---------+------------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+-------------+------+-----+---------+------------+\n| a | int(11) | NO | | NULL | |\n| b | varchar(32) | YES | | NULL | |\n| c | int(11) | YES | | NULL | VIRTUAL |\n| d | varchar(5) | YES | | NULL | PERSISTENT |\n+-------+-------------+------+-----+---------+------------+\n\n* Generated columns can be properly referenced in the NEW and OLD rows in\ntriggers.\n\n* Stored procedures support generated columns.\n\n* The HANDLER statement supports generated columns.\n\nExpression Support\n------------------\n\n* Most legal, deterministic expressions which can be calculated are supported\nin expressions for generated columns.\n\n* Most built-in functions are supported in expressions for generated columns.\nHowever, some built-in functions can\'t be supported for technical reasons. For\nexample, If you try to use an unsupported function in an expression, an error\nis generated similar to the following:\n\nERROR 1901 (HY000): Function or expression \'dayname()\' cannot be used in the\nGENERATED ALWAYS AS clause of `v`\n\n* Subqueries are not supported in expressions for generated columns because\nthe underlying data can change.\n\n* Using anything that depends on data outside the row is not supported in\nexpressions for generated columns.\n\n* Stored functions are not supported in expressions for generated columns. See\nMDEV-17587 for more information.\n\n* Non-deterministic built-in functions are supported in expressions for not\nindexed VIRTUAL generated columns.\n\n* Non-deterministic built-in functions are not supported in expressions for\nPERSISTENT or indexed VIRTUAL generated columns.\n\n* User-defined functions (UDFs) are supported in expressions for generated\ncolumns.\nHowever, MariaDB can\'t check whether a UDF is deterministic, so it is up to\nthe user to be sure that they do not use non-deterministic UDFs with VIRTUAL\ngenerated columns.\n\n* Defining a generated column based on other generated columns defined before\nit in the table definition is supported. For example:\n\nCREATE TABLE t1 (a int as (1), b int as (a));\n\n* However, defining a generated column based on other generated columns\ndefined after in the table definition is not supported in expressions for\ngeneration columns because generated columns are calculated in the order they\nare defined.\n\n* Using an expression that exceeds 255 characters in length is supported in\nexpressions for generated columns. The new limit for the entire table\ndefinition, including all expressions for generated columns, is 65,535 bytes.\n\n* Using constant expressions is supported in expressions for generated\ncolumns. For example:\n\nCREATE TABLE t1 (a int as (1));\n\nMaking Stored Values Consistent\n-------------------------------\n\nWhen a generated column is PERSISTENT or indexed, the value of the expression\nneeds to be consistent regardless of the SQL Mode flags in the current\nsession. If it is not, then the table will be seen as corrupted when the value\nthat should actually be returned by the computed expression and the value that\nwas previously stored and/or indexed using a different sql_mode setting\ndisagree.\n\nThere are currently two affected classes of inconsistencies: character padding\nand unsigned subtraction:\n\n* For a VARCHAR or TEXT generated column the length of the value returned can\nvary depending on the PAD_CHAR_TO_FULL_LENGTH sql_mode flag. To make the\nvalue consistent, create the generated column using an RTRIM() or RPAD()\nfunction. Alternately, create the generated column as a CHAR column so that\nits data is always fully padded.\n\n* If a SIGNED generated column is based on the subtraction of an UNSIGNED\nvalue, the resulting value can vary depending on how large the value is and\nthe NO_UNSIGNED_SUBTRACTION sql_mode flag. To make the value consistent, use\nCAST() to ensure that each UNSIGNED operand is SIGNED before the subtraction.\n\nMariaDB starting with 10.5\n--------------------------\nBeginning in MariaDB 10.5, there is a fatal error generated when trying to\ncreate a generated column whose value can change depending on the SQL Mode\nwhen its data is PERSISTENT or indexed.\n\nFor an existing generated column that has a potentially inconsistent value, a\nwarning about a bad expression is generated the first time it is used (if\nwarnings are enabled).\n\nBeginning in MariaDB 10.4.8, MariaDB 10.3.18, and MariaDB 10.2.27 a\npotentially inconsistent generated column outputs a warning when created or\nfirst used (without restricting their creation).\n\nHere is an example of two tables that would be rejected in MariaDB 10.5 and\nwarned about in the other listed versions:\n\nCREATE TABLE bad_pad (\n txt CHAR(5),\n -- CHAR -> VARCHAR or CHAR -> TEXT can\'t be persistent or indexed:\n vtxt VARCHAR(5) AS (txt) PERSISTENT\n);\n\nCREATE TABLE bad_sub (\n num1 BIGINT UNSIGNED,\n num2 BIGINT UNSIGNED,\n -- The resulting value can vary for some large values\n vnum BIGINT AS (num1 - num2) VIRTUAL,\n KEY(vnum)\n);\n\nThe warnings for the above tables look like this:\n\nWarning (Code 1901): Function or expression \'`txt`\' cannot be used in the\nGENERATED ALWAYS AS clause of `vtxt`\nWarning (Code 1105): Expression depends on the @@sql_mode value\nPAD_CHAR_TO_FULL_LENGTH\n\nWarning (Code 1901): Function or expression \'`num1` - `num2`\' cannot be used\nin the GENERATED ALWAYS AS clause of `vnum`\nWarning (Code 1105): Expression depends on the @@sql_mode value\nNO_UNSIGNED_SUBTRACTION\n\nTo work around the issue, force the padding or type to make the generated\ncolumn\'s expression return a consistent value. For example:\n\nCREATE TABLE good_pad (\n txt CHAR(5),\n -- Using RTRIM() or RPAD() makes the value consistent:\n vtxt VARCHAR(5) AS (RTRIM(txt)) PERSISTENT,\n -- When not persistent or indexed, it is OK for the value to vary by mode:\n vtxt2 VARCHAR(5) AS (txt) VIRTUAL,\n -- CHAR -> CHAR is always OK:\n txt2 CHAR(5) AS (txt) PERSISTENT\n);\n\nCREATE TABLE good_sub (\n num1 BIGINT UNSIGNED,\n num2 BIGINT UNSIGNED,\n -- The indexed value will always be consistent in this expression:\n vnum BIGINT AS (CAST(num1 AS SIGNED) - CAST(num2 AS SIGNED)) VIRTUAL,\n KEY(vnum)\n);\n\nMySQL Compatibility Support\n---------------------------\n\n* The STORED keyword is supported as an alias for the PERSISTENT keyword.\n\n* Tables created with MySQL 5.7 or later that contain MySQL\'s generated\ncolumns can be imported into MariaDB without a dump and restore.\n\nImplementation Differences\n--------------------------\n\nGenerated columns are subject to various constraints in other DBMSs that are\nnot present in MariaDB\'s implementation. Generated columns may also be called\ncomputed columns or virtual columns in different implementations. The various\ndetails for a specific implementation can be found in the documentation for\neach specific DBMS.\n\nImplementation Differences Compared to Microsoft SQL Server\n-----------------------------------------------------------\n\nMariaDB\'s generated columns implementation does not enforce the following\nrestrictions that are present in Microsoft SQL Server\'s computed columns\nimplementation:\n\n* MariaDB allows server variables in generated column expressions, including\nthose that change dynamically, such as warning_count.\n* MariaDB allows the CONVERT_TZ() function to be called with a named time zone\nas an argument, even though time zone names and time offsets are configurable.\n* MariaDB allows the CAST() function to be used with non-unicode character\nsets, even though character sets are configurable and differ between\nbinaries/versions.\n* MariaDB allows FLOAT expressions to be used in generated columns. Microsoft\nSQL Server considers these expressions to be \"imprecise\" due to potential\ncross-platform differences in floating-point implementations and precision.\n* Microsoft SQL Server requires the ARITHABORT mode to be set, so that\ndivision by zero returns an error, and not a NULL.\n* Microsoft SQL Server requires QUOTED_IDENTIFIER to be set in sql_mode. In\nMariaDB, if data is inserted without ANSI_QUOTES set in sql_mode, then it will','','https://mariadb.com/kb/en/generated-columns/'); +update help_topic set description = CONCAT(description, '\nbe processed and stored differently in a generated column that contains quoted\nidentifiers.\n\nMicrosoft SQL Server enforces the above restrictions by doing one of the\nfollowing things:\n\n* Refusing to create computed columns.\n* Refusing to allow updates to a table containing them.\n* Refusing to use an index over such a column if it can not be guaranteed that\nthe expression is fully deterministic.\n\nIn MariaDB, as long as the sql_mode, language, and other settings that were in\neffect during the CREATE TABLE remain unchanged, the generated column\nexpression will always be evaluated the same. If any of these things change,\nthen please be aware that the generated column expression might not be\nevaluated the same way as it previously was.\n\nIf you try to update a virtual column, you will get an error if the default\nstrict mode is enabled in sql_mode, or a warning otherwise.\n\nDevelopment History\n-------------------\n\nGenerated columns was originally developed by Andrey Zhakov. It was then\nmodified by Sanja Byelkin and Igor Babaev at Monty Program for inclusion in\nMariaDB. Monty did the work on MariaDB 10.2 to lift a some of the old\nlimitations.\n\nExamples\n--------\n\nHere is an example table that uses both VIRTUAL and PERSISTENT virtual columns:\n\nUSE TEST;\n\nCREATE TABLE table1 (\n a INT NOT NULL,\n b VARCHAR(32),\n c INT AS (a mod 10) VIRTUAL,\n d VARCHAR(5) AS (left(b,5)) PERSISTENT);\n\nIf you describe the table, you can easily see which columns are virtual by\nlooking in the \"Extra\" column:\n\nDESCRIBE table1;\n+-------+-------------+------+-----+---------+------------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+-------------+------+-----+---------+------------+\n| a | int(11) | NO | | NULL | |\n| b | varchar(32) | YES | | NULL | |\n| c | int(11) | YES | | NULL | VIRTUAL |\n| d | varchar(5) | YES | | NULL | PERSISTENT |\n+-------+-------------+------+-----+---------+------------+\n\nTo find out what function(s) generate the value of the virtual column you can\nuse SHOW CREATE TABLE:\n\nSHOW CREATE TABLE table1;\n\n| table1 | CREATE TABLE `table1` (\n `a` int(11) NOT NULL,\n `b` varchar(32) DEFAULT NULL,\n `c` int(11) AS (a mod 10) VIRTUAL,\n `d` varchar(5) AS (left(b,5)) PERSISTENT\n) ENGINE=MyISAM DEFAULT CHARSET=latin1 |\n\nIf you try to insert non-default values into a virtual column, you will\nreceive a warning and what you tried to insert will be ignored and the derived\nvalue inserted instead:\n\nWARNINGS;\nShow warnings enabled.\n\nINSERT INTO table1 VALUES (1, \'some text\',default,default);\nQuery OK, 1 row affected (0.00 sec)\n\nINSERT INTO table1 VALUES (2, \'more text\',5,default);\nQuery OK, 1 row affected, 1 warning (0.00 sec)\n\nWarning (Code 1645): The value specified for computed column \'c\' in table\n\'table1\' has been ignored.\n\nINSERT INTO table1 VALUES (123, \'even more text\',default,\'something\');\nQuery OK, 1 row affected, 2 warnings (0.00 sec)\n\nWarning (Code 1645): The value specified for computed column \'d\' in table\n\'table1\' has been ignored.\nWarning (Code 1265): Data truncated for column \'d\' at row 1\n\nSELECT * FROM table1;\n+-----+----------------+------+-------+\n| a | b | c | d |\n+-----+----------------+------+-------+\n| 1 | some text | 1 | some |\n| 2 | more text | 2 | more |\n| 123 | even more text | 3 | even |\n+-----+----------------+------+-------+\n3 rows in set (0.00 sec)\n\nIf the ZEROFILL clause is specified, it should be placed directly after the\ntype definition, before the AS ():\n\nCREATE TABLE table2 (a INT, b INT ZEROFILL AS (a*2) VIRTUAL);\nINSERT INTO table2 (a) VALUES (1);\n\nSELECT * FROM table2;\n+------+------------+\n| a | b |\n+------+------------+\n| 1 | 0000000002 |\n+------+------------+\n1 row in set (0.00 sec)\n\nYou can also use virtual columns to implement a \"poor man\'s partial index\".\nSee example at the end of Unique Index.\n\nURL: https://mariadb.com/kb/en/generated-columns/') WHERE help_topic_id = 715; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (716,38,'Invisible Columns','MariaDB starting with 10.3.3\n----------------------------\nInvisible columns (sometimes also called hidden columns) first appeared in\nMariaDB 10.3.3.\n\nColumns can be given an INVISIBLE attribute in a CREATE TABLE or ALTER TABLE\nstatement. These columns will then not be listed in the results of a SELECT *\nstatement, nor do they need to be assigned a value in an INSERT statement,\nunless INSERT explicitly mentions them by name.\n\nSince SELECT * does not return the invisible columns, new tables or views\ncreated in this manner will have no trace of the invisible columns. If\nspecifically referenced in the SELECT statement, the columns will be brought\ninto the view/new table, but the INVISIBLE attribute will not.\n\nInvisible columns can be declared as NOT NULL, but then require a DEFAULT\nvalue.\n\nIt is not possible for all columns in a table to be invisible.\n\nExamples\n--------\n\nCREATE TABLE t (x INT INVISIBLE);\nERROR 1113 (42000): A table must have at least 1 column\n\nCREATE TABLE t (x INT, y INT INVISIBLE, z INT INVISIBLE NOT NULL);\nERROR 4106 (HY000): Invisible column `z` must have a default value\n\nCREATE TABLE t (x INT, y INT INVISIBLE, z INT INVISIBLE NOT NULL DEFAULT 4);\n\nINSERT INTO t VALUES (1),(2);\n\nINSERT INTO t (x,y) VALUES (3,33);\n\nSELECT * FROM t;\n+------+\n| x |\n+------+\n| 1 |\n| 2 |\n| 3 |\n+------+\n\nSELECT x,y,z FROM t;\n+------+------+---+\n| x | y | z |\n+------+------+---+\n| 1 | NULL | 4 |\n| 2 | NULL | 4 |\n| 3 | 33 | 4 |\n+------+------+---+\n\nDESC t;\n+-------+---------+------+-----+---------+-----------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+---------+------+-----+---------+-----------+\n| x | int(11) | YES | | NULL | |\n| y | int(11) | YES | | NULL | INVISIBLE |\n| z | int(11) | NO | | 4 | INVISIBLE |\n+-------+---------+------+-----+---------+-----------+\n\nALTER TABLE t MODIFY x INT INVISIBLE, MODIFY y INT, MODIFY z INT NOT NULL\nDEFAULT 4;\n\nDESC t;\n+-------+---------+------+-----+---------+-----------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+---------+------+-----+---------+-----------+\n| x | int(11) | YES | | NULL | INVISIBLE |\n| y | int(11) | YES | | NULL | |\n| z | int(11) | NO | | 4 | |\n+-------+---------+------+-----+---------+-----------+\n\nCreating a view from a table with hidden columns:\n\nCREATE VIEW v1 AS SELECT * FROM t;\n\nDESC v1;\n+-------+---------+------+-----+---------+-------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+---------+------+-----+---------+-------+\n| y | int(11) | YES | | NULL | |\n| z | int(11) | NO | | 4 | |\n+-------+---------+------+-----+---------+-------+\n\nCREATE VIEW v2 AS SELECT x,y,z FROM t;\n\nDESC v2;\n+-------+---------+------+-----+---------+-------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+---------+------+-----+---------+-------+\n| x | int(11) | YES | | NULL | |\n| y | int(11) | YES | | NULL | |\n| z | int(11) | NO | | 4 | |\n+-------+---------+------+-----+---------+-------+\n\nAdding a Surrogate Primary Key:\n\ncreate table t1 (x bigint unsigned not null, y varchar(16), z text);\n\ninsert into t1 values (123, \'qq11\', \'ipsum\');\n\ninsert into t1 values (123, \'qq22\', \'lorem\');\n\nalter table t1 add pkid serial primary key invisible first;\n\ninsert into t1 values (123, \'qq33\', \'amet\');\n\nselect * from t1;\n+-----+------+-------+\n| x | y | z |\n+-----+------+-------+\n| 123 | qq11 | ipsum |\n| 123 | qq22 | lorem |\n| 123 | qq33 | amet |\n+-----+------+-------+\n\nselect pkid, z from t1;\n+------+-------+\n| pkid | z |\n+------+-------+\n| 1 | ipsum |\n| 2 | lorem |\n| 3 | amet |\n+------+-------+\n\nURL: https://mariadb.com/kb/en/invisible-columns/','','https://mariadb.com/kb/en/invisible-columns/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (717,38,'DROP DATABASE','Syntax\n------\n\nDROP {DATABASE | SCHEMA} [IF EXISTS] db_name\n\nDescription\n-----------\n\nDROP DATABASE drops all tables in the database and deletes the database. Be\nvery careful with this statement! To use DROP DATABASE, you need the DROP\nprivilege on the database. DROP SCHEMA is a synonym for DROP DATABASE.\n\nImportant: When a database is dropped, user privileges on the database are not\nautomatically dropped. See GRANT.\n\nIF EXISTS\n---------\n\nUse IF EXISTS to prevent an error from occurring for databases that do not\nexist. A NOTE is generated for each non-existent database when using IF\nEXISTS. See SHOW WARNINGS.\n\nAtomic DDL\n----------\n\nMariaDB starting with 10.6.1\n----------------------------\nMariaDB 10.6.1 supports Atomic DDL.\n\nDROP DATABASE is implemented as\n\nloop over all tables\n DROP TABLE table\n\nEach individual DROP TABLE is atomic while DROP DATABASE as a whole is\ncrash-safe.\n\nExamples\n--------\n\nDROP DATABASE bufg;\nQuery OK, 0 rows affected (0.39 sec)\n\nDROP DATABASE bufg;\nERROR 1008 (HY000): Can\'t drop database \'bufg\'; database doesn\'t exist\n\n\\W\nShow warnings enabled.\n\nDROP DATABASE IF EXISTS bufg;\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\nNote (Code 1008): Can\'t drop database \'bufg\'; database doesn\'t exist\n\nURL: https://mariadb.com/kb/en/drop-database/','','https://mariadb.com/kb/en/drop-database/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (718,38,'DROP EVENT','Syntax\n------\n\nDROP EVENT [IF EXISTS] event_name\n\nDescription\n-----------\n\nThis statement drops the event named event_name. The event immediately ceases\nbeing active, and is deleted completely from the server.\n\nIf the event does not exist, the error ERROR 1517 (HY000): Unknown event\n\'event_name\' results. You can override this and cause the statement to\ngenerate a NOTE for non-existent events instead by using IF EXISTS. See SHOW\nWARNINGS.\n\nThis statement requires the EVENT privilege. In MySQL 5.1.11 and earlier, an\nevent could be dropped only by its definer, or by a user having the SUPER\nprivilege.\n\nExamples\n--------\n\nDROP EVENT myevent3;\n\nUsing the IF EXISTS clause:\n\nDROP EVENT IF EXISTS myevent3;\nQuery OK, 0 rows affected, 1 warning (0.01 sec)\n\nSHOW WARNINGS;\n+-------+------+-------------------------------+\n| Level | Code | Message |\n+-------+------+-------------------------------+\n| Note | 1305 | Event myevent3 does not exist |\n+-------+------+-------------------------------+\n\nURL: https://mariadb.com/kb/en/drop-event/','','https://mariadb.com/kb/en/drop-event/'); @@ -913,8 +913,8 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (800,44,'WSREP_LAST_SEEN_GTID','MariaDB starting with 10.4.2\n----------------------------\nWSREP_LAST_SEEN_GTID was added as part of Galera 4 in MariaDB 10.4.2.\n\nSyntax\n------\n\nWSREP_LAST_SEEN_GTID()\n\nDescription\n-----------\n\nReturns the Global Transaction ID of the most recent write transaction\nobserved by the client.\n\nThe result can be useful to determine the transaction to provide to\nWSREP_SYNC_WAIT_UPTO_GTID for waiting and unblocking purposes.\n\nURL: https://mariadb.com/kb/en/wsrep_last_seen_gtid/','','https://mariadb.com/kb/en/wsrep_last_seen_gtid/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (801,44,'WSREP_LAST_WRITTEN_GTID','MariaDB starting with 10.4.2\n----------------------------\nWSREP_LAST_WRITTEN_GTID was added as part of Galera 4 in MariaDB 10.4.2.\n\nSyntax\n------\n\nWSREP_LAST_WRITTEN_GTID()\n\nDescription\n-----------\n\nReturns the Global Transaction ID of the most recent write transaction\nperformed by the client.\n\nURL: https://mariadb.com/kb/en/wsrep_last_written_gtid/','','https://mariadb.com/kb/en/wsrep_last_written_gtid/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (802,44,'WSREP_SYNC_WAIT_UPTO_GTID','MariaDB starting with 10.4.2\n----------------------------\nWSREP_SYNC_WAIT_UPTO_GTID was added as part of Galera 4 in MariaDB 10.4.2.\n\nSyntax\n------\n\nWSREP_SYNC_WAIT_UPTO_GTID(gtid[,timeout])\n\nDescription\n-----------\n\nBlocks the client until the transaction specified by the given Global\nTransaction ID is applied and committed by the node.\n\nThe optional timeout argument can be used to specify a block timeout in\nseconds. If not provided, the timeout will be indefinite.\n\nReturns the node that applied and committed the Global Transaction ID,\nER_LOCAL_WAIT_TIMEOUT if the function is timed out before this, or\nER_WRONG_ARGUMENTS if the function is given an invalid GTID.\n\nThe result from WSREP_LAST_SEEN_GTID can be useful to determine the\ntransaction to provide to WSREP_SYNC_WAIT_UPTO_GTID for waiting and unblocking\npurposes.\n\nURL: https://mariadb.com/kb/en/wsrep_sync_wait_upto_gtid/','','https://mariadb.com/kb/en/wsrep_sync_wait_upto_gtid/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (803,45,'System-Versioned Tables','MariaDB supports temporal data tables in the form of system-versioning tables\n(allowing you to query and operate on historic data, discussed below),\napplication-time periods (allow you to query and operate on a temporal range\nof data), and bitemporal tables (which combine both system-versioning and\napplication-time periods).\n\nSystem-Versioned Tables\n-----------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nSupport for system-versioned tables was added in MariaDB 10.3.4.\n\nSystem-versioned tables store the history of all changes, not only data which\nis currently valid. This allows data analysis for any point in time, auditing\nof changes and comparison of data from different points in time. Typical uses\ncases are:\n\n* Forensic analysis & legal requirements to store data for N years.\n* Data analytics (retrospective, trends etc.), e.g. to get your staff\ninformation as of one year ago.\n* Point-in-time recovery - recover a table state as of particular point in\ntime.\n\nSystem-versioned tables were first introduced in the SQL:2011 standard.\n\nCreating a System-Versioned Table\n---------------------------------\n\nThe CREATE TABLE syntax has been extended to permit creating a\nsystem-versioned table. To be system-versioned, according to SQL:2011, a table\nmust have two generated columns, a period, and a special table option clause:\n\nCREATE TABLE t(\n x INT,\n start_timestamp TIMESTAMP(6) GENERATED ALWAYS AS ROW START,\n end_timestamp TIMESTAMP(6) GENERATED ALWAYS AS ROW END,\n PERIOD FOR SYSTEM_TIME(start_timestamp, end_timestamp)\n) WITH SYSTEM VERSIONING;\n\nIn MariaDB one can also use a simplified syntax:\n\nCREATE TABLE t (\n x INT\n) WITH SYSTEM VERSIONING;\n\nIn the latter case no extra columns will be created and they won\'t clutter the\noutput of, say, SELECT * FROM t. The versioning information will still be\nstored, and it can be accessed via the pseudo-columns ROW_START and ROW_END:\n\nSELECT x, ROW_START, ROW_END FROM t;\n\nAdding or Removing System Versioning To/From a Table\n----------------------------------------------------\n\nAn existing table can be altered to enable system versioning for it.\n\nCREATE TABLE t(\n x INT\n);\n\nALTER TABLE t ADD SYSTEM VERSIONING;\n\nSHOW CREATE TABLE t\\G\n*************************** 1. row ***************************\n Table: t\nCreate Table: CREATE TABLE `t` (\n `x` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING\n\nSimilarly, system versioning can be removed from a table:\n\nALTER TABLE t DROP SYSTEM VERSIONING;\n\nSHOW CREATE TABLE t\\G\n*************************** 1. row ***************************\n Table: t\nCreate Table: CREATE TABLE `t` (\n `x` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=latin1\n\nOne can also add system versioning with all columns created explicitly:\n\nALTER TABLE t ADD COLUMN ts TIMESTAMP(6) GENERATED ALWAYS AS ROW START,\n ADD COLUMN te TIMESTAMP(6) GENERATED ALWAYS AS ROW END,\n ADD PERIOD FOR SYSTEM_TIME(ts, te),\n ADD SYSTEM VERSIONING;\n\nSHOW CREATE TABLE t\\G\n*************************** 1. row ***************************\n Table: t\nCreate Table: CREATE TABLE `t` (\n `x` int(11) DEFAULT NULL,\n `ts` timestamp(6) GENERATED ALWAYS AS ROW START,\n `te` timestamp(6) GENERATED ALWAYS AS ROW END,\n PERIOD FOR SYSTEM_TIME (`ts`, `te`)\n) ENGINE=InnoDB DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING\n\nQuerying Historical Data\n------------------------\n\nSELECT\n------\n\nTo query the historical data one uses the clause FOR SYSTEM_TIME directly\nafter the table name (before the table alias, if any). SQL:2011 provides three\nsyntactic extensions:\n\n* AS OF is used to see the table as it was at a specific point in time in the\npast:\n\nSELECT * FROM t FOR SYSTEM_TIME AS OF TIMESTAMP\'2016-10-09 08:07:06\';\n\n* BETWEEN start AND end will show all rows that were visible at any point\nbetween two specified points in time. It works inclusively, a row visible\nexactly at start or exactly at end will be shown too.\n\nSELECT * FROM t FOR SYSTEM_TIME BETWEEN (NOW() - INTERVAL 1 YEAR) AND NOW();\n\n* FROM start TO end will also show all rows that were visible at any point\nbetween two specified points in time, including start, but excluding end.\n\nSELECT * FROM t FOR SYSTEM_TIME FROM \'2016-01-01 00:00:00\' TO \'2017-01-01\n00:00:00\';\n\nAdditionally MariaDB implements a non-standard extension:\n\n* ALL will show all rows, historical and current.\n\nSELECT * FROM t FOR SYSTEM_TIME ALL;\n\nIf the FOR SYSTEM_TIME clause is not used, the table will show the current\ndata, as if one had specified FOR SYSTEM_TIME AS OF CURRENT_TIMESTAMP.\n\nViews and Subqueries\n--------------------\n\nWhen a system-versioned tables is used in a view or in a subquery in the from\nclause, FOR SYSTEM_TIME can be used directly in the view or subquery body, or\n(non-standard) applied to the whole view when it\'s being used in a SELECT:\n\nCREATE VIEW v1 AS SELECT * FROM t FOR SYSTEM_TIME AS OF TIMESTAMP\'2016-10-09\n08:07:06\';\n\nOr\n\nCREATE VIEW v1 AS SELECT * FROM t;\nSELECT * FROM v1 FOR SYSTEM_TIME AS OF TIMESTAMP\'2016-10-09 08:07:06\';\n\nUse in Replication and Binary Logs\n----------------------------------\n\nTables that use system-versioning implicitly add the row_end column to the\nPrimary Key. While this is generally not an issue for most use cases, it can\nlead to problems when re-applying write statements from the binary log or in\nreplication environments, where a primary retries an SQL statement on the\nreplica.\n\nSpecifically, these writes include a value on the row_end column containing\nthe timestamp from when the write was initially made. The re-occurrence of the\nPrimary Key with the old system-versioning columns raises an error due to the\nduplication.\n\nTo mitigate this with MariaDB Replication, set the secure_timestamp system\nvariable to YES on the replica. When set, the replica uses its own system\nclock when applying to the row log, meaning that the primary can retry as many\ntimes as needed without causing a conflict. The retries generate new\nhistorical rows with new values for the row_start and row_end columns.\n\nTransaction-Precise History in InnoDB\n-------------------------------------\n\nA point in time when a row was inserted or deleted does not necessarily mean\nthat a change became visible at the same moment. With transactional tables, a\nrow might have been inserted in a long transaction, and became visible hours\nafter it was inserted.\n\nFor some applications — for example, when doing data analytics on one-year-old\ndata — this distinction does not matter much. For others — forensic analysis —\nit might be crucial.\n\nMariaDB supports transaction-precise history (only for the InnoDB storage\nengine) that allows seeing the data exactly as it would\'ve been seen by a new\nconnection doing a SELECT at the specified point in time — rows inserted\nbefore that point, but committed after will not be shown.\n\nTo use transaction-precise history, InnoDB needs to remember not timestamps,\nbut transaction identifier per row. This is done by creating generated columns\nas BIGINT UNSIGNED, not TIMESTAMP(6):\n\nCREATE TABLE t(\n x INT,\n start_trxid BIGINT UNSIGNED GENERATED ALWAYS AS ROW START,\n end_trxid BIGINT UNSIGNED GENERATED ALWAYS AS ROW END,\n PERIOD FOR SYSTEM_TIME(start_trxid, end_trxid)\n) WITH SYSTEM VERSIONING;\n\nThese columns must be specified explicitly, but they can be made INVISIBLE to\navoid cluttering SELECT * output.\n\nWhen one uses transaction-precise history, one can optionally use transaction\nidentifiers in the FOR SYSTEM_TIME clause:\n\nSELECT * FROM t FOR SYSTEM_TIME AS OF TRANSACTION 12345;\n\nThis will show the data, exactly as it was seen by the transaction with the\nidentifier 12345.\n\nStoring the History Separately\n------------------------------\n\nWhen the history is stored together with the current data, it increases the\nsize of the table, so current data queries — table scans and index searches —\nwill take more time, because they will need to skip over historical data. If\nmost queries on that table use only current data, it might make sense to store\nthe history separately, to reduce the overhead from versioning.\n\nThis is done by partitioning the table by SYSTEM_TIME. Because of the\npartition pruning optimization, all current data queries will only access one\npartition, the one that stores current data.\n\nThis example shows how to create such a partitioned table:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME (\n PARTITION p_hist HISTORY,\n PARTITION p_cur CURRENT\n );\n\nIn this example all history will be stored in the partition p_hist while all\ncurrent data will be in the partition p_cur. The table must have exactly one\ncurrent partition and at least one historical partition.\n\nPartitioning by SYSTEM_TIME also supports automatic partition rotation. One\ncan rotate historical partitions by time or by size. This example shows how to\nrotate partitions by size:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME LIMIT 100000 (\n PARTITION p0 HISTORY,\n PARTITION p1 HISTORY,\n PARTITION pcur CURRENT\n );\n\nMariaDB will start writing history rows into partition p0, and when it reaches\na size of 100000 rows, MariaDB will switch to partition p1. There are only two\nhistorical partitions, so when p1 overflows, MariaDB will issue a warning, but\nwill continue writing into it.\n\nSimilarly, one can rotate partitions by time:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 WEEK (\n PARTITION p0 HISTORY,\n PARTITION p1 HISTORY,\n PARTITION p2 HISTORY,\n PARTITION pcur CURRENT\n );\n\nThis means that the history for the first week after the table was created\nwill be stored in p0. The history for the second week — in p1, and all later\nhistory will go into p2. One can see the exact rotation time for each\npartition in the INFORMATION_SCHEMA.PARTITIONS table.\n\nIt is possible to combine partitioning by SYSTEM_TIME and subpartitions:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME\n SUBPARTITION BY KEY (x)\n SUBPARTITIONS 4 (\n PARTITION ph HISTORY,\n PARTITION pc CURRENT\n );\n\nDefault Partitions\n------------------\n\nMariaDB starting with 10.5.0\n----------------------------\nSince partitioning by current and historical data is such a typical usecase,\nfrom MariaDB 10.5, it is possible to use a simplified statement to do so. For\nexample, instead of\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING \n PARTITION BY SYSTEM_TIME (\n PARTITION p0 HISTORY,\n PARTITION pn CURRENT\n);\n\nyou can use\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING \n PARTITION BY SYSTEM_TIME;\n\nYou can also specify the number of partitions, which is useful if you want to\nrotate history by time, for example:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING \n PARTITION BY SYSTEM_TIME\n INTERVAL 1 MONTH\n PARTITIONS 12;\n\nSpecifying the number of partitions without specifying a rotation condition\nwill result in a warning:\n\nCREATE OR REPLACE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME PARTITIONS 12;\nQuery OK, 0 rows affected, 1 warning (0.518 sec)\n\nWarning (Code 4115): Maybe missing parameters: no rotation condition for\nmultiple HISTORY partitions.\n\nwhile specifying only 1 partition will result in an error:\n\nCREATE OR REPLACE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME PARTITIONS 1;\nERROR 4128 (HY000): Wrong partitions for `t`: must have at least one HISTORY\nand exactly one last CURRENT\n\nAutomatically Creating Partitions\n---------------------------------\n\nMariaDB starting with 10.9.1\n----------------------------\nFrom MariaDB 10.9.1, the AUTO keyword can be used to automatically create\nhistory partitions.\n\nFor example\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO;\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 MONTH\n STARTS \'2021-01-01 00:00:00\' AUTO PARTITIONS 12;\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME LIMIT 1000 AUTO;\n\nOr with explicit partitions:\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO\n (PARTITION p0 HISTORY, PARTITION pn CURRENT);\n\nTo disable or enable auto-creation one can use ALTER TABLE by adding or\nremoving AUTO from the partitioning specification:\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO;\n\n# Disables auto-creation:\nALTER TABLE t1 PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR;\n\n# Enables auto-creation:\nALTER TABLE t1 PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO;\n\nIf the rest of the partitioning specification is identical to CREATE TABLE, no\nrepartitioning will be done (for details see MDEV-27328).\n\nRemoving Old History\n--------------------\n\nBecause it stores all the history, a system-versioned table might grow very\nlarge over time. There are many options to trim down the space and remove the\nold history.\n\nOne can completely drop the versioning from the table and add it back again,\nthis will delete all the history:\n\nALTER TABLE t DROP SYSTEM VERSIONING;\nALTER TABLE t ADD SYSTEM VERSIONING;\n\nIt might be a rather time-consuming operation, though, as the table will need\nto be rebuilt, possibly twice (depending on the storage engine).\n\nAnother option would be to use partitioning and drop some of historical\npartitions:\n\nALTER TABLE t DROP PARTITION p0;\n\nNote, that one cannot drop a current partition or the only historical\npartition.\n\nAnd the third option; one can use a variant of the DELETE statement to prune\nthe history:\n\nDELETE HISTORY FROM t;\n\nor only old history up to a specific point in time:\n\nDELETE HISTORY FROM t BEFORE SYSTEM_TIME \'2016-10-09 08:07:06\';\n\nor to a specific transaction (with BEFORE SYSTEM_TIME TRANSACTION xxx).\n\nTo protect the integrity of the history, this statement requires a special\nDELETE HISTORY privilege.\n\nCurrently, using the DELETE HISTORY statement with a BEFORE SYSTEM_TIME\ngreater than the ROW_END of the active records (as a TIMESTAMP, this has a\nmaximum value of \'2038-01-19 03:14:07\' UTC) will result in the historical\nrecords being dropped, and the active records being deleted and moved to\nhistory. See MDEV-25468.\n','','https://mariadb.com/kb/en/system-versioned-tables/'); -update help_topic set description = CONCAT(description, '\nPrior to MariaDB 10.4.5, the TRUNCATE TABLE statement drops all historical\nrecords from a system-versioned-table.\n\nFrom MariaDB 10.4.5, historic data is protected from TRUNCATE statements, as\nper the SQL standard, and an Error 4137 is instead raised:\n\nTRUNCATE t;\nERROR 4137 (HY000): System-versioned tables do not support TRUNCATE TABLE\n\nExcluding Columns From Versioning\n---------------------------------\n\nAnother MariaDB extension allows to version only a subset of columns in a\ntable. This is useful, for example, if you have a table with user information\nthat should be versioned, but one column is, let\'s say, a login counter that\nis incremented often and is not interesting to version. Such a column can be\nexcluded from versioning by declaring it WITHOUT VERSIONING\n\nCREATE TABLE t (\n x INT,\n y INT WITHOUT SYSTEM VERSIONING\n) WITH SYSTEM VERSIONING;\n\nA column can also be declared WITH VERSIONING, that will automatically make\nthe table versioned. The statement below is equivalent to the one above:\n\nCREATE TABLE t (\n x INT WITH SYSTEM VERSIONING,\n y INT\n);\n\nChanges in other sections: https://mariadb.com/kb/en/create-table/\nhttps://mariadb.com/kb/en/alter-table/ https://mariadb.com/kb/en/join-syntax/\nhttps://mariadb.com/kb/en/partitioning-types-overview/\nhttps://mariadb.com/kb/en/date-and-time-units/\nhttps://mariadb.com/kb/en/delete/ https://mariadb.com/kb/en/grant/\n\nthey all reference back to this page\n\nAlso, TODO:\n\n* limitations (size, speed, adding history to unique not nullable columns)\n\nSystem Variables\n----------------\n\nThere are a number of system variables related to system-versioned tables:\n\nsystem_versioning_alter_history\n-------------------------------\n\n* Description: SQL:2011 does not allow ALTER TABLE on system-versioned tables.\nWhen this variable is set to ERROR, an attempt to alter a system-versioned\ntable will result in an error. When this variable is set to KEEP, ALTER TABLE\nwill be allowed, but the history will become incorrect — querying historical\ndata will show the new table structure. This mode is still useful, for\nexample, when adding new columns to a table. Note that if historical data\ncontains or would contain nulls, attempting to ALTER these columns to be NOT\nNULL will return an error (or warning if strict_mode is not set).\n* Commandline: --system-versioning-alter-history=value\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Enum\n* Default Value: ERROR\n* Valid Values: ERROR, KEEP\n* Introduced: MariaDB 10.3.4\n\nsystem_versioning_asof\n----------------------\n\n* Description: If set to a specific timestamp value, an implicit FOR\nSYSTEM_TIME AS OF clause will be applied to all queries. This is useful if one\nwants to do many queries for history at the specific point in time. Set it to\nDEFAULT to restore the default behavior. Has no effect on DML, so queries such\nas INSERT .. SELECT and REPLACE .. SELECT need to state AS OF explicitly.\n* Commandline: None\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Varchar\n* Default Value: DEFAULT\n* Introduced: MariaDB 10.3.4\n\nsystem_versioning_innodb_algorithm_simple\n-----------------------------------------\n\n* Description: Never fully implemented and removed in the following release.\n* Commandline: --system-versioning-innodb-algorithm-simple[={0|1}]\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Boolean\n* Default Value: ON\n* Introduced: MariaDB 10.3.4\n* Removed: MariaDB 10.3.5\n\nsystem_versioning_insert_history\n--------------------------------\n\n* Description: Allows direct inserts into ROW_START and ROW_END columns if\nsecure_timestamp allows changing timestamp.\n* Commandline: --system-versioning-insert-history[={0|1}]\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Boolean\n* Default Value: OFF\n* Introduced: MariaDB 10.11.0\n\nLimitations\n-----------\n\n* Versioning clauses can not be applied to generated (virtual and persistent)\ncolumns.\n* mysqldump does not read historical rows from versioned tables, and so\nhistorical data will not be backed up. Also, a restore of the timestamps would\nnot be possible as they cannot be defined by an insert/a user.\n\nURL: https://mariadb.com/kb/en/system-versioned-tables/') WHERE help_topic_id = 803; +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (803,45,'System-Versioned Tables','MariaDB supports temporal data tables in the form of system-versioning tables\n(allowing you to query and operate on historic data, discussed below),\napplication-time periods (allow you to query and operate on a temporal range\nof data), and bitemporal tables (which combine both system-versioning and\napplication-time periods).\n\nSystem-Versioned Tables\n-----------------------\n\nSystem-versioned tables store the history of all changes, not only data which\nis currently valid. This allows data analysis for any point in time, auditing\nof changes and comparison of data from different points in time. Typical uses\ncases are:\n\n* Forensic analysis & legal requirements to store data for N years.\n* Data analytics (retrospective, trends etc.), e.g. to get your staff\ninformation as of one year ago.\n* Point-in-time recovery - recover a table state as of particular point in\ntime.\n\nSystem-versioned tables were first introduced in the SQL:2011 standard.\n\nCreating a System-Versioned Table\n---------------------------------\n\nThe CREATE TABLE syntax has been extended to permit creating a\nsystem-versioned table. To be system-versioned, according to SQL:2011, a table\nmust have two generated columns, a period, and a special table option clause:\n\nCREATE TABLE t(\n x INT,\n start_timestamp TIMESTAMP(6) GENERATED ALWAYS AS ROW START,\n end_timestamp TIMESTAMP(6) GENERATED ALWAYS AS ROW END,\n PERIOD FOR SYSTEM_TIME(start_timestamp, end_timestamp)\n) WITH SYSTEM VERSIONING;\n\nIn MariaDB one can also use a simplified syntax:\n\nCREATE TABLE t (\n x INT\n) WITH SYSTEM VERSIONING;\n\nIn the latter case no extra columns will be created and they won\'t clutter the\noutput of, say, SELECT * FROM t. The versioning information will still be\nstored, and it can be accessed via the pseudo-columns ROW_START and ROW_END:\n\nSELECT x, ROW_START, ROW_END FROM t;\n\nAdding or Removing System Versioning To/From a Table\n----------------------------------------------------\n\nAn existing table can be altered to enable system versioning for it.\n\nCREATE TABLE t(\n x INT\n);\n\nALTER TABLE t ADD SYSTEM VERSIONING;\n\nSHOW CREATE TABLE t\\G\n*************************** 1. row ***************************\n Table: t\nCreate Table: CREATE TABLE `t` (\n `x` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING\n\nSimilarly, system versioning can be removed from a table:\n\nALTER TABLE t DROP SYSTEM VERSIONING;\n\nSHOW CREATE TABLE t\\G\n*************************** 1. row ***************************\n Table: t\nCreate Table: CREATE TABLE `t` (\n `x` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=latin1\n\nOne can also add system versioning with all columns created explicitly:\n\nALTER TABLE t ADD COLUMN ts TIMESTAMP(6) GENERATED ALWAYS AS ROW START,\n ADD COLUMN te TIMESTAMP(6) GENERATED ALWAYS AS ROW END,\n ADD PERIOD FOR SYSTEM_TIME(ts, te),\n ADD SYSTEM VERSIONING;\n\nSHOW CREATE TABLE t\\G\n*************************** 1. row ***************************\n Table: t\nCreate Table: CREATE TABLE `t` (\n `x` int(11) DEFAULT NULL,\n `ts` timestamp(6) GENERATED ALWAYS AS ROW START,\n `te` timestamp(6) GENERATED ALWAYS AS ROW END,\n PERIOD FOR SYSTEM_TIME (`ts`, `te`)\n) ENGINE=InnoDB DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING\n\nInserting Data\n--------------\n\nWhen data is inserted into a system-versioned table, it is given a row_start\nvalue of the current timestamp, and a row_end value of\nFROM_UNIXTIME(2147483647.999999). The current timestamp can be adjusted by\nsetting the timestamp system variable, for example:\n\nSELECT NOW();\n+---------------------+\n| NOW() |\n+---------------------+\n| 2022-10-24 23:09:38 |\n+---------------------+\n\nINSERT INTO t VALUES(1);\n\nSET @@timestamp = UNIX_TIMESTAMP(\'2033-10-24\');\n\nINSERT INTO t VALUES(2);\n\nSET @@timestamp = default;\n\nINSERT INTO t VALUES(3);\n\nSELECT a,row_start,row_end FROM t;\n+------+----------------------------+----------------------------+\n| a | row_start | row_end |\n+------+----------------------------+----------------------------+\n| 1 | 2022-10-24 23:09:38.951347 | 2038-01-19 05:14:07.999999 |\n| 2 | 2033-10-24 00:00:00.000000 | 2038-01-19 05:14:07.999999 |\n| 3 | 2022-10-24 23:09:38.961857 | 2038-01-19 05:14:07.999999 |\n+------+----------------------------+----------------------------+\n\nQuerying Historical Data\n------------------------\n\nSELECT\n------\n\nTo query the historical data one uses the clause FOR SYSTEM_TIME directly\nafter the table name (before the table alias, if any). SQL:2011 provides three\nsyntactic extensions:\n\n* AS OF is used to see the table as it was at a specific point in time in the\npast:\n\nSELECT * FROM t FOR SYSTEM_TIME AS OF TIMESTAMP\'2016-10-09 08:07:06\';\n\n* BETWEEN start AND end will show all rows that were visible at any point\nbetween two specified points in time. It works inclusively, a row visible\nexactly at start or exactly at end will be shown too.\n\nSELECT * FROM t FOR SYSTEM_TIME BETWEEN (NOW() - INTERVAL 1 YEAR) AND NOW();\n\n* FROM start TO end will also show all rows that were visible at any point\nbetween two specified points in time, including start, but excluding end.\n\nSELECT * FROM t FOR SYSTEM_TIME FROM \'2016-01-01 00:00:00\' TO \'2017-01-01\n00:00:00\';\n\nAdditionally MariaDB implements a non-standard extension:\n\n* ALL will show all rows, historical and current.\n\nSELECT * FROM t FOR SYSTEM_TIME ALL;\n\nIf the FOR SYSTEM_TIME clause is not used, the table will show the current\ndata. This is usually the same as if one had specified FOR SYSTEM_TIME AS OF\nCURRENT_TIMESTAMP, unless one has adjusted the row_start value (until MariaDB\n10.11, only possible by setting the secure_timestamp variable). For example:\n\nCREATE OR REPLACE TABLE t (a int) WITH SYSTEM VERSIONING;\n\nSELECT NOW();\n+---------------------+\n| NOW() |\n+---------------------+\n| 2022-10-24 23:43:37 |\n+---------------------+\n\nINSERT INTO t VALUES (1);\n\nSET @@timestamp = UNIX_TIMESTAMP(\'2033-03-03\');\n\nINSERT INTO t VALUES (2);\n\nDELETE FROM t;\n\nSET @@timestamp = default;\n\nSELECT a, row_start, row_end FROM t FOR SYSTEM_TIME ALL;\n+------+----------------------------+----------------------------+\n| a | row_start | row_end |\n+------+----------------------------+----------------------------+\n| 1 | 2022-10-24 23:43:37.192725 | 2033-03-03 00:00:00.000000 |\n| 2 | 2033-03-03 00:00:00.000000 | 2033-03-03 00:00:00.000000 |\n+------+----------------------------+----------------------------+\n2 rows in set (0.000 sec)\n\nSELECT a, row_start, row_end FROM t FOR SYSTEM_TIME AS OF CURRENT_TIMESTAMP;\n+------+----------------------------+----------------------------+\n| a | row_start | row_end |\n+------+----------------------------+----------------------------+\n| 1 | 2022-10-24 23:43:37.192725 | 2033-03-03 00:00:00.000000 |\n+------+----------------------------+----------------------------+\n1 row in set (0.000 sec)\n\nSELECT a, row_start, row_end FROM t;\nEmpty set (0.001 sec)\n\nViews and Subqueries\n--------------------\n\nWhen a system-versioned tables is used in a view or in a subquery in the from\nclause, FOR SYSTEM_TIME can be used directly in the view or subquery body, or\n(non-standard) applied to the whole view when it\'s being used in a SELECT:\n\nCREATE VIEW v1 AS SELECT * FROM t FOR SYSTEM_TIME AS OF TIMESTAMP\'2016-10-09\n08:07:06\';\n\nOr\n\nCREATE VIEW v1 AS SELECT * FROM t;\nSELECT * FROM v1 FOR SYSTEM_TIME AS OF TIMESTAMP\'2016-10-09 08:07:06\';\n\nUse in Replication and Binary Logs\n----------------------------------\n\nTables that use system-versioning implicitly add the row_end column to the\nPrimary Key. While this is generally not an issue for most use cases, it can\nlead to problems when re-applying write statements from the binary log or in\nreplication environments, where a primary retries an SQL statement on the\nreplica.\n\nSpecifically, these writes include a value on the row_end column containing\nthe timestamp from when the write was initially made. The re-occurrence of the\nPrimary Key with the old system-versioning columns raises an error due to the\nduplication.\n\nTo mitigate this with MariaDB Replication, set the secure_timestamp system\nvariable to YES on the replica. When set, the replica uses its own system\nclock when applying to the row log, meaning that the primary can retry as many\ntimes as needed without causing a conflict. The retries generate new\nhistorical rows with new values for the row_start and row_end columns.\n\nTransaction-Precise History in InnoDB\n-------------------------------------\n\nA point in time when a row was inserted or deleted does not necessarily mean\nthat a change became visible at the same moment. With transactional tables, a\nrow might have been inserted in a long transaction, and became visible hours\nafter it was inserted.\n\nFor some applications — for example, when doing data analytics on one-year-old\ndata — this distinction does not matter much. For others — forensic analysis —\nit might be crucial.\n\nMariaDB supports transaction-precise history (only for the InnoDB storage\nengine) that allows seeing the data exactly as it would\'ve been seen by a new\nconnection doing a SELECT at the specified point in time — rows inserted\nbefore that point, but committed after will not be shown.\n\nTo use transaction-precise history, InnoDB needs to remember not timestamps,\nbut transaction identifier per row. This is done by creating generated columns\nas BIGINT UNSIGNED, not TIMESTAMP(6):\n\nCREATE TABLE t(\n x INT,\n start_trxid BIGINT UNSIGNED GENERATED ALWAYS AS ROW START,\n end_trxid BIGINT UNSIGNED GENERATED ALWAYS AS ROW END,\n PERIOD FOR SYSTEM_TIME(start_trxid, end_trxid)\n) WITH SYSTEM VERSIONING;\n\nThese columns must be specified explicitly, but they can be made INVISIBLE to\navoid cluttering SELECT * output.\n\nWhen one uses transaction-precise history, one can optionally use transaction\nidentifiers in the FOR SYSTEM_TIME clause:\n\nSELECT * FROM t FOR SYSTEM_TIME AS OF TRANSACTION 12345;\n\nThis will show the data, exactly as it was seen by the transaction with the\nidentifier 12345.\n\nStoring the History Separately\n------------------------------\n\nWhen the history is stored together with the current data, it increases the\nsize of the table, so current data queries — table scans and index searches —\nwill take more time, because they will need to skip over historical data. If\nmost queries on that table use only current data, it might make sense to store\nthe history separately, to reduce the overhead from versioning.\n\nThis is done by partitioning the table by SYSTEM_TIME. Because of the\npartition pruning optimization, all current data queries will only access one\npartition, the one that stores current data.\n\nThis example shows how to create such a partitioned table:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME (\n PARTITION p_hist HISTORY,\n PARTITION p_cur CURRENT\n );\n\nIn this example all history will be stored in the partition p_hist while all\ncurrent data will be in the partition p_cur. The table must have exactly one\ncurrent partition and at least one historical partition.\n\nPartitioning by SYSTEM_TIME also supports automatic partition rotation. One\ncan rotate historical partitions by time or by size. This example shows how to\nrotate partitions by size:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME LIMIT 100000 (\n PARTITION p0 HISTORY,\n PARTITION p1 HISTORY,\n PARTITION pcur CURRENT\n );\n\nMariaDB will start writing history rows into partition p0, and when it reaches\na size of 100000 rows, MariaDB will switch to partition p1. There are only two\nhistorical partitions, so when p1 overflows, MariaDB will issue a warning, but\nwill continue writing into it.\n\nSimilarly, one can rotate partitions by time:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 WEEK (\n PARTITION p0 HISTORY,\n PARTITION p1 HISTORY,\n PARTITION p2 HISTORY,\n PARTITION pcur CURRENT\n );\n\nThis means that the history for the first week after the table was created\nwill be stored in p0. The history for the second week — in p1, and all later\nhistory will go into p2. One can see the exact rotation time for each\npartition in the INFORMATION_SCHEMA.PARTITIONS table.\n\nIt is possible to combine partitioning by SYSTEM_TIME and subpartitions:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME\n SUBPARTITION BY KEY (x)\n SUBPARTITIONS 4 (\n PARTITION ph HISTORY,\n PARTITION pc CURRENT\n );\n\nDefault Partitions\n------------------\n\nMariaDB starting with 10.5.0\n----------------------------\nSince partitioning by current and historical data is such a typical usecase,\nfrom MariaDB 10.5, it is possible to use a simplified statement to do so. For\nexample, instead of\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING \n PARTITION BY SYSTEM_TIME (\n PARTITION p0 HISTORY,\n PARTITION pn CURRENT\n);\n\nyou can use\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING \n PARTITION BY SYSTEM_TIME;\n\nYou can also specify the number of partitions, which is useful if you want to\nrotate history by time, for example:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING \n PARTITION BY SYSTEM_TIME\n INTERVAL 1 MONTH\n PARTITIONS 12;\n\nSpecifying the number of partitions without specifying a rotation condition\nwill result in a warning:\n\nCREATE OR REPLACE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME PARTITIONS 12;\nQuery OK, 0 rows affected, 1 warning (0.518 sec)\n\nWarning (Code 4115): Maybe missing parameters: no rotation condition for\nmultiple HISTORY partitions.\n\nwhile specifying only 1 partition will result in an error:\n\nCREATE OR REPLACE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME PARTITIONS 1;\nERROR 4128 (HY000): Wrong partitions for `t`: must have at least one HISTORY\nand exactly one last CURRENT\n\nAutomatically Creating Partitions\n---------------------------------\n\nMariaDB starting with 10.9.1\n----------------------------\nFrom MariaDB 10.9.1, the AUTO keyword can be used to automatically create\nhistory partitions.\n\nFor example\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO;\n','','https://mariadb.com/kb/en/system-versioned-tables/'); +update help_topic set description = CONCAT(description, '\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 MONTH\n STARTS \'2021-01-01 00:00:00\' AUTO PARTITIONS 12;\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME LIMIT 1000 AUTO;\n\nOr with explicit partitions:\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO\n (PARTITION p0 HISTORY, PARTITION pn CURRENT);\n\nTo disable or enable auto-creation one can use ALTER TABLE by adding or\nremoving AUTO from the partitioning specification:\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO;\n\n# Disables auto-creation:\nALTER TABLE t1 PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR;\n\n# Enables auto-creation:\nALTER TABLE t1 PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO;\n\nIf the rest of the partitioning specification is identical to CREATE TABLE, no\nrepartitioning will be done (for details see MDEV-27328).\n\nRemoving Old History\n--------------------\n\nBecause it stores all the history, a system-versioned table might grow very\nlarge over time. There are many options to trim down the space and remove the\nold history.\n\nOne can completely drop the versioning from the table and add it back again,\nthis will delete all the history:\n\nALTER TABLE t DROP SYSTEM VERSIONING;\nALTER TABLE t ADD SYSTEM VERSIONING;\n\nIt might be a rather time-consuming operation, though, as the table will need\nto be rebuilt, possibly twice (depending on the storage engine).\n\nAnother option would be to use partitioning and drop some of historical\npartitions:\n\nALTER TABLE t DROP PARTITION p0;\n\nNote, that one cannot drop a current partition or the only historical\npartition.\n\nAnd the third option; one can use a variant of the DELETE statement to prune\nthe history:\n\nDELETE HISTORY FROM t;\n\nor only old history up to a specific point in time:\n\nDELETE HISTORY FROM t BEFORE SYSTEM_TIME \'2016-10-09 08:07:06\';\n\nor to a specific transaction (with BEFORE SYSTEM_TIME TRANSACTION xxx).\n\nTo protect the integrity of the history, this statement requires a special\nDELETE HISTORY privilege.\n\nCurrently, using the DELETE HISTORY statement with a BEFORE SYSTEM_TIME\ngreater than the ROW_END of the active records (as a TIMESTAMP, this has a\nmaximum value of \'2038-01-19 03:14:07\' UTC) will result in the historical\nrecords being dropped, and the active records being deleted and moved to\nhistory. See MDEV-25468.\n\nPrior to MariaDB 10.4.5, the TRUNCATE TABLE statement drops all historical\nrecords from a system-versioned-table.\n\nFrom MariaDB 10.4.5, historic data is protected from TRUNCATE statements, as\nper the SQL standard, and an Error 4137 is instead raised:\n\nTRUNCATE t;\nERROR 4137 (HY000): System-versioned tables do not support TRUNCATE TABLE\n\nExcluding Columns From Versioning\n---------------------------------\n\nAnother MariaDB extension allows to version only a subset of columns in a\ntable. This is useful, for example, if you have a table with user information\nthat should be versioned, but one column is, let\'s say, a login counter that\nis incremented often and is not interesting to version. Such a column can be\nexcluded from versioning by declaring it WITHOUT VERSIONING\n\nCREATE TABLE t (\n x INT,\n y INT WITHOUT SYSTEM VERSIONING\n) WITH SYSTEM VERSIONING;\n\nA column can also be declared WITH VERSIONING, that will automatically make\nthe table versioned. The statement below is equivalent to the one above:\n\nCREATE TABLE t (\n x INT WITH SYSTEM VERSIONING,\n y INT\n);\n\nChanges in other sections: https://mariadb.com/kb/en/create-table/\nhttps://mariadb.com/kb/en/alter-table/ https://mariadb.com/kb/en/join-syntax/\nhttps://mariadb.com/kb/en/partitioning-types-overview/\nhttps://mariadb.com/kb/en/date-and-time-units/\nhttps://mariadb.com/kb/en/delete/ https://mariadb.com/kb/en/grant/\n\nthey all reference back to this page\n\nAlso, TODO:\n\n* limitations (size, speed, adding history to unique not nullable columns)\n\nSystem Variables\n----------------\n\nThere are a number of system variables related to system-versioned tables:\n\nsystem_versioning_alter_history\n-------------------------------\n\n* Description: SQL:2011 does not allow ALTER TABLE on system-versioned tables.\nWhen this variable is set to ERROR, an attempt to alter a system-versioned\ntable will result in an error. When this variable is set to KEEP, ALTER TABLE\nwill be allowed, but the history will become incorrect — querying historical\ndata will show the new table structure. This mode is still useful, for\nexample, when adding new columns to a table. Note that if historical data\ncontains or would contain nulls, attempting to ALTER these columns to be NOT\nNULL will return an error (or warning if strict_mode is not set).\n* Commandline: --system-versioning-alter-history=value\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Enum\n* Default Value: ERROR\n* Valid Values: ERROR, KEEP\n\nsystem_versioning_asof\n----------------------\n\n* Description: If set to a specific timestamp value, an implicit FOR\nSYSTEM_TIME AS OF clause will be applied to all queries. This is useful if one\nwants to do many queries for history at the specific point in time. Set it to\nDEFAULT to restore the default behavior. Has no effect on DML, so queries such\nas INSERT .. SELECT and REPLACE .. SELECT need to state AS OF explicitly.\n* Commandline: None\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Varchar\n* Default Value: DEFAULT\n\nsystem_versioning_innodb_algorithm_simple\n-----------------------------------------\n\n* Description: Never fully implemented and removed in the following release.\n* Commandline: --system-versioning-innodb-algorithm-simple[={0|1}]\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Boolean\n* Default Value: ON\n* Introduced: MariaDB 10.3.4\n* Removed: MariaDB 10.3.5\n\nsystem_versioning_insert_history\n--------------------------------\n\n* Description: Allows direct inserts into ROW_START and ROW_END columns if\nsecure_timestamp allows changing timestamp.\n* Commandline: --system-versioning-insert-history[={0|1}]\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Boolean\n* Default Value: OFF\n* Introduced: MariaDB 10.11.0\n\nLimitations\n-----------\n\n* Versioning clauses can not be applied to generated (virtual and persistent)\ncolumns.\n* Before MariaDB 10.11, mariadb-dump did not read historical rows from\nversioned tables, and so historical data would not be backed up. Also, a\nrestore of the timestamps would not be possible as they cannot be defined by\nan insert/a user. From MariaDB 10.11, use the -H or --dump-history options to\ninclude the history.\n\nURL: https://mariadb.com/kb/en/system-versioned-tables/') WHERE help_topic_id = 803; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (804,45,'Application-Time Periods','MariaDB starting with 10.4.3\n----------------------------\nSupport for application-time period-versioning was added in MariaDB 10.4.3.\n\nExtending system-versioned tables, MariaDB 10.4 supports application-time\nperiod tables. Time periods are defined by a range between two temporal\ncolumns. The columns must be of the same temporal data type, i.e. DATE,\nTIMESTAMP or DATETIME (TIME and YEAR are not supported), and of the same width.\n\nUsing time periods implicitly defines the two columns as NOT NULL. It also\nadds a constraint to check whether the first value is less than the second\nvalue. The constraint is invisible to SHOW CREATE TABLE statements. The name\nof this constraint is prefixed by the time period name, to avoid conflict with\nother constraints.\n\nCreating Tables with Time Periods\n---------------------------------\n\nTo create a table with a time period, use a CREATE TABLE statement with the\nPERIOD table option.\n\nCREATE TABLE t1(\n name VARCHAR(50),\n date_1 DATE,\n date_2 DATE,\n PERIOD FOR date_period(date_1, date_2));\n\nThis creates a table with a time_period period and populates the table with\nsome basic temporal values.\n\nExamples are available in the MariaDB Server source code, at\nmysql-test/suite/period/r/create.result.\n\nAdding and Removing Time Periods\n--------------------------------\n\nThe ALTER TABLE statement now supports syntax for adding and removing time\nperiods from a table. To add a period, use the ADD PERIOD clause.\n\nFor example:\n\nCREATE OR REPLACE TABLE rooms (\n room_number INT,\n guest_name VARCHAR(255),\n checkin DATE,\n checkout DATE\n );\n\nALTER TABLE rooms ADD PERIOD FOR p(checkin,checkout);\n\nTo remove a period, use the DROP PERIOD clause:\n\nALTER TABLE rooms DROP PERIOD FOR p;\n\nBoth ADD PERIOD and DROP PERIOD clauses include an option to handle whether\nthe period already exists:\n\nALTER TABLE rooms ADD PERIOD IF NOT EXISTS FOR p(checkin,checkout);\n\nALTER TABLE rooms DROP PERIOD IF EXISTS FOR p;\n\nDeletion by Portion\n-------------------\n\nYou can also remove rows that fall within certain time periods.\n\nWhen MariaDB executes a DELETE FOR PORTION statement, it removes the row:\n\n* When the row period falls completely within the delete period, it removes\nthe row.\n* When the row period overlaps the delete period, it shrinks the row, removing\nthe overlap from the first or second row period value.\n* When the delete period falls completely within the row period, it splits the\nrow into two rows. The first row runs from the starting row period to the\nstarting delete period. The second runs from the ending delete period to the\nending row period.\n\nTo test this, first populate the table with some data to operate on:\n\nCREATE TABLE t1(\n name VARCHAR(50),\n date_1 DATE,\n date_2 DATE,\n PERIOD FOR date_period(date_1, date_2));\n\nINSERT INTO t1 (name, date_1, date_2) VALUES\n (\'a\', \'1999-01-01\', \'2000-01-01\'),\n (\'b\', \'1999-01-01\', \'2018-12-12\'),\n (\'c\', \'1999-01-01\', \'2017-01-01\'),\n (\'d\', \'2017-01-01\', \'2019-01-01\');\n\nSELECT * FROM t1;\n+------+------------+------------+\n| name | date_1 | date_2 |\n+------+------------+------------+\n| a | 1999-01-01 | 2000-01-01 |\n| b | 1999-01-01 | 2018-12-12 |\n| c | 1999-01-01 | 2017-01-01 |\n| d | 2017-01-01 | 2019-01-01 |\n+------+------------+------------+\n\nThen, run the DELETE FOR PORTION statement:\n\nDELETE FROM t1\nFOR PORTION OF date_period\n FROM \'2001-01-01\' TO \'2018-01-01\';\nQuery OK, 3 rows affected (0.028 sec)\n\nSELECT * FROM t1 ORDER BY name;\n+------+------------+------------+\n| name | date_1 | date_2 |\n+------+------------+------------+\n| a | 1999-01-01 | 2000-01-01 |\n| b | 1999-01-01 | 2001-01-01 |\n| b | 2018-01-01 | 2018-12-12 |\n| c | 1999-01-01 | 2001-01-01 |\n| d | 2018-01-01 | 2019-01-01 |\n+------+------------+------------+\n\nHere:\n\n* a is unchanged, as the range falls entirely out of the specified portion to\nbe deleted.\n* b, with values ranging from 1999 to 2018, is split into two rows, 1999 to\n2000 and 2018-01 to 2018-12.\n* c, with values ranging from 1999 to 2017, where only the upper value falls\nwithin the portion to be deleted, has been shrunk to 1999 to 2001.\n* d, with values ranging from 2017 to 2019, where only the lower value falls\nwithin the portion to be deleted, has been shrunk to 2018 to 2019.\n\nThe DELETE FOR PORTION statement has the following restrictions\n\n* The FROM...TO clause must be constant\n* Multi-delete is not supported\n\nIf there are DELETE or INSERT triggers, it works as follows: any matched row\nis deleted, and then one or two rows are inserted. If the record is deleted\ncompletely, nothing is inserted.\n\nUpdating by Portion\n-------------------\n\nThe UPDATE syntax now supports UPDATE FOR PORTION, which modifies rows based\non their occurrence in a range:\n\nTo test it, first populate the table with some data:\n\nTRUNCATE t1;\n\nINSERT INTO t1 (name, date_1, date_2) VALUES\n (\'a\', \'1999-01-01\', \'2000-01-01\'),\n (\'b\', \'1999-01-01\', \'2018-12-12\'),\n (\'c\', \'1999-01-01\', \'2017-01-01\'),\n (\'d\', \'2017-01-01\', \'2019-01-01\');\n\nSELECT * FROM t1;\n+------+------------+------------+\n| name | date_1 | date_2 |\n+------+------------+------------+\n| a | 1999-01-01 | 2000-01-01 |\n| b | 1999-01-01 | 2018-12-12 |\n| c | 1999-01-01 | 2017-01-01 |\n| d | 2017-01-01 | 2019-01-01 |\n+------+------------+------------+\n\nThen run the update:\n\nUPDATE t1 FOR PORTION OF date_period\n FROM \'2000-01-01\' TO \'2018-01-01\'\nSET name = CONCAT(name,\'_original\');\n\nSELECT * FROM t1 ORDER BY name;\n+------------+------------+------------+\n| name | date_1 | date_2 |\n+------------+------------+------------+\n| a | 1999-01-01 | 2000-01-01 |\n| b | 1999-01-01 | 2000-01-01 |\n| b | 2018-01-01 | 2018-12-12 |\n| b_original | 2000-01-01 | 2018-01-01 |\n| c | 1999-01-01 | 2000-01-01 |\n| c_original | 2000-01-01 | 2017-01-01 |\n| d | 2018-01-01 | 2019-01-01 |\n| d_original | 2017-01-01 | 2018-01-01 |\n+------------+------------+------------+\n\n* a is unchanged, as the range falls entirely out of the specified portion to\nbe deleted.\n* b, with values ranging from 1999 to 2018, is split into two rows, 1999 to\n2000 and 2018-01 to 2018-12.\n* c, with values ranging from 1999 to 2017, where only the upper value falls\nwithin the portion to be deleted, has been shrunk to 1999 to 2001.\n* d, with values ranging from 2017 to 2019, where only the lower value falls\nwithin the portion to be deleted, has been shrunk to 2018 to 2019. \n* Original rows affected by the update have \"_original\" appended to the name.\n\nThe UPDATE FOR PORTION statement has the following limitations:\n\n* The operation cannot modify the two temporal columns used by the time period\n* The operation cannot reference period values in the SET expression\n* FROM...TO expressions must be constant\n\nWITHOUT OVERLAPS\n----------------\n\nMariaDB starting with 10.5.3\n----------------------------\nMariaDB 10.5 introduced a new clause, WITHOUT OVERLAPS, which allows one to\ncreate an index specifying that application time periods should not overlap.\n\nAn index constrained by WITHOUT OVERLAPS is required to be either a primary\nkey or a unique index.\n\nTake the following example, an application time period table for a booking\nsystem:\n\nCREATE OR REPLACE TABLE rooms (\n room_number INT,\n guest_name VARCHAR(255),\n checkin DATE,\n checkout DATE,\n PERIOD FOR p(checkin,checkout)\n );\n\nINSERT INTO rooms VALUES \n (1, \'Regina\', \'2020-10-01\', \'2020-10-03\'),\n (2, \'Cochise\', \'2020-10-02\', \'2020-10-05\'),\n (1, \'Nowell\', \'2020-10-03\', \'2020-10-07\'),\n (2, \'Eusebius\', \'2020-10-04\', \'2020-10-06\');\n\nOur system is not intended to permit overlapping bookings, so the fourth\nrecord above should not have been inserted. Using WITHOUT OVERLAPS in a unique\nindex (in this case based on a combination of room number and the application\ntime period) allows us to specify this constraint in the table definition.\n\nCREATE OR REPLACE TABLE rooms (\n room_number INT,\n guest_name VARCHAR(255),\n checkin DATE,\n checkout DATE,\n PERIOD FOR p(checkin,checkout),\n UNIQUE (room_number, p WITHOUT OVERLAPS)\n );\n\nINSERT INTO rooms VALUES \n (1, \'Regina\', \'2020-10-01\', \'2020-10-03\'),\n (2, \'Cochise\', \'2020-10-02\', \'2020-10-05\'),\n (1, \'Nowell\', \'2020-10-03\', \'2020-10-07\'),\n (2, \'Eusebius\', \'2020-10-04\', \'2020-10-06\');\nERROR 1062 (23000): Duplicate entry \'2-2020-10-06-2020-10-04\' for key\n\'room_number\'\n\nFurther Examples\n----------------\n\nThe implicit change from NULL to NOT NULL:\n\nCREATE TABLE `t2` (\n `id` int(11) DEFAULT NULL,\n `d1` datetime DEFAULT NULL,\n `d2` datetime DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=latin1;\n\nALTER TABLE t2 ADD PERIOD FOR p(d1,d2);\n\nSHOW CREATE TABLE t2\\G\n*************************** 1. row ***************************\n Table: t2\nCreate Table: CREATE TABLE `t2` (\n `id` int(11) DEFAULT NULL,\n `d1` datetime NOT NULL,\n `d2` datetime NOT NULL,\n PERIOD FOR `p` (`d1`, `d2`)\n) ENGINE=InnoDB DEFAULT CHARSET=latin1\n\nDue to this constraint, trying to add a time period where null data already\nexists will fail.\n\nCREATE OR REPLACE TABLE `t2` (\n `id` int(11) DEFAULT NULL,\n `d1` datetime DEFAULT NULL,\n `d2` datetime DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=latin1;\n\nINSERT INTO t2(id) VALUES(1);\n\nALTER TABLE t2 ADD PERIOD FOR p(d1,d2);\nERROR 1265 (01000): Data truncated for column \'d1\' at row 1\n\nURL: https://mariadb.com/kb/en/application-time-periods/','','https://mariadb.com/kb/en/application-time-periods/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (805,45,'Bitemporal Tables','MariaDB starting with 10.4.3\n----------------------------\nBitemporal tables are tables that use versioning both at the system and\napplication-time period levels.\n\nUsing Bitemporal Tables\n-----------------------\n\nTo create a bitemporal table, use:\n\nCREATE TABLE test.t3 (\n date_1 DATE,\n date_2 DATE,\n row_start TIMESTAMP(6) AS ROW START INVISIBLE,\n row_end TIMESTAMP(6) AS ROW END INVISIBLE,\n PERIOD FOR application_time(date_1, date_2),\n PERIOD FOR system_time(row_start, row_end))\nWITH SYSTEM VERSIONING;\n\nNote that, while system_time here is also a time period, it cannot be used in\nDELETE FOR PORTION or UPDATE FOR PORTION statements.\n\nDELETE FROM test.t3 \nFOR PORTION OF system_time \n FROM \'2000-01-01\' TO \'2018-01-01\';\nERROR 42000: You have an error in your SQL syntax; check the manual that\ncorresponds \n to your MariaDB server version for the right syntax to use near\n \'of system_time from \'2000-01-01\' to \'2018-01-01\'\' at line 1\n\nURL: https://mariadb.com/kb/en/bitemporal-tables/','','https://mariadb.com/kb/en/bitemporal-tables/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (806,46,'ST_AsGeoJSON','Syntax\n------\n\nST_AsGeoJSON(g[, max_decimals[, options]])\n\nDescription\n-----------\n\nReturns the given geometry g as a GeoJSON element. The optional max_decimals\nlimits the maximum number of decimals displayed.\n\nThe optional options flag can be set to 1 to add a bounding box to the output.\n\nExamples\n--------\n\nSELECT ST_AsGeoJSON(ST_GeomFromText(\'POINT(5.3 7.2)\'));\n+-------------------------------------------------+\n| ST_AsGeoJSON(ST_GeomFromText(\'POINT(5.3 7.2)\')) |\n+-------------------------------------------------+\n| {\"type\": \"Point\", \"coordinates\": [5.3, 7.2]} |\n+-------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/geojson-st_asgeojson/','','https://mariadb.com/kb/en/geojson-st_asgeojson/'); @@ -925,9 +925,9 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (811,48,'Modulo Operator (%)','Syntax\n------\n\nN % M\n\nDescription\n-----------\n\nModulo operator. Returns the remainder of N divided by M. See also MOD.\n\nExamples\n--------\n\nSELECT 1042 % 50;\n+-----------+\n| 1042 % 50 |\n+-----------+\n| 42 |\n+-----------+\n\nURL: https://mariadb.com/kb/en/modulo-operator/','','https://mariadb.com/kb/en/modulo-operator/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (812,48,'Multiplication Operator (*)','Syntax\n------\n\n*\n\nDescription\n-----------\n\nMultiplication operator.\n\nExamples\n--------\n\nSELECT 7*6;\n+-----+\n| 7*6 |\n+-----+\n| 42 |\n+-----+\n\nSELECT 1234567890*9876543210;\n+-----------------------+\n| 1234567890*9876543210 |\n+-----------------------+\n| -6253480962446024716 |\n+-----------------------+\n\nSELECT 18014398509481984*18014398509481984.0;\n+---------------------------------------+\n| 18014398509481984*18014398509481984.0 |\n+---------------------------------------+\n| 324518553658426726783156020576256.0 |\n+---------------------------------------+\n\nSELECT 18014398509481984*18014398509481984;\n+-------------------------------------+\n| 18014398509481984*18014398509481984 |\n+-------------------------------------+\n| 0 |\n+-------------------------------------+\n\nURL: https://mariadb.com/kb/en/multiplication-operator/','','https://mariadb.com/kb/en/multiplication-operator/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (813,48,'Subtraction Operator (-)','Syntax\n------\n\n-\n\nDescription\n-----------\n\nSubtraction. The operator is also used as the unary minus for changing sign.\n\nIf both operands are integers, the result is calculated with BIGINT precision.\nIf either integer is unsigned, the result is also an unsigned integer, unless\nthe NO_UNSIGNED_SUBTRACTION SQL_MODE is enabled, in which case the result is\nalways signed.\n\nFor real or string operands, the operand with the highest precision determines\nthe result precision.\n\nExamples\n--------\n\nSELECT 96-9;\n+------+\n| 96-9 |\n+------+\n| 87 |\n+------+\n\nSELECT 15-17;\n+-------+\n| 15-17 |\n+-------+\n| -2 |\n+-------+\n\nSELECT 3.66 + 1.333;\n+--------------+\n| 3.66 + 1.333 |\n+--------------+\n| 4.993 |\n+--------------+\n\nUnary minus:\n\nSELECT - (3+5);\n+---------+\n| - (3+5) |\n+---------+\n| -8 |\n+---------+\n\nURL: https://mariadb.com/kb/en/subtraction-operator-/','','https://mariadb.com/kb/en/subtraction-operator-/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (814,49,'CHANGE MASTER TO','The terms master and slave have historically been used in replication, but the\nterms terms primary and replica are now preferred. The old terms are used\nstill used in parts of the documentation, and in MariaDB commands, although\nMariaDB 10.5 has begun the process of renaming. The documentation process is\nongoing. See MDEV-18777 to follow progress on this effort.\n\nSyntax\n------\n\nCHANGE MASTER [\'connection_name\'] TO master_def [, master_def] ... \n [FOR CHANNEL \'channel_name\']\n\nmaster_def:\n MASTER_BIND = \'interface_name\'\n | MASTER_HOST = \'host_name\'\n | MASTER_USER = \'user_name\'\n | MASTER_PASSWORD = \'password\'\n | MASTER_PORT = port_num\n | MASTER_CONNECT_RETRY = interval\n | MASTER_HEARTBEAT_PERIOD = interval\n | MASTER_LOG_FILE = \'master_log_name\'\n | MASTER_LOG_POS = master_log_pos\n | RELAY_LOG_FILE = \'relay_log_name\'\n | RELAY_LOG_POS = relay_log_pos\n | MASTER_DELAY = interval\n | MASTER_SSL = {0|1}\n | MASTER_SSL_CA = \'ca_file_name\'\n | MASTER_SSL_CAPATH = \'ca_directory_name\'\n | MASTER_SSL_CERT = \'cert_file_name\'\n | MASTER_SSL_CRL = \'crl_file_name\'\n | MASTER_SSL_CRLPATH = \'crl_directory_name\'\n | MASTER_SSL_KEY = \'key_file_name\'\n | MASTER_SSL_CIPHER = \'cipher_list\'\n | MASTER_SSL_VERIFY_SERVER_CERT = {0|1}\n | MASTER_USE_GTID = {current_pos|slave_pos|no}\n | MASTER_DEMOTE_TO_SLAVE = bool\n | IGNORE_SERVER_IDS = (server_id_list)\n | DO_DOMAIN_IDS = ([N,..])\n | IGNORE_DOMAIN_IDS = ([N,..])\n\nDescription\n-----------\n\nThe CHANGE MASTER statement sets the options that a replica uses to connect to\nand replicate from a primary.\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nto using the channel_name directly after CHANGE MASTER.\n\nMulti-Source Replication\n------------------------\n\nIf you are using multi-source replication, then you need to specify a\nconnection name when you execute CHANGE MASTER. There are two ways to do this:\n\n* Setting the default_master_connection system variable prior to executing\nCHANGE MASTER.\n* Setting the connection_name parameter when executing CHANGE MASTER.\n\ndefault_master_connection\n-------------------------\n\nSET default_master_connection = \'gandalf\';\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nconnection_name\n---------------\n\nSTOP SLAVE \'gandalf\';\nCHANGE MASTER \'gandalf\' TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE \'gandalf\';\n\nOptions\n-------\n\nConnection Options\n------------------\n\nMASTER_USER\n-----------\n\nThe MASTER_USER option for CHANGE MASTER defines the user account that the\nreplica will use to connect to the primary.\n\nThis user account will need the REPLICATION SLAVE privilege (or, from MariaDB\n10.5.1, the REPLICATION REPLICA on the primary.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_USER=\'repl\',\n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nThe maximum length of the MASTER_USER string is 96 characters until MariaDB\n10.5, and 128 characters from MariaDB 10.6.\n\nMASTER_PASSWORD\n---------------\n\nThe MASTER_USER option for CHANGE MASTER defines the password that the replica\nwill use to connect to the primary as the user account defined by the\nMASTER_USER option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nThe maximum length of the MASTER_PASSWORD string is 32 characters.\n\nMASTER_HOST\n-----------\n\nThe MASTER_HOST option for CHANGE MASTER defines the hostname or IP address of\nthe primary.\n\nIf you set the value of the MASTER_HOST option to the empty string, then that\nis not the same as not setting the option\'s value at all. If you set the value\nof the MASTER_HOST option to the empty string, then the CHANGE MASTER command\nwill fail with an error. In MariaDB 5.3 and before, if you set the value of\nthe MASTER_HOST option to the empty string, then the CHANGE MASTER command\nwould succeed, but the subsequent START SLAVE command would fail.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_HOST=\'dbserver1.example.com\',\n MASTER_USER=\'repl\',\n MASTER_PASSWORD=\'new3cret\',\n MASTER_USE_GTID=slave_pos;\nSTART SLAVE;\n\nIf you set the value of the MASTER_HOST option in a CHANGE MASTER command,\nthen the replica assumes that the primary is different from before, even if\nyou set the value of this option to the same value it had previously. In this\nscenario, the replica will consider the old values for the primary\'s binary\nlog file name and position to be invalid for the new primary. As a side\neffect, if you do not explicitly set the values of the MASTER_LOG_FILE and\nMASTER_LOG_POS options in the statement, then the statement will be implicitly\nappended with MASTER_LOG_FILE=\'\' and MASTER_LOG_POS=4. However, if you enable\nGTID mode for replication by setting the MASTER_USE_GTID option to some value\nother than no in the statement, then these values will effectively be ignored\nanyway.\n\nReplicas cannot connect to primaries using Unix socket files or Windows named\npipes. The replica must connect to the primary using TCP/IP.\n\nThe maximum length of the MASTER_HOST string is 60 characters until MariaDB\n10.5, and 255 characters from MariaDB 10.6.\n\nMASTER_PORT\n-----------\n\nThe MASTER_PORT option for CHANGE MASTER defines the TCP/IP port of the\nprimary.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_HOST=\'dbserver1.example.com\',\n MASTER_PORT=3307,\n MASTER_USER=\'repl\',\n MASTER_PASSWORD=\'new3cret\',\n MASTER_USE_GTID=slave_pos;\nSTART SLAVE;\n\nIf you set the value of the MASTER_PORT option in a CHANGE MASTER command,\nthen the replica assumes that the primary is different from before, even if\nyou set the value of this option to the same value it had previously. In this\nscenario, the replica will consider the old values for the primary\'s binary\nlog file name and position to be invalid for the new primary. As a side\neffect, if you do not explicitly set the values of the MASTER_LOG_FILE and\nMASTER_LOG_POS options in the statement, then the statement will be implicitly\nappended with MASTER_LOG_FILE=\'\' and MASTER_LOG_POS=4. However, if you enable\nGTID mode for replication by setting the MASTER_USE_GTID option to some value\nother than no in the statement, then these values will effectively be ignored\nanyway.\n\nReplicas cannot connect to primaries using Unix socket files or Windows named\npipes. The replica must connect to the primary using TCP/IP.\n\nMASTER_CONNECT_RETRY\n--------------------\n\nThe MASTER_CONNECT_RETRY option for CHANGE MASTER defines how many seconds\nthat the replica will wait between connection retries. The default is 60.\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_CONNECT_RETRY=20;\nSTART SLAVE;\n\nThe number of connection attempts is limited by the master_retry_count option.\nIt can be set either on the command-line or in a server option group in an\noption file prior to starting up the server. For example:\n\n[mariadb]\n...\nmaster_retry_count=4294967295\n\nMASTER_BIND\n-----------\n\nThe MASTER_BIND option for CHANGE MASTER is only supported by MySQL 5.6.2 and\nlater and by MySQL NDB Cluster 7.3.1 and later. This option is not supported\nby MariaDB. See MDEV-19248 for more information.\n\nThe MASTER_BIND option for CHANGE MASTER can be used on replicas that have\nmultiple network interfaces to choose which network interface the replica will\nuse to connect to the primary.\n\nMASTER_HEARTBEAT_PERIOD\n-----------------------\n\nThe MASTER_HEARTBEAT_PERIOD option for CHANGE MASTER can be used to set the\ninterval in seconds between replication heartbeats. Whenever the primary\'s\nbinary log is updated with an event, the waiting period for the next heartbeat\nis reset.\n\nThis option\'s interval argument has the following characteristics:\n\n* It is a decimal value with a range of 0 to 4294967 seconds.\n* It has a resolution of hundredths of a second.\n* Its smallest valid non-zero value is 0.001.\n* Its default value is the value of the slave_net_timeout system variable\ndivided by 2.\n* If it\'s set to 0, then heartbeats are disabled.\n\nHeartbeats are sent by the primary only if there are no unsent events in the\nbinary log file for a period longer than the interval.\n\nIf the RESET SLAVE statement is executed, then the heartbeat interval is reset\nto the default.\n\nIf the slave_net_timeout system variable is set to a value that is lower than\nthe current heartbeat interval, then a warning will be issued.\n\nTLS Options\n-----------\n\nThe TLS options are used for providing information about TLS. The options can\nbe set even on replicas that are compiled without TLS support. The TLS options\nare saved to either the default master.info file or the file that is\nconfigured by the master_info_file option, but these TLS options are ignored\nunless the replica supports TLS.\n\nSee Replication with Secure Connections for more information.\n\nMASTER_SSL\n----------\n\nThe MASTER_SSL option for CHANGE MASTER tells the replica whether to force TLS\nfor the connection. The valid values are 0 or 1.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL=1;\nSTART SLAVE;\n\nMASTER_SSL_CA\n-------------\n\nThe MASTER_SSL_CA option for CHANGE MASTER defines a path to a PEM file that\nshould contain one or more X509 certificates for trusted Certificate\nAuthorities (CAs) to use for TLS. This option requires that you use the\nabsolute path, not a relative path. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Authorities (CAs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CA string is 511 characters.\n\nMASTER_SSL_CAPATH\n-----------------\n\nThe MASTER_SSL_CAPATH option for CHANGE MASTER defines a path to a directory\nthat contains one or more PEM files that should each contain one X509\ncertificate for a trusted Certificate Authority (CA) to use for TLS. This\noption requires that you use the absolute path, not a relative path. The\ndirectory specified by this option needs to be run through the openssl rehash\ncommand. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CAPATH=\'/etc/my.cnf.d/certificates/ca/\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Authorities (CAs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CA_PATH string is 511 characters.\n\nMASTER_SSL_CERT\n---------------\n\nThe MASTER_SSL_CERT option for CHANGE MASTER defines a path to the X509\ncertificate file to use for TLS. This option requires that you use the\nabsolute path, not a relative path. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nThe maximum length of MASTER_SSL_CERT string is 511 characters.\n\nMASTER_SSL_CRL\n--------------\n\nThe MASTER_SSL_CRL option for CHANGE MASTER defines a path to a PEM file that\nshould contain one or more revoked X509 certificates to use for TLS. This\noption requires that you use the absolute path, not a relative path.\n\nThis option is only supported if the server was built with OpenSSL. If the\nserver was built with yaSSL, then this option is not supported. See TLS and\nCryptography Libraries Used by MariaDB for more information about which\nlibraries are used on which platforms.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1,\n MASTER_SSL_CRL=\'/etc/my.cnf.d/certificates/crl.pem\';\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Revocation Lists (CRLs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CRL string is 511 characters.\n\nMASTER_SSL_CRLPATH\n------------------\n\nThe MASTER_SSL_CRLPATH option for CHANGE MASTER defines a path to a directory\nthat contains one or more PEM files that should each contain one revoked X509\ncertificate to use for TLS. This option requires that you use the absolute\npath, not a relative path. The directory specified by this variable needs to\nbe run through the openssl rehash command.\n\nThis option is only supported if the server was built with OpenSSL. If the\nserver was built with yaSSL, then this option is not supported. See TLS and\nCryptography Libraries Used by MariaDB for more information about which\nlibraries are used on which platforms.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1,\n MASTER_SSL_CRLPATH=\'/etc/my.cnf.d/certificates/crl/\';\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Revocation Lists (CRLs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CRL_PATH string is 511 characters.\n\nMASTER_SSL_KEY\n--------------\n\nThe MASTER_SSL_KEY option for CHANGE MASTER defines a path to a private key\nfile to use for TLS. This option requires that you use the absolute path, not\na relative path. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nThe maximum length of MASTER_SSL_KEY string is 511 characters.\n\nMASTER_SSL_CIPHER\n-----------------\n\nThe MASTER_SSL_CIPHER option for CHANGE MASTER defines the list of permitted\nciphers or cipher suites to use for TLS. Besides cipher names, if MariaDB was','','https://mariadb.com/kb/en/change-master-to/'); -update help_topic set description = CONCAT(description, '\ncompiled with OpenSSL, this option could be set to \"SSLv3\" or \"TLSv1.2\" to\nallow all SSLv3 or all TLSv1.2 ciphers. Note that the TLSv1.3 ciphers cannot\nbe excluded when using OpenSSL, even by using this option. See Using TLSv1.3\nfor details. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1,\n MASTER_SSL_CIPHER=\'TLSv1.2\';\nSTART SLAVE;\n\nThe maximum length of MASTER_SSL_CIPHER string is 511 characters.\n\nMASTER_SSL_VERIFY_SERVER_CERT\n-----------------------------\n\nThe MASTER_SSL_VERIFY_SERVER_CERT option for CHANGE MASTER enables server\ncertificate verification. This option is disabled by default.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nSee Secure Connections Overview: Server Certificate Verification for more\ninformation.\n\nBinary Log Options\n------------------\n\nThese options are related to the binary log position on the primary.\n\nMASTER_LOG_FILE\n---------------\n\nThe MASTER_LOG_FILE option for CHANGE MASTER can be used along with\nMASTER_LOG_POS to specify the coordinates at which the replica\'s I/O thread\nshould begin reading from the primary\'s binary logs the next time the thread\nstarts.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4;\nSTART SLAVE;\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options cannot be specified if the\nRELAY_LOG_FILE and RELAY_LOG_POS options were also specified.\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options are effectively ignored if you\nenable GTID mode for replication by setting the MASTER_USE_GTID option to some\nvalue other than no in the statement.\n\nMASTER_LOG_POS\n--------------\n\nThe MASTER_LOG_POS option for CHANGE MASTER can be used along with\nMASTER_LOG_FILE to specify the coordinates at which the replica\'s I/O thread\nshould begin reading from the primary\'s binary logs the next time the thread\nstarts.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4;\nSTART SLAVE;\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options cannot be specified if the\nRELAY_LOG_FILE and RELAY_LOG_POS options were also specified.\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options are effectively ignored if you\nenable GTID mode for replication by setting the MASTER_USE_GTID option to some\nvalue other than no in the statement.\n\nRelay Log Options\n-----------------\n\nThese options are related to the relay log position on the replica.\n\nRELAY_LOG_FILE\n--------------\n\nThe RELAY_LOG_FILE option for CHANGE MASTER can be used along with the\nRELAY_LOG_POS option to specify the coordinates at which the replica\'s SQL\nthread should begin reading from the relay log the next time the thread starts.\n\nThe CHANGE MASTER statement usually deletes all relay log files. However, if\nthe RELAY_LOG_FILE and/or RELAY_LOG_POS options are specified, then existing\nrelay log files are kept.\n\nWhen you want to change the relay log position, you only need to stop the\nreplica\'s SQL thread. The replica\'s I/O thread can continue running. The STOP\nSLAVE and START SLAVE statements support the SQL_THREAD option for this\nscenario. For example:\n\nSTOP SLAVE SQL_THREAD;\nCHANGE MASTER TO\n RELAY_LOG_FILE=\'slave-relay-bin.006\',\n RELAY_LOG_POS=4025;\nSTART SLAVE SQL_THREAD;\n\nWhen the value of this option is changed, the metadata about the replica\'s SQL\nthread\'s position in the relay logs will also be changed in the relay-log.info\nfile or the file that is configured by the relay_log_info_file system variable.\n\nThe RELAY_LOG_FILE and RELAY_LOG_POS options cannot be specified if the\nMASTER_LOG_FILE and MASTER_LOG_POS options were also specified.\n\nRELAY_LOG_POS\n-------------\n\nThe RELAY_LOG_POS option for CHANGE MASTER can be used along with the\nRELAY_LOG_FILE option to specify the coordinates at which the replica\'s SQL\nthread should begin reading from the relay log the next time the thread starts.\n\nThe CHANGE MASTER statement usually deletes all relay log files. However, if\nthe RELAY_LOG_FILE and/or RELAY_LOG_POS options are specified, then existing\nrelay log files are kept.\n\nWhen you want to change the relay log position, you only need to stop the\nreplica\'s SQL thread. The replica\'s I/O thread can continue running. The STOP\nSLAVE and START SLAVE statements support the SQL_THREAD option for this\nscenario. For example:\n\nSTOP SLAVE SQL_THREAD;\nCHANGE MASTER TO\n RELAY_LOG_FILE=\'slave-relay-bin.006\',\n RELAY_LOG_POS=4025;\nSTART SLAVE SQL_THREAD;\n\nWhen the value of this option is changed, the metadata about the replica\'s SQL\nthread\'s position in the relay logs will also be changed in the relay-log.info\nfile or the file that is configured by the relay_log_info_file system variable.\n\nThe RELAY_LOG_FILE and RELAY_LOG_POS options cannot be specified if the\nMASTER_LOG_FILE and MASTER_LOG_POS options were also specified.\n\nGTID Options\n------------\n\nMASTER_USE_GTID\n---------------\n\nThe MASTER_USE_GTID option for CHANGE MASTER can be used to configure the\nreplica to use the global transaction ID (GTID) when connecting to a primary.\nThe possible values are:\n\n* current_pos - Replicate in GTID mode and use gtid_current_pos as the\nposition to start downloading transactions from the primary. Deprecated from\nMariaDB 10.10. Using to transition to primary can break the replication state\nif the replica executes local transactions due to actively updating\ngtid_current_pos with gtid_binlog_pos and gtid_slave_pos. Use the new, safe,\nMASTER_DEMOTE_TO_SLAVE= option instead.\n* slave_pos - Replicate in GTID mode and use gtid_slave_pos as the position to\nstart downloading transactions from the primary. From MariaDB 10.5.1,\nreplica_pos is an alias for slave_pos.\n* no - Don\'t replicate in GTID mode.\n\nMASTER_DEMOTE_TO_SLAVE\n----------------------\n\nMariaDB starting with 10.10\n---------------------------\nUsed to transition a primary to become a replica. Replaces the old\nMASTER_USE_GTID=current_pos with a safe alternative by forcing users to set\nUsing_Gtid=Slave_Pos and merging gtid_binlog_pos into gtid_slave_pos once at\nCHANGE MASTER TO time. If gtid_slave_pos is more recent than gtid_binlog_pos\n(as in the case of chain replication), the replication state should be\npreserved.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_USE_GTID = current_pos;\nSTART SLAVE;\n\nOr:\n\nSTOP SLAVE;\nSET GLOBAL gtid_slave_pos=\'0-1-153\';\nCHANGE MASTER TO\n MASTER_USE_GTID = slave_pos;\nSTART SLAVE;\n\nReplication Filter Options\n--------------------------\n\nAlso see Replication filters.\n\nIGNORE_SERVER_IDS\n-----------------\n\nThe IGNORE_SERVER_IDS option for CHANGE MASTER can be used to configure a\nreplica to ignore binary log events that originated from certain servers.\nFiltered binary log events will not get logged to the replica’s relay log, and\nthey will not be applied by the replica.\n\nThe option\'s value can be specified by providing a comma-separated list of\nserver_id values. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_SERVER_IDS = (3,5);\nSTART SLAVE;\n\nIf you would like to clear a previously set list, then you can set the value\nto an empty list. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_SERVER_IDS = ();\nSTART SLAVE;\n\nDO_DOMAIN_IDS\n-------------\n\nThe DO_DOMAIN_IDS option for CHANGE MASTER can be used to configure a replica\nto only apply binary log events if the transaction\'s GTID is in a specific\ngtid_domain_id value. Filtered binary log events will not get logged to the\nreplica’s relay log, and they will not be applied by the replica.\n\nThe option\'s value can be specified by providing a comma-separated list of\ngtid_domain_id values. Duplicate values are automatically ignored. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n DO_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nIf you would like to clear a previously set list, then you can set the value\nto an empty list. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n DO_DOMAIN_IDS = ();\nSTART SLAVE;\n\nThe DO_DOMAIN_IDS option and the IGNORE_DOMAIN_IDS option cannot both be set\nto non-empty values at the same time. If you want to set the DO_DOMAIN_IDS\noption, and the IGNORE_DOMAIN_IDS option was previously set, then you need to\nclear the value of the IGNORE_DOMAIN_IDS option. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_DOMAIN_IDS = (),\n DO_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nThe DO_DOMAIN_IDS option can only be specified if the replica is replicating\nin GTID mode. Therefore, the MASTER_USE_GTID option must also be set to some\nvalue other than no in order to use this option.\n\nIGNORE_DOMAIN_IDS\n-----------------\n\nThe IGNORE_DOMAIN_IDS option for CHANGE MASTER can be used to configure a\nreplica to ignore binary log events if the transaction\'s GTID is in a specific\ngtid_domain_id value. Filtered binary log events will not get logged to the\nreplica’s relay log, and they will not be applied by the replica.\n\nThe option\'s value can be specified by providing a comma-separated list of\ngtid_domain_id values. Duplicate values are automatically ignored. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nIf you would like to clear a previously set list, then you can set the value\nto an empty list. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_DOMAIN_IDS = ();\nSTART SLAVE;\n\nThe DO_DOMAIN_IDS option and the IGNORE_DOMAIN_IDS option cannot both be set\nto non-empty values at the same time. If you want to set the IGNORE_DOMAIN_IDS\noption, and the DO_DOMAIN_IDS option was previously set, then you need to\nclear the value of the DO_DOMAIN_IDS option. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n DO_DOMAIN_IDS = (),\n IGNORE_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nThe IGNORE_DOMAIN_IDS option can only be specified if the replica is\nreplicating in GTID mode. Therefore, the MASTER_USE_GTID option must also be\nset to some value other than no in order to use this option.\n\nDelayed Replication Options\n---------------------------\n\nMASTER_DELAY\n------------\n\nThe MASTER_DELAY option for CHANGE MASTER can be used to enable delayed\nreplication. This option specifies the time in seconds (at least) that a\nreplica should lag behind the primary up to a maximum value of 2147483647, or\nabout 68 years. Before executing an event, the replica will first wait, if\nnecessary, until the given time has passed since the event was created on the\nprimary. The result is that the replica will reflect the state of the primary\nsome time back in the past. The default is zero, no delay.\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_DELAY=3600;\nSTART SLAVE;\n\nChanging Option Values\n----------------------\n\nIf you don\'t specify a given option when executing the CHANGE MASTER\nstatement, then the option keeps its old value in most cases. Most of the\ntime, there is no need to specify the options that do not need to change. For\nexample, if the password for the user account that the replica uses to connect\nto its primary has changed, but no other options need to change, then you can\njust change the MASTER_PASSWORD option by executing the following commands:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nThere are some cases where options are implicitly reset, such as when the\nMASTER_HOST and MASTER_PORT options are changed.\n\nOption Persistence\n------------------\n\nThe values of the MASTER_LOG_FILE and MASTER_LOG_POS options (i.e. the binary\nlog position on the primary) and most other options are written to either the\ndefault master.info file or the file that is configured by the\nmaster_info_file option. The replica\'s I/O thread keeps this binary log\nposition updated as it downloads events only when MASTER_USE_GTID option is\nset to NO. Otherwise the file is not updated on a per event basis.\n\nThe master_info_file option can be set either on the command-line or in a\nserver option group in an option file prior to starting up the server. For\nexample:\n\n[mariadb]\n...\nmaster_info_file=/mariadb/myserver1-master.info\n\nThe values of the RELAY_LOG_FILE and RELAY_LOG_POS options (i.e. the relay log\nposition) are written to either the default relay-log.info file or the file\nthat is configured by the relay_log_info_file system variable. The replica\'s\nSQL thread keeps this relay log position updated as it applies events.\n\nThe relay_log_info_file system variable can be set either on the command-line\nor in a server option group in an option file prior to starting up the server.\nFor example:\n\n[mariadb]\n...\nrelay_log_info_file=/mariadb/myserver1-relay-log.info\n\nGTID Persistence\n----------------\n\nIf the replica is replicating binary log events that contain GTIDs, then the\nreplica\'s SQL thread will write every GTID that it applies to the\nmysql.gtid_slave_pos table. This GTID can be inspected and modified through\nthe gtid_slave_pos system variable.\n\nIf the replica has the log_slave_updates system variable enabled and if the\nreplica has the binary log enabled, then every write by the replica\'s SQL\nthread will also go into the replica\'s binary log. This means that GTIDs of\nreplicated transactions would be reflected in the value of the gtid_binlog_pos\nsystem variable.\n\nCreating a Replica from a Backup\n--------------------------------\n\nThe CHANGE MASTER statement is useful for setting up a replica when you have a\nbackup of the primary and you also have the binary log position or GTID\nposition corresponding to the backup.\n\nAfter restoring the backup on the replica, you could execute something like\nthis to use the binary log position:\n\nCHANGE MASTER TO\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4;\nSTART SLAVE;\n\nOr you could execute something like this to use the GTID position:\n\nSET GLOBAL gtid_slave_pos=\'0-1-153\';\nCHANGE MASTER TO\n MASTER_USE_GTID=slave_pos;\nSTART SLAVE;\n\nSee Setting up a Replication Slave with Mariabackup for more information on\nhow to do this with Mariabackup.\n\nExample') WHERE help_topic_id = 814; -update help_topic set description = CONCAT(description, '\n-------\n\nThe following example changes the primary and primary\'s binary log\ncoordinates. This is used when you want to set up the replica to replicate the\nprimary:\n\nCHANGE MASTER TO\n MASTER_HOST=\'master2.mycompany.com\',\n MASTER_USER=\'replication\',\n MASTER_PASSWORD=\'bigs3cret\',\n MASTER_PORT=3306,\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4,\n MASTER_CONNECT_RETRY=10;\nSTART SLAVE;\n\nURL: https://mariadb.com/kb/en/change-master-to/') WHERE help_topic_id = 814; +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (814,49,'CHANGE MASTER TO','The terms master and slave have historically been used in replication, but the\nterms terms primary and replica are now preferred. The old terms are used\nstill used in parts of the documentation, and in MariaDB commands, although\nMariaDB 10.5 has begun the process of renaming. The documentation process is\nongoing. See MDEV-18777 to follow progress on this effort.\n\nSyntax\n------\n\nCHANGE MASTER [\'connection_name\'] TO master_def [, master_def] ... \n [FOR CHANNEL \'channel_name\']\n\nmaster_def:\n MASTER_BIND = \'interface_name\'\n | MASTER_HOST = \'host_name\'\n | MASTER_USER = \'user_name\'\n | MASTER_PASSWORD = \'password\'\n | MASTER_PORT = port_num\n | MASTER_CONNECT_RETRY = interval\n | MASTER_HEARTBEAT_PERIOD = interval\n | MASTER_LOG_FILE = \'master_log_name\'\n | MASTER_LOG_POS = master_log_pos\n | RELAY_LOG_FILE = \'relay_log_name\'\n | RELAY_LOG_POS = relay_log_pos\n | MASTER_DELAY = interval\n | MASTER_SSL = {0|1}\n | MASTER_SSL_CA = \'ca_file_name\'\n | MASTER_SSL_CAPATH = \'ca_directory_name\'\n | MASTER_SSL_CERT = \'cert_file_name\'\n | MASTER_SSL_CRL = \'crl_file_name\'\n | MASTER_SSL_CRLPATH = \'crl_directory_name\'\n | MASTER_SSL_KEY = \'key_file_name\'\n | MASTER_SSL_CIPHER = \'cipher_list\'\n | MASTER_SSL_VERIFY_SERVER_CERT = {0|1}\n | MASTER_USE_GTID = {current_pos|slave_pos|no}\n | MASTER_DEMOTE_TO_SLAVE = bool\n | IGNORE_SERVER_IDS = (server_id_list)\n | DO_DOMAIN_IDS = ([N,..])\n | IGNORE_DOMAIN_IDS = ([N,..])\n\nDescription\n-----------\n\nThe CHANGE MASTER statement sets the options that a replica uses to connect to\nand replicate from a primary.\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nto using the channel_name directly after CHANGE MASTER.\n\nMulti-Source Replication\n------------------------\n\nIf you are using multi-source replication, then you need to specify a\nconnection name when you execute CHANGE MASTER. There are two ways to do this:\n\n* Setting the default_master_connection system variable prior to executing\nCHANGE MASTER.\n* Setting the connection_name parameter when executing CHANGE MASTER.\n\ndefault_master_connection\n-------------------------\n\nSET default_master_connection = \'gandalf\';\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nconnection_name\n---------------\n\nSTOP SLAVE \'gandalf\';\nCHANGE MASTER \'gandalf\' TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE \'gandalf\';\n\nOptions\n-------\n\nConnection Options\n------------------\n\nMASTER_USER\n-----------\n\nThe MASTER_USER option for CHANGE MASTER defines the user account that the\nreplica will use to connect to the primary.\n\nThis user account will need the REPLICATION SLAVE privilege (or, from MariaDB\n10.5.1, the REPLICATION REPLICA on the primary.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_USER=\'repl\',\n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nThe maximum length of the MASTER_USER string is 96 characters until MariaDB\n10.5, and 128 characters from MariaDB 10.6.\n\nMASTER_PASSWORD\n---------------\n\nThe MASTER_USER option for CHANGE MASTER defines the password that the replica\nwill use to connect to the primary as the user account defined by the\nMASTER_USER option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nThe maximum length of the MASTER_PASSWORD string is 32 characters. The\neffective maximum length of the string depends on how many bytes are used per\ncharacter and can be up to 96 characters.\n\nDue to MDEV-29994, the password can be silently truncated to 41 characters\nwhen MariaDB is restarted. For this reason it is recommended to use a password\nthat is shorter than this.\n\nMASTER_HOST\n-----------\n\nThe MASTER_HOST option for CHANGE MASTER defines the hostname or IP address of\nthe primary.\n\nIf you set the value of the MASTER_HOST option to the empty string, then that\nis not the same as not setting the option\'s value at all. If you set the value\nof the MASTER_HOST option to the empty string, then the CHANGE MASTER command\nwill fail with an error. In MariaDB 5.3 and before, if you set the value of\nthe MASTER_HOST option to the empty string, then the CHANGE MASTER command\nwould succeed, but the subsequent START SLAVE command would fail.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_HOST=\'dbserver1.example.com\',\n MASTER_USER=\'repl\',\n MASTER_PASSWORD=\'new3cret\',\n MASTER_USE_GTID=slave_pos;\nSTART SLAVE;\n\nIf you set the value of the MASTER_HOST option in a CHANGE MASTER command,\nthen the replica assumes that the primary is different from before, even if\nyou set the value of this option to the same value it had previously. In this\nscenario, the replica will consider the old values for the primary\'s binary\nlog file name and position to be invalid for the new primary. As a side\neffect, if you do not explicitly set the values of the MASTER_LOG_FILE and\nMASTER_LOG_POS options in the statement, then the statement will be implicitly\nappended with MASTER_LOG_FILE=\'\' and MASTER_LOG_POS=4. However, if you enable\nGTID mode for replication by setting the MASTER_USE_GTID option to some value\nother than no in the statement, then these values will effectively be ignored\nanyway.\n\nReplicas cannot connect to primaries using Unix socket files or Windows named\npipes. The replica must connect to the primary using TCP/IP.\n\nThe maximum length of the MASTER_HOST string is 60 characters until MariaDB\n10.5, and 255 characters from MariaDB 10.6.\n\nMASTER_PORT\n-----------\n\nThe MASTER_PORT option for CHANGE MASTER defines the TCP/IP port of the\nprimary.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_HOST=\'dbserver1.example.com\',\n MASTER_PORT=3307,\n MASTER_USER=\'repl\',\n MASTER_PASSWORD=\'new3cret\',\n MASTER_USE_GTID=slave_pos;\nSTART SLAVE;\n\nIf you set the value of the MASTER_PORT option in a CHANGE MASTER command,\nthen the replica assumes that the primary is different from before, even if\nyou set the value of this option to the same value it had previously. In this\nscenario, the replica will consider the old values for the primary\'s binary\nlog file name and position to be invalid for the new primary. As a side\neffect, if you do not explicitly set the values of the MASTER_LOG_FILE and\nMASTER_LOG_POS options in the statement, then the statement will be implicitly\nappended with MASTER_LOG_FILE=\'\' and MASTER_LOG_POS=4. However, if you enable\nGTID mode for replication by setting the MASTER_USE_GTID option to some value\nother than no in the statement, then these values will effectively be ignored\nanyway.\n\nReplicas cannot connect to primaries using Unix socket files or Windows named\npipes. The replica must connect to the primary using TCP/IP.\n\nMASTER_CONNECT_RETRY\n--------------------\n\nThe MASTER_CONNECT_RETRY option for CHANGE MASTER defines how many seconds\nthat the replica will wait between connection retries. The default is 60.\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_CONNECT_RETRY=20;\nSTART SLAVE;\n\nThe number of connection attempts is limited by the master_retry_count option.\nIt can be set either on the command-line or in a server option group in an\noption file prior to starting up the server. For example:\n\n[mariadb]\n...\nmaster_retry_count=4294967295\n\nMASTER_BIND\n-----------\n\nThe MASTER_BIND option for CHANGE MASTER is only supported by MySQL 5.6.2 and\nlater and by MySQL NDB Cluster 7.3.1 and later. This option is not supported\nby MariaDB. See MDEV-19248 for more information.\n\nThe MASTER_BIND option for CHANGE MASTER can be used on replicas that have\nmultiple network interfaces to choose which network interface the replica will\nuse to connect to the primary.\n\nMASTER_HEARTBEAT_PERIOD\n-----------------------\n\nThe MASTER_HEARTBEAT_PERIOD option for CHANGE MASTER can be used to set the\ninterval in seconds between replication heartbeats. Whenever the primary\'s\nbinary log is updated with an event, the waiting period for the next heartbeat\nis reset.\n\nThis option\'s interval argument has the following characteristics:\n\n* It is a decimal value with a range of 0 to 4294967 seconds.\n* It has a resolution of hundredths of a second.\n* Its smallest valid non-zero value is 0.001.\n* Its default value is the value of the slave_net_timeout system variable\ndivided by 2.\n* If it\'s set to 0, then heartbeats are disabled.\n\nHeartbeats are sent by the primary only if there are no unsent events in the\nbinary log file for a period longer than the interval.\n\nIf the RESET SLAVE statement is executed, then the heartbeat interval is reset\nto the default.\n\nIf the slave_net_timeout system variable is set to a value that is lower than\nthe current heartbeat interval, then a warning will be issued.\n\nTLS Options\n-----------\n\nThe TLS options are used for providing information about TLS. The options can\nbe set even on replicas that are compiled without TLS support. The TLS options\nare saved to either the default master.info file or the file that is\nconfigured by the master_info_file option, but these TLS options are ignored\nunless the replica supports TLS.\n\nSee Replication with Secure Connections for more information.\n\nMASTER_SSL\n----------\n\nThe MASTER_SSL option for CHANGE MASTER tells the replica whether to force TLS\nfor the connection. The valid values are 0 or 1.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL=1;\nSTART SLAVE;\n\nMASTER_SSL_CA\n-------------\n\nThe MASTER_SSL_CA option for CHANGE MASTER defines a path to a PEM file that\nshould contain one or more X509 certificates for trusted Certificate\nAuthorities (CAs) to use for TLS. This option requires that you use the\nabsolute path, not a relative path. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Authorities (CAs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CA string is 511 characters.\n\nMASTER_SSL_CAPATH\n-----------------\n\nThe MASTER_SSL_CAPATH option for CHANGE MASTER defines a path to a directory\nthat contains one or more PEM files that should each contain one X509\ncertificate for a trusted Certificate Authority (CA) to use for TLS. This\noption requires that you use the absolute path, not a relative path. The\ndirectory specified by this option needs to be run through the openssl rehash\ncommand. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CAPATH=\'/etc/my.cnf.d/certificates/ca/\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Authorities (CAs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CA_PATH string is 511 characters.\n\nMASTER_SSL_CERT\n---------------\n\nThe MASTER_SSL_CERT option for CHANGE MASTER defines a path to the X509\ncertificate file to use for TLS. This option requires that you use the\nabsolute path, not a relative path. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nThe maximum length of MASTER_SSL_CERT string is 511 characters.\n\nMASTER_SSL_CRL\n--------------\n\nThe MASTER_SSL_CRL option for CHANGE MASTER defines a path to a PEM file that\nshould contain one or more revoked X509 certificates to use for TLS. This\noption requires that you use the absolute path, not a relative path.\n\nThis option is only supported if the server was built with OpenSSL. If the\nserver was built with yaSSL, then this option is not supported. See TLS and\nCryptography Libraries Used by MariaDB for more information about which\nlibraries are used on which platforms.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1,\n MASTER_SSL_CRL=\'/etc/my.cnf.d/certificates/crl.pem\';\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Revocation Lists (CRLs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CRL string is 511 characters.\n\nMASTER_SSL_CRLPATH\n------------------\n\nThe MASTER_SSL_CRLPATH option for CHANGE MASTER defines a path to a directory\nthat contains one or more PEM files that should each contain one revoked X509\ncertificate to use for TLS. This option requires that you use the absolute\npath, not a relative path. The directory specified by this variable needs to\nbe run through the openssl rehash command.\n\nThis option is only supported if the server was built with OpenSSL. If the\nserver was built with yaSSL, then this option is not supported. See TLS and\nCryptography Libraries Used by MariaDB for more information about which\nlibraries are used on which platforms.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1,\n MASTER_SSL_CRLPATH=\'/etc/my.cnf.d/certificates/crl/\';\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Revocation Lists (CRLs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CRL_PATH string is 511 characters.\n\nMASTER_SSL_KEY\n--------------\n\nThe MASTER_SSL_KEY option for CHANGE MASTER defines a path to a private key\nfile to use for TLS. This option requires that you use the absolute path, not\na relative path. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;','','https://mariadb.com/kb/en/change-master-to/'); +update help_topic set description = CONCAT(description, '\nSTART SLAVE;\n\nThe maximum length of MASTER_SSL_KEY string is 511 characters.\n\nMASTER_SSL_CIPHER\n-----------------\n\nThe MASTER_SSL_CIPHER option for CHANGE MASTER defines the list of permitted\nciphers or cipher suites to use for TLS. Besides cipher names, if MariaDB was\ncompiled with OpenSSL, this option could be set to \"SSLv3\" or \"TLSv1.2\" to\nallow all SSLv3 or all TLSv1.2 ciphers. Note that the TLSv1.3 ciphers cannot\nbe excluded when using OpenSSL, even by using this option. See Using TLSv1.3\nfor details. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1,\n MASTER_SSL_CIPHER=\'TLSv1.2\';\nSTART SLAVE;\n\nThe maximum length of MASTER_SSL_CIPHER string is 511 characters.\n\nMASTER_SSL_VERIFY_SERVER_CERT\n-----------------------------\n\nThe MASTER_SSL_VERIFY_SERVER_CERT option for CHANGE MASTER enables server\ncertificate verification. This option is disabled by default.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nSee Secure Connections Overview: Server Certificate Verification for more\ninformation.\n\nBinary Log Options\n------------------\n\nThese options are related to the binary log position on the primary.\n\nMASTER_LOG_FILE\n---------------\n\nThe MASTER_LOG_FILE option for CHANGE MASTER can be used along with\nMASTER_LOG_POS to specify the coordinates at which the replica\'s I/O thread\nshould begin reading from the primary\'s binary logs the next time the thread\nstarts.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4;\nSTART SLAVE;\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options cannot be specified if the\nRELAY_LOG_FILE and RELAY_LOG_POS options were also specified.\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options are effectively ignored if you\nenable GTID mode for replication by setting the MASTER_USE_GTID option to some\nvalue other than no in the statement.\n\nMASTER_LOG_POS\n--------------\n\nThe MASTER_LOG_POS option for CHANGE MASTER can be used along with\nMASTER_LOG_FILE to specify the coordinates at which the replica\'s I/O thread\nshould begin reading from the primary\'s binary logs the next time the thread\nstarts.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4;\nSTART SLAVE;\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options cannot be specified if the\nRELAY_LOG_FILE and RELAY_LOG_POS options were also specified.\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options are effectively ignored if you\nenable GTID mode for replication by setting the MASTER_USE_GTID option to some\nvalue other than no in the statement.\n\nRelay Log Options\n-----------------\n\nThese options are related to the relay log position on the replica.\n\nRELAY_LOG_FILE\n--------------\n\nThe RELAY_LOG_FILE option for CHANGE MASTER can be used along with the\nRELAY_LOG_POS option to specify the coordinates at which the replica\'s SQL\nthread should begin reading from the relay log the next time the thread starts.\n\nThe CHANGE MASTER statement usually deletes all relay log files. However, if\nthe RELAY_LOG_FILE and/or RELAY_LOG_POS options are specified, then existing\nrelay log files are kept.\n\nWhen you want to change the relay log position, you only need to stop the\nreplica\'s SQL thread. The replica\'s I/O thread can continue running. The STOP\nSLAVE and START SLAVE statements support the SQL_THREAD option for this\nscenario. For example:\n\nSTOP SLAVE SQL_THREAD;\nCHANGE MASTER TO\n RELAY_LOG_FILE=\'slave-relay-bin.006\',\n RELAY_LOG_POS=4025;\nSTART SLAVE SQL_THREAD;\n\nWhen the value of this option is changed, the metadata about the replica\'s SQL\nthread\'s position in the relay logs will also be changed in the relay-log.info\nfile or the file that is configured by the relay_log_info_file system variable.\n\nThe RELAY_LOG_FILE and RELAY_LOG_POS options cannot be specified if the\nMASTER_LOG_FILE and MASTER_LOG_POS options were also specified.\n\nRELAY_LOG_POS\n-------------\n\nThe RELAY_LOG_POS option for CHANGE MASTER can be used along with the\nRELAY_LOG_FILE option to specify the coordinates at which the replica\'s SQL\nthread should begin reading from the relay log the next time the thread starts.\n\nThe CHANGE MASTER statement usually deletes all relay log files. However, if\nthe RELAY_LOG_FILE and/or RELAY_LOG_POS options are specified, then existing\nrelay log files are kept.\n\nWhen you want to change the relay log position, you only need to stop the\nreplica\'s SQL thread. The replica\'s I/O thread can continue running. The STOP\nSLAVE and START SLAVE statements support the SQL_THREAD option for this\nscenario. For example:\n\nSTOP SLAVE SQL_THREAD;\nCHANGE MASTER TO\n RELAY_LOG_FILE=\'slave-relay-bin.006\',\n RELAY_LOG_POS=4025;\nSTART SLAVE SQL_THREAD;\n\nWhen the value of this option is changed, the metadata about the replica\'s SQL\nthread\'s position in the relay logs will also be changed in the relay-log.info\nfile or the file that is configured by the relay_log_info_file system variable.\n\nThe RELAY_LOG_FILE and RELAY_LOG_POS options cannot be specified if the\nMASTER_LOG_FILE and MASTER_LOG_POS options were also specified.\n\nGTID Options\n------------\n\nMASTER_USE_GTID\n---------------\n\nThe MASTER_USE_GTID option for CHANGE MASTER can be used to configure the\nreplica to use the global transaction ID (GTID) when connecting to a primary.\nThe possible values are:\n\n* current_pos - Replicate in GTID mode and use gtid_current_pos as the\nposition to start downloading transactions from the primary. Deprecated from\nMariaDB 10.10. Using to transition to primary can break the replication state\nif the replica executes local transactions due to actively updating\ngtid_current_pos with gtid_binlog_pos and gtid_slave_pos. Use the new, safe,\nMASTER_DEMOTE_TO_SLAVE= option instead.\n* slave_pos - Replicate in GTID mode and use gtid_slave_pos as the position to\nstart downloading transactions from the primary. From MariaDB 10.5.1,\nreplica_pos is an alias for slave_pos.\n* no - Don\'t replicate in GTID mode.\n\nMASTER_DEMOTE_TO_SLAVE\n----------------------\n\nMariaDB starting with 10.10\n---------------------------\nUsed to transition a primary to become a replica. Replaces the old\nMASTER_USE_GTID=current_pos with a safe alternative by forcing users to set\nUsing_Gtid=Slave_Pos and merging gtid_binlog_pos into gtid_slave_pos once at\nCHANGE MASTER TO time. If gtid_slave_pos is more recent than gtid_binlog_pos\n(as in the case of chain replication), the replication state should be\npreserved.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_USE_GTID = current_pos;\nSTART SLAVE;\n\nOr:\n\nSTOP SLAVE;\nSET GLOBAL gtid_slave_pos=\'0-1-153\';\nCHANGE MASTER TO\n MASTER_USE_GTID = slave_pos;\nSTART SLAVE;\n\nReplication Filter Options\n--------------------------\n\nAlso see Replication filters.\n\nIGNORE_SERVER_IDS\n-----------------\n\nThe IGNORE_SERVER_IDS option for CHANGE MASTER can be used to configure a\nreplica to ignore binary log events that originated from certain servers.\nFiltered binary log events will not get logged to the replica’s relay log, and\nthey will not be applied by the replica.\n\nThe option\'s value can be specified by providing a comma-separated list of\nserver_id values. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_SERVER_IDS = (3,5);\nSTART SLAVE;\n\nIf you would like to clear a previously set list, then you can set the value\nto an empty list. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_SERVER_IDS = ();\nSTART SLAVE;\n\nDO_DOMAIN_IDS\n-------------\n\nThe DO_DOMAIN_IDS option for CHANGE MASTER can be used to configure a replica\nto only apply binary log events if the transaction\'s GTID is in a specific\ngtid_domain_id value. Filtered binary log events will not get logged to the\nreplica’s relay log, and they will not be applied by the replica.\n\nThe option\'s value can be specified by providing a comma-separated list of\ngtid_domain_id values. Duplicate values are automatically ignored. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n DO_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nIf you would like to clear a previously set list, then you can set the value\nto an empty list. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n DO_DOMAIN_IDS = ();\nSTART SLAVE;\n\nThe DO_DOMAIN_IDS option and the IGNORE_DOMAIN_IDS option cannot both be set\nto non-empty values at the same time. If you want to set the DO_DOMAIN_IDS\noption, and the IGNORE_DOMAIN_IDS option was previously set, then you need to\nclear the value of the IGNORE_DOMAIN_IDS option. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_DOMAIN_IDS = (),\n DO_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nThe DO_DOMAIN_IDS option can only be specified if the replica is replicating\nin GTID mode. Therefore, the MASTER_USE_GTID option must also be set to some\nvalue other than no in order to use this option.\n\nIGNORE_DOMAIN_IDS\n-----------------\n\nThe IGNORE_DOMAIN_IDS option for CHANGE MASTER can be used to configure a\nreplica to ignore binary log events if the transaction\'s GTID is in a specific\ngtid_domain_id value. Filtered binary log events will not get logged to the\nreplica’s relay log, and they will not be applied by the replica.\n\nThe option\'s value can be specified by providing a comma-separated list of\ngtid_domain_id values. Duplicate values are automatically ignored. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nIf you would like to clear a previously set list, then you can set the value\nto an empty list. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_DOMAIN_IDS = ();\nSTART SLAVE;\n\nThe DO_DOMAIN_IDS option and the IGNORE_DOMAIN_IDS option cannot both be set\nto non-empty values at the same time. If you want to set the IGNORE_DOMAIN_IDS\noption, and the DO_DOMAIN_IDS option was previously set, then you need to\nclear the value of the DO_DOMAIN_IDS option. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n DO_DOMAIN_IDS = (),\n IGNORE_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nThe IGNORE_DOMAIN_IDS option can only be specified if the replica is\nreplicating in GTID mode. Therefore, the MASTER_USE_GTID option must also be\nset to some value other than no in order to use this option.\n\nDelayed Replication Options\n---------------------------\n\nMASTER_DELAY\n------------\n\nThe MASTER_DELAY option for CHANGE MASTER can be used to enable delayed\nreplication. This option specifies the time in seconds (at least) that a\nreplica should lag behind the primary up to a maximum value of 2147483647, or\nabout 68 years. Before executing an event, the replica will first wait, if\nnecessary, until the given time has passed since the event was created on the\nprimary. The result is that the replica will reflect the state of the primary\nsome time back in the past. The default is zero, no delay.\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_DELAY=3600;\nSTART SLAVE;\n\nChanging Option Values\n----------------------\n\nIf you don\'t specify a given option when executing the CHANGE MASTER\nstatement, then the option keeps its old value in most cases. Most of the\ntime, there is no need to specify the options that do not need to change. For\nexample, if the password for the user account that the replica uses to connect\nto its primary has changed, but no other options need to change, then you can\njust change the MASTER_PASSWORD option by executing the following commands:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nThere are some cases where options are implicitly reset, such as when the\nMASTER_HOST and MASTER_PORT options are changed.\n\nOption Persistence\n------------------\n\nThe values of the MASTER_LOG_FILE and MASTER_LOG_POS options (i.e. the binary\nlog position on the primary) and most other options are written to either the\ndefault master.info file or the file that is configured by the\nmaster_info_file option. The replica\'s I/O thread keeps this binary log\nposition updated as it downloads events only when MASTER_USE_GTID option is\nset to NO. Otherwise the file is not updated on a per event basis.\n\nThe master_info_file option can be set either on the command-line or in a\nserver option group in an option file prior to starting up the server. For\nexample:\n\n[mariadb]\n...\nmaster_info_file=/mariadb/myserver1-master.info\n\nThe values of the RELAY_LOG_FILE and RELAY_LOG_POS options (i.e. the relay log\nposition) are written to either the default relay-log.info file or the file\nthat is configured by the relay_log_info_file system variable. The replica\'s\nSQL thread keeps this relay log position updated as it applies events.\n\nThe relay_log_info_file system variable can be set either on the command-line\nor in a server option group in an option file prior to starting up the server.\nFor example:\n\n[mariadb]\n...\nrelay_log_info_file=/mariadb/myserver1-relay-log.info\n\nGTID Persistence\n----------------\n\nIf the replica is replicating binary log events that contain GTIDs, then the\nreplica\'s SQL thread will write every GTID that it applies to the\nmysql.gtid_slave_pos table. This GTID can be inspected and modified through\nthe gtid_slave_pos system variable.\n\nIf the replica has the log_slave_updates system variable enabled and if the\nreplica has the binary log enabled, then every write by the replica\'s SQL\nthread will also go into the replica\'s binary log. This means that GTIDs of\nreplicated transactions would be reflected in the value of the gtid_binlog_pos\nsystem variable.\n\nCreating a Replica from a Backup\n--------------------------------\n\nThe CHANGE MASTER statement is useful for setting up a replica when you have a\nbackup of the primary and you also have the binary log position or GTID\nposition corresponding to the backup.\n\nAfter restoring the backup on the replica, you could execute something like\nthis to use the binary log position:\n\nCHANGE MASTER TO\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4;\nSTART SLAVE;\n') WHERE help_topic_id = 814; +update help_topic set description = CONCAT(description, '\nOr you could execute something like this to use the GTID position:\n\nSET GLOBAL gtid_slave_pos=\'0-1-153\';\nCHANGE MASTER TO\n MASTER_USE_GTID=slave_pos;\nSTART SLAVE;\n\nSee Setting up a Replication Slave with Mariabackup for more information on\nhow to do this with Mariabackup.\n\nExample\n-------\n\nThe following example changes the primary and primary\'s binary log\ncoordinates. This is used when you want to set up the replica to replicate the\nprimary:\n\nCHANGE MASTER TO\n MASTER_HOST=\'master2.mycompany.com\',\n MASTER_USER=\'replication\',\n MASTER_PASSWORD=\'bigs3cret\',\n MASTER_PORT=3306,\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4,\n MASTER_CONNECT_RETRY=10;\nSTART SLAVE;\n\nURL: https://mariadb.com/kb/en/change-master-to/') WHERE help_topic_id = 814; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (815,49,'START SLAVE','The terms master and slave have historically been used in replication, but the\nterms terms primary and replica are now preferred. The old terms are used\nstill used in parts of the documentation, and in MariaDB commands, although\nMariaDB 10.5 has begun the process of renaming. The documentation process is\nongoing. See MDEV-18777 to follow progress on this effort.\n\nSyntax\n------\n\nSTART SLAVE [\"connection_name\"] [thread_type [, thread_type] ... ] [FOR\nCHANNEL \"connection_name\"]\nSTART SLAVE [\"connection_name\"] [SQL_THREAD] UNTIL \n MASTER_LOG_FILE = \'log_name\', MASTER_LOG_POS = log_pos [FOR CHANNEL\n\"connection_name\"]\nSTART SLAVE [\"connection_name\"] [SQL_THREAD] UNTIL\n RELAY_LOG_FILE = \'log_name\', RELAY_LOG_POS = log_pos [FOR CHANNEL\n\"connection_name\"]\nSTART SLAVE [\"connection_name\"] [SQL_THREAD] UNTIL\n MASTER_GTID_POS = [FOR CHANNEL \"connection_name\"]\nSTART ALL SLAVES [thread_type [, thread_type]]\n\nSTART REPLICA [\"connection_name\"] [thread_type [, thread_type] ... ] -- from\n10.5.1\nSTART REPLICA [\"connection_name\"] [SQL_THREAD] UNTIL \n MASTER_LOG_FILE = \'log_name\', MASTER_LOG_POS = log_pos -- from 10.5.1\nSTART REPLICA [\"connection_name\"] [SQL_THREAD] UNTIL\n RELAY_LOG_FILE = \'log_name\', RELAY_LOG_POS = log_pos -- from 10.5.1\nSTART REPLICA [\"connection_name\"] [SQL_THREAD] UNTIL\n MASTER_GTID_POS = -- from 10.5.1\nSTART ALL REPLICAS [thread_type [, thread_type]] -- from 10.5.1\n\nthread_type: IO_THREAD | SQL_THREAD\n\nDescription\n-----------\n\nSTART SLAVE (START REPLICA from MariaDB 10.5.1) with no thread_type options\nstarts both of the replica threads (see replication). The I/O thread reads\nevents from the primary server and stores them in the relay log. The SQL\nthread reads events from the relay log and executes them. START SLAVE requires\nthe SUPER privilege, or, from MariaDB 10.5.2, the REPLICATION SLAVE ADMIN\nprivilege.\n\nIf START SLAVE succeeds in starting the replica threads, it returns without\nany error. However, even in that case, it might be that the replica threads\nstart and then later stop (for example, because they do not manage to connect\nto the primary or read its binary log, or some other problem). START SLAVE\ndoes not warn you about this. You must check the replica\'s error log for error\nmessages generated by the replica threads, or check that they are running\nsatisfactorily with SHOW SLAVE STATUS (SHOW REPLICA STATUS from MariaDB\n10.5.1).\n\nSTART SLAVE UNTIL\n-----------------\n\nSTART SLAVE UNTIL refers to the SQL_THREAD replica position at which the\nSQL_THREAD replication will halt. If SQL_THREAD isn\'t specified both threads\nare started.\n\nSTART SLAVE UNTIL master_gtid_pos=xxx is also supported. See Global\nTransaction ID/START SLAVE UNTIL master_gtid_pos=xxx for more details.\n\nconnection_name\n---------------\n\nIf there is only one nameless primary, or the default primary (as specified by\nthe default_master_connection system variable) is intended, connection_name\ncan be omitted. If provided, the START SLAVE statement will apply to the\nspecified primary. connection_name is case-insensitive.\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nas using the channel_name directly after START SLAVE.\n\nSTART ALL SLAVES\n----------------\n\nSTART ALL SLAVES starts all configured replicas (replicas with master_host not\nempty) that were not started before. It will give a note for all started\nconnections. You can check the notes with SHOW WARNINGS.\n\nSTART REPLICA\n-------------\n\nMariaDB starting with 10.5.1\n----------------------------\nSTART REPLICA is an alias for START SLAVE from MariaDB 10.5.1.\n\nURL: https://mariadb.com/kb/en/start-replica/','','https://mariadb.com/kb/en/start-replica/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (816,49,'STOP SLAVE','The terms master and slave have historically been used in replication, but the\nterms terms primary and replica are now preferred. The old terms are used\nstill used in parts of the documentation, and in MariaDB commands, although\nMariaDB 10.5 has begun the process of renaming. The documentation process is\nongoing. See MDEV-18777 to follow progress on this effort.\n\nSyntax\n------\n\nSTOP SLAVE [\"connection_name\"] [thread_type [, thread_type] ... ] [FOR CHANNEL\n\"connection_name\"]\n\nSTOP ALL SLAVES [thread_type [, thread_type]]\n\nSTOP REPLICA [\"connection_name\"] [thread_type [, thread_type] ... ] -- from\n10.5.1\n\nSTOP ALL REPLICAS [thread_type [, thread_type]] -- from 10.5.1\n\nthread_type: IO_THREAD | SQL_THREAD\n\nDescription\n-----------\n\nStops the replica threads. STOP SLAVE requires the SUPER privilege, or, from\nMariaDB 10.5.2, the REPLICATION SLAVE ADMIN privilege.\n\nLike START SLAVE, this statement may be used with the IO_THREAD and SQL_THREAD\noptions to name the thread or threads to be stopped. In almost all cases, one\nnever need to use the thread_type options.\n\nSTOP SLAVE waits until any current replication event group affecting one or\nmore non-transactional tables has finished executing (if there is any such\nreplication group), or until the user issues a KILL QUERY or KILL CONNECTION\nstatement.\n\nNote that STOP SLAVE doesn\'t delete the connection permanently. Next time you\nexecute START SLAVE or the MariaDB server restarts, the replica connection is\nrestored with it\'s original arguments. If you want to delete a connection, you\nshould execute RESET SLAVE.\n\nSTOP ALL SLAVES\n---------------\n\nSTOP ALL SLAVES stops all your running replicas. It will give you a note for\nevery stopped connection. You can check the notes with SHOW WARNINGS.\n\nconnection_name\n---------------\n\nThe connection_name option is used for multi-source replication.\n\nIf there is only one nameless master, or the default master (as specified by\nthe default_master_connection system variable) is intended, connection_name\ncan be omitted. If provided, the STOP SLAVE statement will apply to the\nspecified master. connection_name is case-insensitive.\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nas using the channel_name directly after STOP SLAVE.\n\nSTOP REPLICA\n------------\n\nMariaDB starting with 10.5.1\n----------------------------\nSTOP REPLICA is an alias for STOP SLAVE from MariaDB 10.5.1.\n\nURL: https://mariadb.com/kb/en/stop-replica/','','https://mariadb.com/kb/en/stop-replica/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (817,49,'RESET REPLICA/SLAVE','The terms master and slave have historically been used in replication, but the\nterms terms primary and replica are now preferred. The old terms are used\nstill used in parts of the documentation, and in MariaDB commands, although\nMariaDB 10.5 has begun the process of renaming. The documentation process is\nongoing. See MDEV-18777 to follow progress on this effort.\n\nSyntax\n------\n\nRESET REPLICA [\"connection_name\"] [ALL] [FOR CHANNEL \"connection_name\"] --\nfrom MariaDB 10.5.1 \nRESET SLAVE [\"connection_name\"] [ALL] [FOR CHANNEL \"connection_name\"]\n\nDescription\n-----------\n\nRESET REPLICA/SLAVE makes the replica forget its replication position in the\nmaster\'s binary log. This statement is meant to be used for a clean start. It\ndeletes the master.info and relay-log.info files, all the relay log files, and\nstarts a new relay log file. To use RESET REPLICA/SLAVE, the replica threads\nmust be stopped (use STOP REPLICA/SLAVE if necessary).\n\nNote: All relay log files are deleted, even if they have not been completely\nexecuted by the slave SQL thread. (This is a condition likely to exist on a\nreplication slave if you have issued a STOP REPLICA/SLAVE statement or if the\nslave is highly loaded.)\n\nNote: RESET REPLICA does not reset the global gtid_slave_pos variable. This\nmeans that a replica server configured with CHANGE MASTER TO\nMASTER_USE_GTID=slave_pos will not receive events with GTIDs occurring before\nthe state saved in gtid_slave_pos. If the intent is to reprocess these events,\ngtid_slave_pos must be manually reset, e.g. by executing set global\ngtid_slave_pos=\"\".\n\nConnection information stored in the master.info file is immediately reset\nusing any values specified in the corresponding startup options. This\ninformation includes values such as master host, master port, master user, and\nmaster password. If the replica SQL thread was in the middle of replicating\ntemporary tables when it was stopped, and RESET REPLICA/SLAVE is issued, these\nreplicated temporary tables are deleted on the slave.\n\nThe ALL also resets the PORT, HOST, USER and PASSWORD parameters for the\nslave. If you are using a connection name, it will permanently delete it and\nit will not show up anymore in SHOW ALL REPLICAS/SLAVE STATUS.\n\nconnection_name\n---------------\n\nThe connection_name option is used for multi-source replication.\n\nIf there is only one nameless primary, or the default primary (as specified by\nthe default_master_connection system variable) is intended, connection_name\ncan be omitted. If provided, the RESET REPLICA/SLAVE statement will apply to\nthe specified primary. connection_name is case-insensitive.\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nas using the channel_name directly after RESET REPLICA.\n\nRESET REPLICA\n-------------\n\nMariaDB starting with 10.5.1\n----------------------------\nRESET REPLICA is an alias for RESET SLAVE from MariaDB 10.5.1.\n\nURL: https://mariadb.com/kb/en/reset-replica/','','https://mariadb.com/kb/en/reset-replica/'); -- cgit v1.2.1 From 946b6f0f7d5237eeca811bd177380235c54d11c4 Mon Sep 17 00:00:00 2001 From: Ian Gilfillan Date: Mon, 23 Jan 2023 12:58:02 +0200 Subject: Update 10.7 HELP tables --- scripts/fill_help_tables.sql | 62 ++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/scripts/fill_help_tables.sql b/scripts/fill_help_tables.sql index a3bdc17a045..e3b817404f1 100644 --- a/scripts/fill_help_tables.sql +++ b/scripts/fill_help_tables.sql @@ -84,8 +84,8 @@ insert into help_category (help_category_id,name,parent_category_id,url) values insert into help_category (help_category_id,name,parent_category_id,url) values (49,'Replication',1,''); insert into help_category (help_category_id,name,parent_category_id,url) values (50,'Prepared Statements',1,''); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (1,9,'HELP_DATE','Help Contents generated from the MariaDB Knowledge Base on 22 October 2022.','',''); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (2,9,'HELP_VERSION','Help Contents generated for MariaDB 10.7 from the MariaDB Knowledge Base on 22 October 2022.','',''); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (1,9,'HELP_DATE','Help Contents generated from the MariaDB Knowledge Base on 23 January 2023.','',''); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (2,9,'HELP_VERSION','Help Contents generated for MariaDB 10.7 from the MariaDB Knowledge Base on 23 January 2023.','',''); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (3,2,'AREA','A synonym for ST_AREA.\n\nURL: https://mariadb.com/kb/en/polygon-properties-area/','','https://mariadb.com/kb/en/polygon-properties-area/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (4,2,'CENTROID','A synonym for ST_CENTROID.\n\nURL: https://mariadb.com/kb/en/centroid/','','https://mariadb.com/kb/en/centroid/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (5,2,'ExteriorRing','A synonym for ST_ExteriorRing.\n\nURL: https://mariadb.com/kb/en/polygon-properties-exteriorring/','','https://mariadb.com/kb/en/polygon-properties-exteriorring/'); @@ -151,7 +151,7 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (65,4,'POWER','Syntax\n------\n\nPOWER(X,Y)\n\nDescription\n-----------\n\nThis is a synonym for POW(), which returns the value of X raised to the power\nof Y.\n\nURL: https://mariadb.com/kb/en/power/','','https://mariadb.com/kb/en/power/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (66,4,'RADIANS','Syntax\n------\n\nRADIANS(X)\n\nDescription\n-----------\n\nReturns the argument X, converted from degrees to radians. Note that π radians\nequals 180 degrees.\n\nThis is the converse of the DEGREES() function.\n\nExamples\n--------\n\nSELECT RADIANS(45);\n+-------------------+\n| RADIANS(45) |\n+-------------------+\n| 0.785398163397448 |\n+-------------------+\n\nSELECT RADIANS(90);\n+-----------------+\n| RADIANS(90) |\n+-----------------+\n| 1.5707963267949 |\n+-----------------+\n\nSELECT RADIANS(PI());\n+--------------------+\n| RADIANS(PI()) |\n+--------------------+\n| 0.0548311355616075 |\n+--------------------+\n\nSELECT RADIANS(180);\n+------------------+\n| RADIANS(180) |\n+------------------+\n| 3.14159265358979 |\n+------------------+\n\nURL: https://mariadb.com/kb/en/radians/','','https://mariadb.com/kb/en/radians/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (67,4,'RAND','Syntax\n------\n\nRAND(), RAND(N)\n\nDescription\n-----------\n\nReturns a random DOUBLE precision floating point value v in the range 0 <= v <\n1.0. If a constant integer argument N is specified, it is used as the seed\nvalue, which produces a repeatable sequence of column values. In the example\nbelow, note that the sequences of values produced by RAND(3) is the same both\nplaces where it occurs.\n\nIn a WHERE clause, RAND() is evaluated each time the WHERE is executed.\n\nStatements using the RAND() function are not safe for statement-based\nreplication.\n\nPractical uses\n--------------\n\nThe expression to get a random integer from a given range is the following:\n\nFLOOR(min_value + RAND() * (max_value - min_value +1))\n\nRAND() is often used to read random rows from a table, as follows:\n\nSELECT * FROM my_table ORDER BY RAND() LIMIT 10;\n\nNote, however, that this technique should never be used on a large table as it\nwill be extremely slow. MariaDB will read all rows in the table, generate a\nrandom value for each of them, order them, and finally will apply the LIMIT\nclause.\n\nExamples\n--------\n\nCREATE TABLE t (i INT);\n\nINSERT INTO t VALUES(1),(2),(3);\n\nSELECT i, RAND() FROM t;\n+------+-------------------+\n| i | RAND() |\n+------+-------------------+\n| 1 | 0.255651095188829 |\n| 2 | 0.833920199269355 |\n| 3 | 0.40264774151393 |\n+------+-------------------+\n\nSELECT i, RAND(3) FROM t;\n+------+-------------------+\n| i | RAND(3) |\n+------+-------------------+\n| 1 | 0.90576975597606 |\n| 2 | 0.373079058130345 |\n| 3 | 0.148086053457191 |\n+------+-------------------+\n\nSELECT i, RAND() FROM t;\n+------+-------------------+\n| i | RAND() |\n+------+-------------------+\n| 1 | 0.511478140495232 |\n| 2 | 0.349447508668012 |\n| 3 | 0.212803152588013 |\n+------+-------------------+\n\nUsing the same seed, the same sequence will be returned:\n\nSELECT i, RAND(3) FROM t;\n+------+-------------------+\n| i | RAND(3) |\n+------+-------------------+\n| 1 | 0.90576975597606 |\n| 2 | 0.373079058130345 |\n| 3 | 0.148086053457191 |\n+------+-------------------+\n\nGenerating a random number from 5 to 15:\n\nSELECT FLOOR(5 + (RAND() * 11));\n\nURL: https://mariadb.com/kb/en/rand/','','https://mariadb.com/kb/en/rand/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (68,4,'ROUND','Syntax\n------\n\nROUND(X), ROUND(X,D)\n\nDescription\n-----------\n\nRounds the argument X to D decimal places. The rounding algorithm depends on\nthe data type of X. D defaults to 0 if not specified. D can be negative to\ncause D digits left of the decimal point of the value X to become zero.\n\nExamples\n--------\n\nSELECT ROUND(-1.23);\n+--------------+\n| ROUND(-1.23) |\n+--------------+\n| -1 |\n+--------------+\n\nSELECT ROUND(-1.58);\n+--------------+\n| ROUND(-1.58) |\n+--------------+\n| -2 |\n+--------------+\n\nSELECT ROUND(1.58); \n+-------------+\n| ROUND(1.58) |\n+-------------+\n| 2 |\n+-------------+\n\nSELECT ROUND(1.298, 1);\n+-----------------+\n| ROUND(1.298, 1) |\n+-----------------+\n| 1.3 |\n+-----------------+\n\nSELECT ROUND(1.298, 0);\n+-----------------+\n| ROUND(1.298, 0) |\n+-----------------+\n| 1 |\n+-----------------+\n\nSELECT ROUND(23.298, -1);\n+-------------------+\n| ROUND(23.298, -1) |\n+-------------------+\n| 20 |\n+-------------------+\n\nURL: https://mariadb.com/kb/en/round/','','https://mariadb.com/kb/en/round/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (68,4,'ROUND','Syntax\n------\n\nROUND(X), ROUND(X,D)\n\nDescription\n-----------\n\nRounds the argument X to D decimal places. D defaults to 0 if not specified. D\ncan be negative to cause D digits left of the decimal point of the value X to\nbecome zero.\n\nThe rounding algorithm depends on the data type of X:\n\n* for floating point types (FLOAT, DOUBLE) the C libraries rounding function\nis used, so the behavior *may* differ between operating systems\n* for fixed point types (DECIMAL, DEC/NUMBER/FIXED) the \"round half up\" rule\nis used, meaning that e.g. a value ending in exactly .5 is always rounded up.\n\nExamples\n--------\n\nSELECT ROUND(-1.23);\n+--------------+\n| ROUND(-1.23) |\n+--------------+\n| -1 |\n+--------------+\n\nSELECT ROUND(-1.58);\n+--------------+\n| ROUND(-1.58) |\n+--------------+\n| -2 |\n+--------------+\n\nSELECT ROUND(1.58); \n+-------------+\n| ROUND(1.58) |\n+-------------+\n| 2 |\n+-------------+\n\nSELECT ROUND(1.298, 1);\n+-----------------+\n| ROUND(1.298, 1) |\n+-----------------+\n| 1.3 |\n+-----------------+\n\nSELECT ROUND(1.298, 0);\n+-----------------+\n| ROUND(1.298, 0) |\n+-----------------+\n| 1 |\n+-----------------+\n\nSELECT ROUND(23.298, -1);\n+-------------------+\n| ROUND(23.298, -1) |\n+-------------------+\n| 20 |\n+-------------------+\n\nURL: https://mariadb.com/kb/en/round/','','https://mariadb.com/kb/en/round/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (69,4,'SIGN','Syntax\n------\n\nSIGN(X)\n\nDescription\n-----------\n\nReturns the sign of the argument as -1, 0, or 1, depending on whether X is\nnegative, zero, or positive.\n\nExamples\n--------\n\nSELECT SIGN(-32);\n+-----------+\n| SIGN(-32) |\n+-----------+\n| -1 |\n+-----------+\n\nSELECT SIGN(0);\n+---------+\n| SIGN(0) |\n+---------+\n| 0 |\n+---------+\n\nSELECT SIGN(234);\n+-----------+\n| SIGN(234) |\n+-----------+\n| 1 |\n+-----------+\n\nURL: https://mariadb.com/kb/en/sign/','','https://mariadb.com/kb/en/sign/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (70,4,'SIN','Syntax\n------\n\nSIN(X)\n\nDescription\n-----------\n\nReturns the sine of X, where X is given in radians.\n\nExamples\n--------\n\nSELECT SIN(1.5707963267948966);\n+-------------------------+\n| SIN(1.5707963267948966) |\n+-------------------------+\n| 1 |\n+-------------------------+\n\nSELECT SIN(PI());\n+----------------------+\n| SIN(PI()) |\n+----------------------+\n| 1.22460635382238e-16 |\n+----------------------+\n\nSELECT ROUND(SIN(PI()));\n+------------------+\n| ROUND(SIN(PI())) |\n+------------------+\n| 0 |\n+------------------+\n\nURL: https://mariadb.com/kb/en/sin/','','https://mariadb.com/kb/en/sin/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (71,4,'SQRT','Syntax\n------\n\nSQRT(X)\n\nDescription\n-----------\n\nReturns the square root of X. If X is negative, NULL is returned.\n\nExamples\n--------\n\nSELECT SQRT(4);\n+---------+\n| SQRT(4) |\n+---------+\n| 2 |\n+---------+\n\nSELECT SQRT(20);\n+------------------+\n| SQRT(20) |\n+------------------+\n| 4.47213595499958 |\n+------------------+\n\nSELECT SQRT(-16);\n+-----------+\n| SQRT(-16) |\n+-----------+\n| NULL |\n+-----------+\n\nSELECT SQRT(1764);\n+------------+\n| SQRT(1764) |\n+------------+\n| 42 |\n+------------+\n\nURL: https://mariadb.com/kb/en/sqrt/','','https://mariadb.com/kb/en/sqrt/'); @@ -192,11 +192,11 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, update help_topic set description = CONCAT(description, '\n| | executed by the user. See also |\n| | Aborting Statements that Exceed a |\n| | Certain Time to Execute. |\n+--------------------------------------+--------------------------------------+\n\nIf any of these limits are set to 0, then there is no limit for that resource\nfor that user.\n\nHere is an example showing how to create a user with resource limits:\n\nCREATE USER \'someone\'@\'localhost\' WITH\n MAX_USER_CONNECTIONS 10\n MAX_QUERIES_PER_HOUR 200;\n\nThe resources are tracked per account, which means \'user\'@\'server\'; not per\nuser name or per connection.\n\nThe count can be reset for all users using FLUSH USER_RESOURCES, FLUSH\nPRIVILEGES or mysqladmin reload.\n\nPer account resource limits are stored in the user table, in the mysql\ndatabase. Columns used for resources limits are named max_questions,\nmax_updates, max_connections (for MAX_CONNECTIONS_PER_HOUR), and\nmax_user_connections (for MAX_USER_CONNECTIONS).\n\nAccount Names\n-------------\n\nAccount names have both a user name component and a host name component, and\nare specified as \'user_name\'@\'host_name\'.\n\nThe user name and host name may be unquoted, quoted as strings using double\nquotes (\") or single quotes (\'), or quoted as identifiers using backticks (`).\nYou must use quotes when using special characters (such as a hyphen) or\nwildcard characters. If you quote, you must quote the user name and host name\nseparately (for example \'user_name\'@\'host_name\').\n\nHost Name Component\n-------------------\n\nIf the host name is not provided, it is assumed to be \'%\'.\n\nHost names may contain the wildcard characters % and _. They are matched as if\nby the LIKE clause. If you need to use a wildcard character literally (for\nexample, to match a domain name with an underscore), prefix the character with\na backslash. See LIKE for more information on escaping wildcard characters.\n\nHost name matches are case-insensitive. Host names can match either domain\nnames or IP addresses. Use \'localhost\' as the host name to allow only local\nclient connections.\n\nYou can use a netmask to match a range of IP addresses using \'base_ip/netmask\'\nas the host name. A user with an IP address ip_addr will be allowed to connect\nif the following condition is true:\n\nip_addr & netmask = base_ip\n\nFor example, given a user:\n\nCREATE USER \'maria\'@\'247.150.130.0/255.255.255.0\';\n\nthe IP addresses satisfying this condition range from 247.150.130.0 to\n247.150.130.255.\n\nUsing 255.255.255.255 is equivalent to not using a netmask at all. Netmasks\ncannot be used for IPv6 addresses.\n\nNote that the credentials added when creating a user with the \'%\' wildcard\nhost will not grant access in all cases. For example, some systems come with\nan anonymous localhost user, and when connecting from localhost this will take\nprecedence.\n\nBefore MariaDB 10.6, the host name component could be up to 60 characters in\nlength. Starting from MariaDB 10.6, it can be up to 255 characters.\n\nUser Name Component\n-------------------\n\nUser names must match exactly, including case. A user name that is empty is\nknown as an anonymous account and is allowed to match a login attempt with any\nuser name component. These are described more in the next section.\n\nFor valid identifiers to use as user names, see Identifier Names.\n\nIt is possible for more than one account to match when a user connects.\nMariaDB selects the first matching account after sorting according to the\nfollowing criteria:\n\n* Accounts with an exact host name are sorted before accounts using a wildcard\nin the\nhost name. Host names using a netmask are considered to be exact for sorting.\n* Accounts with a wildcard in the host name are sorted according to the\nposition of\nthe first wildcard character. Those with a wildcard character later in the\nhost name\nsort before those with a wildcard character earlier in the host name.\n* Accounts with a non-empty user name sort before accounts with an empty user\nname.\n* Accounts with an empty user name are sorted last. As mentioned previously,\nthese are known as anonymous accounts. These are described more in the next\nsection.\n\nThe following table shows a list of example account as sorted by these\ncriteria:\n\n+---------+-------------+\n| User | Host |\n+---------+-------------+\n| joffrey | 192.168.0.3 |\n| | 192.168.0.% |\n| joffrey | 192.168.% |\n| | 192.168.% |\n+---------+-------------+\n\nOnce connected, you only have the privileges granted to the account that\nmatched, not all accounts that could have matched. For example, consider the\nfollowing commands:\n\nCREATE USER \'joffrey\'@\'192.168.0.3\';\nCREATE USER \'joffrey\'@\'%\';\nGRANT SELECT ON test.t1 to \'joffrey\'@\'192.168.0.3\';\nGRANT SELECT ON test.t2 to \'joffrey\'@\'%\';\n\nIf you connect as joffrey from 192.168.0.3, you will have the SELECT privilege\non the table test.t1, but not on the table test.t2. If you connect as joffrey\nfrom any other IP address, you will have the SELECT privilege on the table\ntest.t2, but not on the table test.t1.\n\nUsernames can be up to 80 characters long before 10.6 and starting from 10.6\nit can be 128 characters long.\n\nAnonymous Accounts\n------------------\n\nAnonymous accounts are accounts where the user name portion of the account\nname is empty. These accounts act as special catch-all accounts. If a user\nattempts to log into the system from a host, and an anonymous account exists\nwith a host name portion that matches the user\'s host, then the user will log\nin as the anonymous account if there is no more specific account match for the\nuser name that the user entered.\n\nFor example, here are some anonymous accounts:\n\nCREATE USER \'\'@\'localhost\';\nCREATE USER \'\'@\'192.168.0.3\';\n\nFixing a Legacy Default Anonymous Account\n-----------------------------------------\n\nOn some systems, the mysql.db table has some entries for the \'\'@\'%\' anonymous\naccount by default. Unfortunately, there is no matching entry in the\nmysql.user/mysql.global_priv_table table, which means that this anonymous\naccount doesn\'t exactly exist, but it does have privileges--usually on the\ndefault test database created by mysql_install_db. These account-less\nprivileges are a legacy that is leftover from a time when MySQL\'s privilege\nsystem was less advanced.\n\nThis situation means that you will run into errors if you try to create a\n\'\'@\'%\' account. For example:\n\nCREATE USER \'\'@\'%\';\nERROR 1396 (HY000): Operation CREATE USER failed for \'\'@\'%\'\n\nThe fix is to DELETE the row in the mysql.db table and then execute FLUSH\nPRIVILEGES:\n\nDELETE FROM mysql.db WHERE User=\'\' AND Host=\'%\';\nFLUSH PRIVILEGES;\n\nAnd then the account can be created:\n\nCREATE USER \'\'@\'%\';\nQuery OK, 0 rows affected (0.01 sec)\n\nSee MDEV-13486 for more information.\n\nPassword Expiry\n---------------\n\nMariaDB starting with 10.4.3\n----------------------------\nBesides automatic password expiry, as determined by default_password_lifetime,\npassword expiry times can be set on an individual user basis, overriding the\nglobal setting, for example:\n\nCREATE USER \'monty\'@\'localhost\' PASSWORD EXPIRE INTERVAL 120 DAY;\n\nSee User Password Expiry for more details.\n\nAccount Locking\n---------------\n\nMariaDB starting with 10.4.2\n----------------------------\nAccount locking permits privileged administrators to lock/unlock user\naccounts. No new client connections will be permitted if an account is locked\n(existing connections are not affected). For example:\n\nCREATE USER \'marijn\'@\'localhost\' ACCOUNT LOCK;\n\nSee Account Locking for more details.\n\nFrom MariaDB 10.4.7 and MariaDB 10.5.8, the lock_option and password_option\nclauses can occur in either order.\n\nURL: https://mariadb.com/kb/en/create-user/') WHERE help_topic_id = 104; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (105,10,'ALTER USER','Syntax\n------\n\nALTER USER [IF EXISTS] \n user_specification [,user_specification] ...\n [REQUIRE {NONE | tls_option [[AND] tls_option] ...}]\n [WITH resource_option [resource_option] ...]\n [lock_option] [password_option]\n\nuser_specification:\n username [authentication_option]\n\nauthentication_option:\n IDENTIFIED BY \'password\'\n | IDENTIFIED BY PASSWORD \'password_hash\'\n | IDENTIFIED {VIA|WITH} authentication_rule [OR authentication_rule] ...\n\nauthentication_rule:\n authentication_plugin\n | authentication_plugin {USING|AS} \'authentication_string\'\n | authentication_plugin {USING|AS} PASSWORD(\'password\')\n\ntls_option\n SSL\n | X509\n | CIPHER \'cipher\'\n | ISSUER \'issuer\'\n | SUBJECT \'subject\'\n\nresource_option\n MAX_QUERIES_PER_HOUR count\n | MAX_UPDATES_PER_HOUR count\n | MAX_CONNECTIONS_PER_HOUR count\n | MAX_USER_CONNECTIONS count\n | MAX_STATEMENT_TIME time\n\npassword_option:\n PASSWORD EXPIRE\n | PASSWORD EXPIRE DEFAULT\n | PASSWORD EXPIRE NEVER\n | PASSWORD EXPIRE INTERVAL N DAY\n\nlock_option:\n ACCOUNT LOCK\n | ACCOUNT UNLOCK\n}\n\nDescription\n-----------\n\nThe ALTER USER statement modifies existing MariaDB accounts. To use it, you\nmust have the global CREATE USER privilege or the UPDATE privilege for the\nmysql database. The global SUPER privilege is also required if the read_only\nsystem variable is enabled.\n\nIf any of the specified user accounts do not yet exist, an error results. If\nan error occurs, ALTER USER will still modify the accounts that do not result\nin an error. Only one error is produced for all users which have not been\nmodified.\n\nIF EXISTS\n---------\n\nWhen the IF EXISTS clause is used, MariaDB will return a warning instead of an\nerror for each specified user that does not exist.\n\nAccount Names\n-------------\n\nFor ALTER USER statements, account names are specified as the username\nargument in the same way as they are for CREATE USER statements. See account\nnames from the CREATE USER page for details on how account names are specified.\n\nCURRENT_USER or CURRENT_USER() can also be used to alter the account logged\ninto the current session. For example, to change the current user\'s password\nto mariadb:\n\nALTER USER CURRENT_USER() IDENTIFIED BY \'mariadb\';\n\nAuthentication Options\n----------------------\n\nMariaDB starting with 10.4\n--------------------------\nFrom MariaDB 10.4, it is possible to use more than one authentication plugin\nfor each user account. For example, this can be useful to slowly migrate users\nto the more secure ed25519 authentication plugin over time, while allowing the\nold mysql_native_password authentication plugin as an alternative for the\ntransitional period. See Authentication from MariaDB 10.4 for more.\n\nWhen running ALTER USER, not specifying an authentication option in the\nIDENTIFIED VIA clause will remove that authentication method. (However this\nwas not the case before MariaDB 10.4.13, see MDEV-21928)\n\nFor example, a user is created with the ability to authenticate via both a\npassword and unix_socket:\n\nCREATE USER \'bob\'@\'localhost\' \n IDENTIFIED VIA mysql_native_password USING PASSWORD(\'pwd\')\n OR unix_socket;\n\nSHOW CREATE USER \'bob\'@\'localhost\'\\G\n*************************** 1. row ***************************\nCREATE USER for bob@localhost: CREATE USER `bob`@`localhost` \n IDENTIFIED VIA mysql_native_password\n USING \'*975B2CD4FF9AE554FE8AD33168FBFC326D2021DD\'\n OR unix_socket\n\nIf the user\'s password is updated, but unix_socket authentication is not\nspecified in the IDENTIFIED VIA clause, unix_socket authentication will no\nlonger be permitted.\n\nALTER USER \'bob\'@\'localhost\' IDENTIFIED VIA mysql_native_password \n USING PASSWORD(\'pwd2\');\n\nSHOW CREATE USER \'bob\'@\'localhost\'\\G\n*************************** 1. row ***************************\nCREATE USER for bob@localhost: CREATE USER `bob`@`localhost` \n IDENTIFIED BY PASSWORD \'*38366FDA01695B6A5A9DD4E428D9FB8F7EB75512\'\n\nIDENTIFIED BY \'password\'\n------------------------\n\nThe optional IDENTIFIED BY clause can be used to provide an account with a\npassword. The password should be specified in plain text. It will be hashed by\nthe PASSWORD function prior to being stored to the mysql.user table.\n\nFor example, if our password is mariadb, then we can set the account\'s\npassword with:\n\nALTER USER foo2@test IDENTIFIED BY \'mariadb\';\n\nIf you do not specify a password with the IDENTIFIED BY clause, the user will\nbe able to connect without a password. A blank password is not a wildcard to\nmatch any password. The user must connect without providing a password if no\npassword is set.\n\nThe only authentication plugins that this clause supports are\nmysql_native_password and mysql_old_password.\n\nIDENTIFIED BY PASSWORD \'password_hash\'\n--------------------------------------\n\nThe optional IDENTIFIED BY PASSWORD clause can be used to provide an account\nwith a password that has already been hashed. The password should be specified\nas a hash that was provided by the PASSWORD#function. It will be stored to the\nmysql.user table as-is.\n\nFor example, if our password is mariadb, then we can find the hash with:\n\nSELECT PASSWORD(\'mariadb\');\n+-------------------------------------------+\n| PASSWORD(\'mariadb\') |\n+-------------------------------------------+\n| *54958E764CE10E50764C2EECBB71D01F08549980 |\n+-------------------------------------------+\n\nAnd then we can set an account\'s password with the hash:\n\nALTER USER foo2@test \n IDENTIFIED BY PASSWORD \'*54958E764CE10E50764C2EECBB71D01F08549980\';\n\nIf you do not specify a password with the IDENTIFIED BY clause, the user will\nbe able to connect without a password. A blank password is not a wildcard to\nmatch any password. The user must connect without providing a password if no\npassword is set.\n\nThe only authentication plugins that this clause supports are\nmysql_native_password and mysql_old_password.\n\nIDENTIFIED {VIA|WITH} authentication_plugin\n-------------------------------------------\n\nThe optional IDENTIFIED VIA authentication_plugin allows you to specify that\nthe account should be authenticated by a specific authentication plugin. The\nplugin name must be an active authentication plugin as per SHOW PLUGINS. If it\ndoesn\'t show up in that output, then you will need to install it with INSTALL\nPLUGIN or INSTALL SONAME.\n\nFor example, this could be used with the PAM authentication plugin:\n\nALTER USER foo2@test IDENTIFIED VIA pam;\n\nSome authentication plugins allow additional arguments to be specified after a\nUSING or AS keyword. For example, the PAM authentication plugin accepts a\nservice name:\n\nALTER USER foo2@test IDENTIFIED VIA pam USING \'mariadb\';\n\nThe exact meaning of the additional argument would depend on the specific\nauthentication plugin.\n\nIn MariaDB 10.4 and later, the USING or AS keyword can also be used to provide\na plain-text password to a plugin if it\'s provided as an argument to the\nPASSWORD() function. This is only valid for authentication plugins that have\nimplemented a hook for the PASSWORD() function. For example, the ed25519\nauthentication plugin supports this:\n\nALTER USER safe@\'%\' IDENTIFIED VIA ed25519 USING PASSWORD(\'secret\');\n\nTLS Options\n-----------\n\nBy default, MariaDB transmits data between the server and clients without\nencrypting it. This is generally acceptable when the server and client run on\nthe same host or in networks where security is guaranteed through other means.\nHowever, in cases where the server and client exist on separate networks or\nthey are in a high-risk network, the lack of encryption does introduce\nsecurity concerns as a malicious actor could potentially eavesdrop on the\ntraffic as it is sent over the network between them.\n\nTo mitigate this concern, MariaDB allows you to encrypt data in transit\nbetween the server and clients using the Transport Layer Security (TLS)\nprotocol. TLS was formerly known as Secure Socket Layer (SSL), but strictly\nspeaking the SSL protocol is a predecessor to TLS and, that version of the\nprotocol is now considered insecure. The documentation still uses the term SSL\noften and for compatibility reasons TLS-related server system and status\nvariables still use the prefix ssl_, but internally, MariaDB only supports its\nsecure successors.\n\nSee Secure Connections Overview for more information about how to determine\nwhether your MariaDB server has TLS support.\n\nYou can set certain TLS-related restrictions for specific user accounts. For\ninstance, you might use this with user accounts that require access to\nsensitive data while sending it across networks that you do not control. These\nrestrictions can be enabled for a user account with the CREATE USER, ALTER\nUSER, or GRANT statements. The following options are available:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| REQUIRE NONE | TLS is not required for this account, but can |\n| | still be used. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SSL | The account must use TLS, but no valid X509 |\n| | certificate is required. This option cannot |\n| | be combined with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE X509 | The account must use TLS and must have a |\n| | valid X509 certificate. This option implies |\n| | REQUIRE SSL. This option cannot be combined |\n| | with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE ISSUER \'issuer\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the Certificate |\n| | Authority must be the one specified via the |\n| | string issuer. This option implies REQUIRE |\n| | X509. This option can be combined with the |\n| | SUBJECT, and CIPHER options in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SUBJECT \'subject\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the |\n| | certificate\'s Subject must be the one |\n| | specified via the string subject. This option |\n| | implies REQUIRE X509. This option can be |\n| | combined with the ISSUER, and CIPHER options |\n| | in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE CIPHER \'cipher\' | The account must use TLS, but no valid X509 |\n| | certificate is required. Also, the encryption |\n| | used for the connection must use a specific |\n| | cipher method specified in the string cipher. |\n| | This option implies REQUIRE SSL. This option |\n| | can be combined with the ISSUER, and SUBJECT |\n| | options in any order. |\n+---------------------------+------------------------------------------------+\n\nThe REQUIRE keyword must be used only once for all specified options, and the\nAND keyword can be used to separate individual options, but it is not required.\n\nFor example, you can alter a user account to require these TLS options with\nthe following:\n\nALTER USER \'alice\'@\'%\'\n REQUIRE SUBJECT \'/CN=alice/O=My Dom, Inc./C=US/ST=Oregon/L=Portland\' AND\n ISSUER \'/C=FI/ST=Somewhere/L=City/ O=Some Company/CN=Peter\nParker/emailAddress=p.parker@marvel.com\'\n AND CIPHER \'SHA-DES-CBC3-EDH-RSA\';\n\nIf any of these options are set for a specific user account, then any client\nwho tries to connect with that user account will have to be configured to\nconnect with TLS.\n\nSee Securing Connections for Client and Server for information on how to\nenable TLS on the client and server.\n\nResource Limit Options\n----------------------\n\nIt is possible to set per-account limits for certain server resources. The\nfollowing table shows the values that can be set per account:\n\n+------------------------------------+---------------------------------------+\n| Limit Type | Description |\n+------------------------------------+---------------------------------------+\n| MAX_QUERIES_PER_HOUR | Number of statements that the |\n| | account can issue per hour |\n| | (including updates) |\n+------------------------------------+---------------------------------------+\n| MAX_UPDATES_PER_HOUR | Number of updates (not queries) that |\n| | the account can issue per hour |\n+------------------------------------+---------------------------------------+\n| MAX_CONNECTIONS_PER_HOUR | Number of connections that the |\n| | account can start per hour |\n+------------------------------------+---------------------------------------+\n| MAX_USER_CONNECTIONS | Number of simultaneous connections |\n| | that can be accepted from the same |\n| | account; if it is 0, max_connections |\n| | will be used instead; if |\n| | max_connections is 0, there is no |\n| | limit for this account\'s |\n| | simultaneous connections. |','','https://mariadb.com/kb/en/alter-user/'); update help_topic set description = CONCAT(description, '\n+------------------------------------+---------------------------------------+\n| MAX_STATEMENT_TIME | Timeout, in seconds, for statements |\n| | executed by the user. See also |\n| | Aborting Statements that Exceed a |\n| | Certain Time to Execute. |\n+------------------------------------+---------------------------------------+\n\nIf any of these limits are set to 0, then there is no limit for that resource\nfor that user.\n\nHere is an example showing how to set an account\'s resource limits:\n\nALTER USER \'someone\'@\'localhost\' WITH\n MAX_USER_CONNECTIONS 10\n MAX_QUERIES_PER_HOUR 200;\n\nThe resources are tracked per account, which means \'user\'@\'server\'; not per\nuser name or per connection.\n\nThe count can be reset for all users using FLUSH USER_RESOURCES, FLUSH\nPRIVILEGES or mysqladmin reload.\n\nPer account resource limits are stored in the user table, in the mysql\ndatabase. Columns used for resources limits are named max_questions,\nmax_updates, max_connections (for MAX_CONNECTIONS_PER_HOUR), and\nmax_user_connections (for MAX_USER_CONNECTIONS).\n\nPassword Expiry\n---------------\n\nMariaDB starting with 10.4.3\n----------------------------\nBesides automatic password expiry, as determined by default_password_lifetime,\npassword expiry times can be set on an individual user basis, overriding the\nglobal setting, for example:\n\nALTER USER \'monty\'@\'localhost\' PASSWORD EXPIRE INTERVAL 120 DAY;\nALTER USER \'monty\'@\'localhost\' PASSWORD EXPIRE NEVER;\nALTER USER \'monty\'@\'localhost\' PASSWORD EXPIRE DEFAULT;\n\nSee User Password Expiry for more details.\n\nAccount Locking\n---------------\n\nMariaDB starting with 10.4.2\n----------------------------\nAccount locking permits privileged administrators to lock/unlock user\naccounts. No new client connections will be permitted if an account is locked\n(existing connections are not affected). For example:\n\nALTER USER \'marijn\'@\'localhost\' ACCOUNT LOCK;\n\nSee Account Locking for more details.\n\nFrom MariaDB 10.4.7 and MariaDB 10.5.8, the lock_option and password_option\nclauses can occur in either order.\n\nURL: https://mariadb.com/kb/en/alter-user/') WHERE help_topic_id = 105; -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (106,10,'DROP USER','Syntax\n------\n\nDROP USER [IF EXISTS] user_name [, user_name] ...\n\nDescription\n-----------\n\nThe DROP USER statement removes one or more MariaDB accounts. It removes\nprivilege rows for the account from all grant tables. To use this statement,\nyou must have the global CREATE USER privilege or the DELETE privilege for the\nmysql database. Each account is named using the same format as for the CREATE\nUSER statement; for example, \'jeffrey\'@\'localhost\'. If you specify only the\nuser name part of the account name, a host name part of \'%\' is used. For\nadditional information about specifying account names, see CREATE USER.\n\nNote that, if you specify an account that is currently connected, it will not\nbe deleted until the connection is closed. The connection will not be\nautomatically closed.\n\nIf any of the specified user accounts do not exist, ERROR 1396 (HY000)\nresults. If an error occurs, DROP USER will still drop the accounts that do\nnot result in an error. Only one error is produced for all users which have\nnot been dropped:\n\nERROR 1396 (HY000): Operation DROP USER failed for \'u1\'@\'%\',\'u2\'@\'%\'\n\nFailed CREATE or DROP operations, for both users and roles, produce the same\nerror code.\n\nIF EXISTS\n---------\n\nIf the IF EXISTS clause is used, MariaDB will return a note instead of an\nerror if the user does not exist.\n\nExamples\n--------\n\nDROP USER bob;\n\nIF EXISTS:\n\nDROP USER bob;\nERROR 1396 (HY000): Operation DROP USER failed for \'bob\'@\'%\'\n\nDROP USER IF EXISTS bob;\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+---------------------------------------------+\n| Level | Code | Message |\n+-------+------+---------------------------------------------+\n| Note | 1974 | Can\'t drop user \'bob\'@\'%\'; it doesn\'t exist |\n+-------+------+---------------------------------------------+\n\nURL: https://mariadb.com/kb/en/drop-user/','','https://mariadb.com/kb/en/drop-user/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (106,10,'DROP USER','Syntax\n------\n\nDROP USER [IF EXISTS] user_name [, user_name] ...\n\nDescription\n-----------\n\nThe DROP USER statement removes one or more MariaDB accounts. It removes\nprivilege rows for the account from all grant tables. To use this statement,\nyou must have the global CREATE USER privilege or the DELETE privilege for the\nmysql database. Each account is named using the same format as for the CREATE\nUSER statement; for example, \'jeffrey\'@\'localhost\'. If you specify only the\nuser name part of the account name, a host name part of \'%\' is used. For\nadditional information about specifying account names, see CREATE USER.\n\nNote that, if you specify an account that is currently connected, it will not\nbe deleted until the connection is closed. The connection will not be\nautomatically closed.\n\nIf any of the specified user accounts do not exist, ERROR 1396 (HY000)\nresults. If an error occurs, DROP USER will still drop the accounts that do\nnot result in an error. Only one error is produced for all users which have\nnot been dropped:\n\nERROR 1396 (HY000): Operation DROP USER failed for \'u1\'@\'%\',\'u2\'@\'%\'\n\nFailed CREATE or DROP operations, for both users and roles, produce the same\nerror code.\n\nIF EXISTS\n---------\n\nIf the IF EXISTS clause is used, MariaDB will return a note instead of an\nerror if the user does not exist.\n\nExamples\n--------\n\nDROP USER bob;\n\nDROP USER foo2@localhost,foo2@\'127.%\';\n\nIF EXISTS:\n\nDROP USER bob;\nERROR 1396 (HY000): Operation DROP USER failed for \'bob\'@\'%\'\n\nDROP USER IF EXISTS bob;\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+---------------------------------------------+\n| Level | Code | Message |\n+-------+------+---------------------------------------------+\n| Note | 1974 | Can\'t drop user \'bob\'@\'%\'; it doesn\'t exist |\n+-------+------+---------------------------------------------+\n\nURL: https://mariadb.com/kb/en/drop-user/','','https://mariadb.com/kb/en/drop-user/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (107,10,'GRANT','Syntax\n------\n\nGRANT\n priv_type [(column_list)]\n [, priv_type [(column_list)]] ...\n ON [object_type] priv_level\n TO user_specification [ user_options ...]\n\nuser_specification:\n username [authentication_option]\n | PUBLIC\nauthentication_option:\n IDENTIFIED BY \'password\'\n | IDENTIFIED BY PASSWORD \'password_hash\'\n | IDENTIFIED {VIA|WITH} authentication_rule [OR authentication_rule ...]\n\nauthentication_rule:\n authentication_plugin\n | authentication_plugin {USING|AS} \'authentication_string\'\n | authentication_plugin {USING|AS} PASSWORD(\'password\')\n\nGRANT PROXY ON username\n TO user_specification [, user_specification ...]\n [WITH GRANT OPTION]\n\nGRANT rolename TO grantee [, grantee ...]\n [WITH ADMIN OPTION]\n\ngrantee:\n rolename\n username [authentication_option]\n\nuser_options:\n [REQUIRE {NONE | tls_option [[AND] tls_option] ...}]\n [WITH with_option [with_option] ...]\n\nobject_type:\n TABLE\n | FUNCTION\n | PROCEDURE\n | PACKAGE\n\npriv_level:\n *\n | *.*\n | db_name.*\n | db_name.tbl_name\n | tbl_name\n | db_name.routine_name\n\nwith_option:\n GRANT OPTION\n | resource_option\n\nresource_option:\n MAX_QUERIES_PER_HOUR count\n | MAX_UPDATES_PER_HOUR count\n | MAX_CONNECTIONS_PER_HOUR count\n | MAX_USER_CONNECTIONS count\n | MAX_STATEMENT_TIME time\n\ntls_option:\n SSL\n | X509\n | CIPHER \'cipher\'\n | ISSUER \'issuer\'\n | SUBJECT \'subject\'\n\nDescription\n-----------\n\nThe GRANT statement allows you to grant privileges or roles to accounts. To\nuse GRANT, you must have the GRANT OPTION privilege, and you must have the\nprivileges that you are granting.\n\nUse the REVOKE statement to revoke privileges granted with the GRANT statement.\n\nUse the SHOW GRANTS statement to determine what privileges an account has.\n\nAccount Names\n-------------\n\nFor GRANT statements, account names are specified as the username argument in\nthe same way as they are for CREATE USER statements. See account names from\nthe CREATE USER page for details on how account names are specified.\n\nImplicit Account Creation\n-------------------------\n\nThe GRANT statement also allows you to implicitly create accounts in some\ncases.\n\nIf the account does not yet exist, then GRANT can implicitly create it. To\nimplicitly create an account with GRANT, a user is required to have the same\nprivileges that would be required to explicitly create the account with the\nCREATE USER statement.\n\nIf the NO_AUTO_CREATE_USER SQL_MODE is set, then accounts can only be created\nif authentication information is specified, or with a CREATE USER statement.\nIf no authentication information is provided, GRANT will produce an error when\nthe specified account does not exist, for example:\n\nshow variables like \'%sql_mode%\' ;\n+---------------+--------------------------------------------+\n| Variable_name | Value |\n+---------------+--------------------------------------------+\n| sql_mode | NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |\n+---------------+--------------------------------------------+\n\nGRANT USAGE ON *.* TO \'user123\'@\'%\' IDENTIFIED BY \'\';\nERROR 1133 (28000): Can\'t find any matching row in the user table\n\nGRANT USAGE ON *.* TO \'user123\'@\'%\' \n IDENTIFIED VIA PAM using \'mariadb\' require ssl ;\nQuery OK, 0 rows affected (0.00 sec)\n\nselect host, user from mysql.user where user=\'user123\' ;\n\n+------+----------+\n| host | user |\n+------+----------+\n| % | user123 |\n+------+----------+\n\nPrivilege Levels\n----------------\n\nPrivileges can be set globally, for an entire database, for a table or\nroutine, or for individual columns in a table. Certain privileges can only be\nset at certain levels.\n\n* Global privileges priv_type are granted using *.* for\npriv_level. Global privileges include privileges to administer the database\nand manage user accounts, as well as privileges for all tables, functions, and\nprocedures. Global privileges are stored in the mysql.user table prior to\nMariaDB 10.4, and in mysql.global_priv table afterwards.\n* Database privileges priv_type are granted using db_name.*\nfor priv_level, or using just * to use default database. Database\nprivileges include privileges to create tables and functions, as well as\nprivileges for all tables, functions, and procedures in the database. Database\nprivileges are stored in the mysql.db table.\n* Table privileges priv_type are granted using db_name.tbl_name\nfor priv_level, or using just tbl_name to specify a table in the default\ndatabase. The TABLE keyword is optional. Table privileges include the\nability to select and change data in the table. Certain table privileges can\nbe granted for individual columns.\n* Column privileges priv_type are granted by specifying a table for\npriv_level and providing a column list after the privilege type. They allow\nyou to control exactly which columns in a table users can select and change.\n* Function privileges priv_type are granted using FUNCTION db_name.routine_name\nfor priv_level, or using just FUNCTION routine_name to specify a function\nin the default database.\n* Procedure privileges priv_type are granted using PROCEDURE\ndb_name.routine_name\nfor priv_level, or using just PROCEDURE routine_name to specify a procedure\nin the default database.\n\nThe USAGE Privilege\n-------------------\n\nThe USAGE privilege grants no real privileges. The SHOW GRANTS statement will\nshow a global USAGE privilege for a newly-created user. You can use USAGE with\nthe GRANT statement to change options like GRANT OPTION and\nMAX_USER_CONNECTIONS without changing any account privileges.\n\nThe ALL PRIVILEGES Privilege\n----------------------------\n\nThe ALL PRIVILEGES privilege grants all available privileges. Granting all\nprivileges only affects the given privilege level. For example, granting all\nprivileges on a table does not grant any privileges on the database or\nglobally.\n\nUsing ALL PRIVILEGES does not grant the special GRANT OPTION privilege.\n\nYou can use ALL instead of ALL PRIVILEGES.\n\nThe GRANT OPTION Privilege\n--------------------------\n\nUse the WITH GRANT OPTION clause to give users the ability to grant privileges\nto other users at the given privilege level. Users with the GRANT OPTION\nprivilege can only grant privileges they have. They cannot grant privileges at\na higher privilege level than they have the GRANT OPTION privilege.\n\nThe GRANT OPTION privilege cannot be set for individual columns. If you use\nWITH GRANT OPTION when specifying column privileges, the GRANT OPTION\nprivilege will be granted for the entire table.\n\nUsing the WITH GRANT OPTION clause is equivalent to listing GRANT OPTION as a\nprivilege.\n\nGlobal Privileges\n-----------------\n\nThe following table lists the privileges that can be granted globally. You can\nalso grant all database, table, and function privileges globally. When granted\nglobally, these privileges apply to all databases, tables, or functions,\nincluding those created later.\n\nTo set a global privilege, use *.* for priv_level.\n\nBINLOG ADMIN\n------------\n\nEnables administration of the binary log, including the PURGE BINARY LOGS\nstatement and setting the system variables:\n\n* binlog_annotate_row_events\n* binlog_cache_size\n* binlog_commit_wait_count\n* binlog_commit_wait_usec\n* binlog_direct_non_transactional_updates\n* binlog_expire_logs_seconds\n* binlog_file_cache_size\n* binlog_format\n* binlog_row_image\n* binlog_row_metadata\n* binlog_stmt_cache_size\n* expire_logs_days\n* log_bin_compress\n* log_bin_compress_min_len\n* log_bin_trust_function_creators\n* max_binlog_cache_size\n* max_binlog_size\n* max_binlog_stmt_cache_size\n* sql_log_bin and\n* sync_binlog.\n\nAdded in MariaDB 10.5.2.\n\nBINLOG MONITOR\n--------------\n\nNew name for REPLICATION CLIENT from MariaDB 10.5.2, (REPLICATION CLIENT still\nsupported as an alias for compatibility purposes). Permits running SHOW\ncommands related to the binary log, in particular the SHOW BINLOG STATUS and\nSHOW BINARY LOGS statements. Unlike REPLICATION CLIENT prior to MariaDB 10.5,\nSHOW REPLICA STATUS isn\'t included in this privilege, and REPLICA MONITOR is\nrequired.\n\nBINLOG REPLAY\n-------------\n\nEnables replaying the binary log with the BINLOG statement (generated by\nmariadb-binlog), executing SET timestamp when secure_timestamp is set to\nreplication, and setting the session values of system variables usually\nincluded in BINLOG output, in particular:\n\n* gtid_domain_id\n* gtid_seq_no\n* pseudo_thread_id\n* server_id.\n\nAdded in MariaDB 10.5.2\n\nCONNECTION ADMIN\n----------------\n\nEnables administering connection resource limit options. This includes\nignoring the limits specified by:\n\n* max_connections\n* max_user_connections and\n* max_password_errors.\n\nThe statements specified in init_connect are not executed, killing connections\nand queries owned by other users is permitted. The following\nconnection-related system variables can be changed:\n\n* connect_timeout\n* disconnect_on_expired_password\n* extra_max_connections\n* init_connect\n* max_connections\n* max_connect_errors\n* max_password_errors\n* proxy_protocol_networks\n* secure_auth\n* slow_launch_time\n* thread_pool_exact_stats\n* thread_pool_dedicated_listener\n* thread_pool_idle_timeout\n* thread_pool_max_threads\n* thread_pool_min_threads\n* thread_pool_oversubscribe\n* thread_pool_prio_kickup_timer\n* thread_pool_priority\n* thread_pool_size, and\n* thread_pool_stall_limit.\n\nAdded in MariaDB 10.5.2.\n\nCREATE USER\n-----------\n\nCreate a user using the CREATE USER statement, or implicitly create a user\nwith the GRANT statement.\n\nFEDERATED ADMIN\n---------------\n\nExecute CREATE SERVER, ALTER SERVER, and DROP SERVER statements. Added in\nMariaDB 10.5.2.\n\nFILE\n----\n\nRead and write files on the server, using statements like LOAD DATA INFILE or\nfunctions like LOAD_FILE(). Also needed to create CONNECT outward tables.\nMariaDB server must have the permissions to access those files.\n\nGRANT OPTION\n------------\n\nGrant global privileges. You can only grant privileges that you have.\n\nPROCESS\n-------\n\nShow information about the active processes, for example via SHOW PROCESSLIST\nor mysqladmin processlist. If you have the PROCESS privilege, you can see all\nthreads. Otherwise, you can see only your own threads (that is, threads\nassociated with the MariaDB account that you are using).\n\nREAD_ONLY ADMIN\n---------------\n\nUser can set the read_only system variable and allows the user to perform\nwrite operations, even when the read_only option is active. Added in MariaDB\n10.5.2.\n\nFrom MariaDB 10.11.0, the READ_ONLY ADMIN privilege has been removed from\nSUPER. The benefit of this is that one can remove the READ_ONLY ADMIN\nprivilege from all users and ensure that no one can make any changes on any\nnon-temporary tables. This is useful on replicas when one wants to ensure that\nthe replica is kept identical to the primary.\n\nRELOAD\n------\n\nExecute FLUSH statements or equivalent mariadb-admin/mysqladmin commands.\n\nREPLICATION CLIENT\n------------------\n\nExecute SHOW MASTER STATUS and SHOW BINARY LOGS informative statements.\nRenamed to BINLOG MONITOR in MariaDB 10.5.2 (but still supported as an alias\nfor compatibility reasons). SHOW SLAVE STATUS was part of REPLICATION CLIENT\nprior to MariaDB 10.5.\n\nREPLICATION MASTER ADMIN\n------------------------\n\nPermits administration of primary servers, including the SHOW REPLICA HOSTS\nstatement, and setting the gtid_binlog_state, gtid_domain_id,\nmaster_verify_checksum and server_id system variables. Added in MariaDB 10.5.2.\n\nREPLICA MONITOR\n---------------\n\nPermit SHOW REPLICA STATUS and SHOW RELAYLOG EVENTS. From MariaDB 10.5.9.\n\nWhen a user would upgrade from an older major release to a MariaDB 10.5 minor\nrelease prior to MariaDB 10.5.9, certain user accounts would lose\ncapabilities. For example, a user account that had the REPLICATION CLIENT\nprivilege in older major releases could run SHOW REPLICA STATUS, but after\nupgrading to a MariaDB 10.5 minor release prior to MariaDB 10.5.9, they could\nno longer run SHOW REPLICA STATUS, because that statement was changed to\nrequire the REPLICATION REPLICA ADMIN privilege.\n\nThis issue is fixed in MariaDB 10.5.9 with this new privilege, which now\ngrants the user the ability to execute SHOW [ALL] (SLAVE | REPLICA) STATUS.\n\nWhen a database is upgraded from an older major release to MariaDB Server\n10.5.9 or later, any user accounts with the REPLICATION CLIENT or REPLICATION\nSLAVE privileges will automatically be granted the new REPLICA MONITOR\nprivilege. The privilege fix occurs when the server is started up, not when\nmariadb-upgrade is performed.\n\nHowever, when a database is upgraded from an early 10.5 minor release to\n10.5.9 and later, the user will have to fix any user account privileges\nmanually.\n\nREPLICATION REPLICA\n-------------------\n\nSynonym for REPLICATION SLAVE. From MariaDB 10.5.1.\n\nREPLICATION SLAVE\n-----------------\n\nAccounts used by replica servers on the primary need this privilege. This is\nneeded to get the updates made on the master. From MariaDB 10.5.1, REPLICATION\nREPLICA is an alias for REPLICATION SLAVE.\n\nREPLICATION SLAVE ADMIN\n-----------------------\n\nPermits administering replica servers, including START REPLICA/SLAVE, STOP\nREPLICA/SLAVE, CHANGE MASTER, SHOW REPLICA/SLAVE STATUS, SHOW RELAYLOG EVENTS\nstatements, replaying the binary log with the BINLOG statement (generated by\nmariadb-binlog), and setting the system variables:\n\n* gtid_cleanup_batch_size\n* gtid_ignore_duplicates\n* gtid_pos_auto_engines\n* gtid_slave_pos\n* gtid_strict_mode\n* init_slave\n* read_binlog_speed_limit\n* relay_log_purge\n* relay_log_recovery\n* replicate_do_db\n* replicate_do_table\n* replicate_events_marked_for_skip\n* replicate_ignore_db\n* replicate_ignore_table\n* replicate_wild_do_table\n* replicate_wild_ignore_table\n* slave_compressed_protocol\n* slave_ddl_exec_mode\n* slave_domain_parallel_threads\n* slave_exec_mode\n* slave_max_allowed_packet\n* slave_net_timeout\n* slave_parallel_max_queued\n* slave_parallel_mode\n* slave_parallel_threads\n* slave_parallel_workers\n* slave_run_triggers_for_rbr\n* slave_sql_verify_checksum\n* slave_transaction_retry_interval\n* slave_type_conversions\n* sync_master_info\n* sync_relay_log, and\n* sync_relay_log_info.\n\nAdded in MariaDB 10.5.2.\n\nSET USER\n--------\n','','https://mariadb.com/kb/en/grant/'); update help_topic set description = CONCAT(description, '\nEnables setting the DEFINER when creating triggers, views, stored functions\nand stored procedures. Added in MariaDB 10.5.2.\n\nSHOW DATABASES\n--------------\n\nList all databases using the SHOW DATABASES statement. Without the SHOW\nDATABASES privilege, you can still issue the SHOW DATABASES statement, but it\nwill only list databases containing tables on which you have privileges.\n\nSHUTDOWN\n--------\n\nShut down the server using SHUTDOWN or the mysqladmin shutdown command.\n\nSUPER\n-----\n\nExecute superuser statements: CHANGE MASTER TO, KILL (users who do not have\nthis privilege can only KILL their own threads), PURGE LOGS, SET global system\nvariables, or the mysqladmin debug command. Also, this permission allows the\nuser to write data even if the read_only startup option is set, enable or\ndisable logging, enable or disable replication on replica, specify a DEFINER\nfor statements that support that clause, connect once reaching the\nMAX_CONNECTIONS. If a statement has been specified for the init-connect mysqld\noption, that command will not be executed when a user with SUPER privileges\nconnects to the server.\n\nThe SUPER privilege has been split into multiple smaller privileges from\nMariaDB 10.5.2 to allow for more fine-grained privileges, although it remains\nan alias for these smaller privileges.\n\nFrom MariaDB 10.11.0, the READ_ONLY ADMIN privilege has been removed from\nSUPER. The benefit of this is that one can remove the READ_ONLY ADMIN\nprivilege from all users and ensure that no one can make any changes on any\nnon-temporary tables. This is useful on replicas when one wants to ensure that\nthe replica is kept identical to the primary.\n\nDatabase Privileges\n-------------------\n\nThe following table lists the privileges that can be granted at the database\nlevel. You can also grant all table and function privileges at the database\nlevel. Table and function privileges on a database apply to all tables or\nfunctions in that database, including those created later.\n\nTo set a privilege for a database, specify the database using db_name.* for\npriv_level, or just use * to specify the default database.\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |\n+----------------------------------+-----------------------------------------+\n| CREATE | Create a database using the CREATE |\n| | DATABASE statement, when the privilege |\n| | is granted for a database. You can |\n| | grant the CREATE privilege on |\n| | databases that do not yet exist. This |\n| | also grants the CREATE privilege on |\n| | all tables in the database. |\n+----------------------------------+-----------------------------------------+\n| CREATE ROUTINE | Create Stored Programs using the |\n| | CREATE PROCEDURE and CREATE FUNCTION |\n| | statements. |\n+----------------------------------+-----------------------------------------+\n| CREATE TEMPORARY TABLES | Create temporary tables with the |\n| | CREATE TEMPORARY TABLE statement. This |\n| | privilege enable writing and dropping |\n| | those temporary tables |\n+----------------------------------+-----------------------------------------+\n| DROP | Drop a database using the DROP |\n| | DATABASE statement, when the privilege |\n| | is granted for a database. This also |\n| | grants the DROP privilege on all |\n| | tables in the database. |\n+----------------------------------+-----------------------------------------+\n| EVENT | Create, drop and alter EVENTs. |\n+----------------------------------+-----------------------------------------+\n| GRANT OPTION | Grant database privileges. You can |\n| | only grant privileges that you have. |\n+----------------------------------+-----------------------------------------+\n| LOCK TABLES | Acquire explicit locks using the LOCK |\n| | TABLES statement; you also need to |\n| | have the SELECT privilege on a table, |\n| | in order to lock it. |\n+----------------------------------+-----------------------------------------+\n\nTable Privileges\n----------------\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |\n+----------------------------------+-----------------------------------------+\n| ALTER | Change the structure of an existing |\n| | table using the ALTER TABLE statement. |\n+----------------------------------+-----------------------------------------+\n| CREATE | Create a table using the CREATE TABLE |\n| | statement. You can grant the CREATE |\n| | privilege on tables that do not yet |\n| | exist. |\n+----------------------------------+-----------------------------------------+\n| CREATE VIEW | Create a view using the CREATE_VIEW |\n| | statement. |\n+----------------------------------+-----------------------------------------+\n| DELETE | Remove rows from a table using the |\n| | DELETE statement. |\n+----------------------------------+-----------------------------------------+\n| DELETE HISTORY | Remove historical rows from a table |\n| | using the DELETE HISTORY statement. |\n| | Displays as DELETE VERSIONING ROWS |\n| | when running SHOW GRANTS until MariaDB |\n| | 10.3.15 and until MariaDB 10.4.5 |\n| | (MDEV-17655), or when running SHOW |\n| | PRIVILEGES until MariaDB 10.5.2, |\n| | MariaDB 10.4.13 and MariaDB 10.3.23 |\n| | (MDEV-20382). From MariaDB 10.3.4. |\n| | From MariaDB 10.3.5, if a user has the |\n| | SUPER privilege but not this |\n| | privilege, running mysql_upgrade will |\n| | grant this privilege as well. |\n+----------------------------------+-----------------------------------------+\n| DROP | Drop a table using the DROP TABLE |\n| | statement or a view using the DROP |\n| | VIEW statement. Also required to |\n| | execute the TRUNCATE TABLE statement. |\n+----------------------------------+-----------------------------------------+\n| GRANT OPTION | Grant table privileges. You can only |\n| | grant privileges that you have. |\n+----------------------------------+-----------------------------------------+\n| INDEX | Create an index on a table using the |\n| | CREATE INDEX statement. Without the |\n| | INDEX privilege, you can still create |\n| | indexes when creating a table using |\n| | the CREATE TABLE statement if the you |\n| | have the CREATE privilege, and you can |\n| | create indexes using the ALTER TABLE |\n| | statement if you have the ALTER |\n| | privilege. |\n+----------------------------------+-----------------------------------------+\n| INSERT | Add rows to a table using the INSERT |\n| | statement. The INSERT privilege can |\n| | also be set on individual columns; see |\n| | Column Privileges below for details. |\n+----------------------------------+-----------------------------------------+\n| REFERENCES | Unused. |\n+----------------------------------+-----------------------------------------+\n| SELECT | Read data from a table using the |\n| | SELECT statement. The SELECT privilege |\n| | can also be set on individual columns; |\n| | see Column Privileges below for |\n| | details. |\n+----------------------------------+-----------------------------------------+\n| SHOW VIEW | Show the CREATE VIEW statement to |\n| | create a view using the SHOW CREATE |\n| | VIEW statement. |\n+----------------------------------+-----------------------------------------+\n| TRIGGER | Execute triggers associated to tables |\n| | you update, execute the CREATE TRIGGER |\n| | and DROP TRIGGER statements. You will |\n| | still be able to see triggers. |\n+----------------------------------+-----------------------------------------+\n| UPDATE | Update existing rows in a table using |\n| | the UPDATE statement. UPDATE |\n| | statements usually include a WHERE |\n| | clause to update only certain rows. |\n| | You must have SELECT privileges on the |\n| | table or the appropriate columns for |\n| | the WHERE clause. The UPDATE privilege |\n| | can also be set on individual columns; |\n| | see Column Privileges below for |\n| | details. |\n+----------------------------------+-----------------------------------------+\n\nColumn Privileges\n-----------------\n\nSome table privileges can be set for individual columns of a table. To use\ncolumn privileges, specify the table explicitly and provide a list of column\nnames after the privilege type. For example, the following statement would\nallow the user to read the names and positions of employees, but not other\ninformation from the same table, such as salaries.\n\nGRANT SELECT (name, position) on Employee to \'jeffrey\'@\'localhost\';\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |\n+----------------------------------+-----------------------------------------+\n| INSERT (column_list) | Add rows specifying values in columns |\n| | using the INSERT statement. If you |\n| | only have column-level INSERT |\n| | privileges, you must specify the |\n| | columns you are setting in the INSERT |\n| | statement. All other columns will be |\n| | set to their default values, or NULL. |\n+----------------------------------+-----------------------------------------+\n| REFERENCES (column_list) | Unused. |\n+----------------------------------+-----------------------------------------+\n| SELECT (column_list) | Read values in columns using the |\n| | SELECT statement. You cannot access or |\n| | query any columns for which you do not |\n| | have SELECT privileges, including in |\n| | WHERE, ON, GROUP BY, and ORDER BY |\n| | clauses. |\n+----------------------------------+-----------------------------------------+\n| UPDATE (column_list) | Update values in columns of existing |\n| | rows using the UPDATE statement. |\n| | UPDATE statements usually include a |\n| | WHERE clause to update only certain |\n| | rows. You must have SELECT privileges |\n| | on the table or the appropriate |\n| | columns for the WHERE clause. |\n+----------------------------------+-----------------------------------------+\n\nFunction Privileges\n-------------------\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |') WHERE help_topic_id = 107; update help_topic set description = CONCAT(description, '\n+----------------------------------+-----------------------------------------+\n| ALTER ROUTINE | Change the characteristics of a stored |\n| | function using the ALTER FUNCTION |\n| | statement. |\n+----------------------------------+-----------------------------------------+\n| EXECUTE | Use a stored function. You need SELECT |\n| | privileges for any tables or columns |\n| | accessed by the function. |\n+----------------------------------+-----------------------------------------+\n| GRANT OPTION | Grant function privileges. You can |\n| | only grant privileges that you have. |\n+----------------------------------+-----------------------------------------+\n\nProcedure Privileges\n--------------------\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |\n+----------------------------------+-----------------------------------------+\n| ALTER ROUTINE | Change the characteristics of a stored |\n| | procedure using the ALTER PROCEDURE |\n| | statement. |\n+----------------------------------+-----------------------------------------+\n| EXECUTE | Execute a stored procedure using the |\n| | CALL statement. The privilege to call |\n| | a procedure may allow you to perform |\n| | actions you wouldn\'t otherwise be able |\n| | to do, such as insert rows into a |\n| | table. |\n+----------------------------------+-----------------------------------------+\n| GRANT OPTION | Grant procedure privileges. You can |\n| | only grant privileges that you have. |\n+----------------------------------+-----------------------------------------+\n\nGRANT EXECUTE ON PROCEDURE mysql.create_db TO maintainer;\n\nProxy Privileges\n----------------\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |\n+----------------------------------+-----------------------------------------+\n| PROXY | Permits one user to be a proxy for |\n| | another. |\n+----------------------------------+-----------------------------------------+\n\nThe PROXY privilege allows one user to proxy as another user, which means\ntheir privileges change to that of the proxy user, and the CURRENT_USER()\nfunction returns the user name of the proxy user.\n\nThe PROXY privilege only works with authentication plugins that support it.\nThe default mysql_native_password authentication plugin does not support proxy\nusers.\n\nThe pam authentication plugin is the only plugin included with MariaDB that\ncurrently supports proxy users. The PROXY privilege is commonly used with the\npam authentication plugin to enable user and group mapping with PAM.\n\nFor example, to grant the PROXY privilege to an anonymous account that\nauthenticates with the pam authentication plugin, you could execute the\nfollowing:\n\nCREATE USER \'dba\'@\'%\' IDENTIFIED BY \'strongpassword\';\nGRANT ALL PRIVILEGES ON *.* TO \'dba\'@\'%\' ;\n\nCREATE USER \'\'@\'%\' IDENTIFIED VIA pam USING \'mariadb\';\nGRANT PROXY ON \'dba\'@\'%\' TO \'\'@\'%\';\n\nA user account can only grant the PROXY privilege for a specific user account\nif the granter also has the PROXY privilege for that specific user account,\nand if that privilege is defined WITH GRANT OPTION. For example, the following\nexample fails because the granter does not have the PROXY privilege for that\nspecific user account at all:\n\nSELECT USER(), CURRENT_USER();\n+-----------------+-----------------+\n| USER() | CURRENT_USER() |\n+-----------------+-----------------+\n| alice@localhost | alice@localhost |\n+-----------------+-----------------+\n\nSHOW GRANTS;\n+------------------------------------------------------------------------------\n----------------------------------------+\n| Grants for alice@localhost \n |\n+------------------------------------------------------------------------------\n----------------------------------------+\n| GRANT ALL PRIVILEGES ON *.* TO \'alice\'@\'localhost\' IDENTIFIED BY PASSWORD\n\'*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19\' |\n+------------------------------------------------------------------------------\n----------------------------------------+\n\nGRANT PROXY ON \'dba\'@\'localhost\' TO \'bob\'@\'localhost\';\nERROR 1698 (28000): Access denied for user \'alice\'@\'localhost\'\n\nAnd the following example fails because the granter does have the PROXY\nprivilege for that specific user account, but it is not defined WITH GRANT\nOPTION:\n\nSELECT USER(), CURRENT_USER();\n+-----------------+-----------------+\n| USER() | CURRENT_USER() |\n+-----------------+-----------------+\n| alice@localhost | alice@localhost |\n+-----------------+-----------------+\n\nSHOW GRANTS;\n+------------------------------------------------------------------------------\n----------------------------------------+\n| Grants for alice@localhost \n |\n+------------------------------------------------------------------------------\n----------------------------------------+\n| GRANT ALL PRIVILEGES ON *.* TO \'alice\'@\'localhost\' IDENTIFIED BY PASSWORD\n\'*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19\' |\n| GRANT PROXY ON \'dba\'@\'localhost\' TO \'alice\'@\'localhost\' \n |\n+------------------------------------------------------------------------------\n----------------------------------------+\n\nGRANT PROXY ON \'dba\'@\'localhost\' TO \'bob\'@\'localhost\';\nERROR 1698 (28000): Access denied for user \'alice\'@\'localhost\'\n\nBut the following example succeeds because the granter does have the PROXY\nprivilege for that specific user account, and it is defined WITH GRANT OPTION:\n\nSELECT USER(), CURRENT_USER();\n+-----------------+-----------------+\n| USER() | CURRENT_USER() |\n+-----------------+-----------------+\n| alice@localhost | alice@localhost |\n+-----------------+-----------------+\n\nSHOW GRANTS;\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n| Grants for alice@localhost \n |\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n| GRANT ALL PRIVILEGES ON *.* TO \'alice\'@\'localhost\' IDENTIFIED BY PASSWORD\n\'*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19\' WITH GRANT OPTION |\n| GRANT PROXY ON \'dba\'@\'localhost\' TO \'alice\'@\'localhost\' WITH GRANT OPTION \n |\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n\nGRANT PROXY ON \'dba\'@\'localhost\' TO \'bob\'@\'localhost\';\n\nA user account can grant the PROXY privilege for any other user account if the\ngranter has the PROXY privilege for the \'\'@\'%\' anonymous user account, like\nthis:\n\nGRANT PROXY ON \'\'@\'%\' TO \'dba\'@\'localhost\' WITH GRANT OPTION;\n\nFor example, the following example succeeds because the user can grant the\nPROXY privilege for any other user account:\n\nSELECT USER(), CURRENT_USER();\n+-----------------+-----------------+\n| USER() | CURRENT_USER() |\n+-----------------+-----------------+\n| alice@localhost | alice@localhost |\n+-----------------+-----------------+\n\nSHOW GRANTS;\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n| Grants for alice@localhost \n |\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n| GRANT ALL PRIVILEGES ON *.* TO \'alice\'@\'localhost\' IDENTIFIED BY PASSWORD\n\'*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19\' WITH GRANT OPTION |\n| GRANT PROXY ON \'\'@\'%\' TO \'alice\'@\'localhost\' WITH GRANT OPTION \n |\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n\nGRANT PROXY ON \'app1_dba\'@\'localhost\' TO \'bob\'@\'localhost\';\nQuery OK, 0 rows affected (0.004 sec)\n\nGRANT PROXY ON \'app2_dba\'@\'localhost\' TO \'carol\'@\'localhost\';\nQuery OK, 0 rows affected (0.004 sec)\n\nThe default root user accounts created by mysql_install_db have this\nprivilege. For example:\n\nGRANT ALL PRIVILEGES ON *.* TO \'root\'@\'localhost\' WITH GRANT OPTION;\nGRANT PROXY ON \'\'@\'%\' TO \'root\'@\'localhost\' WITH GRANT OPTION;\n\nThis allows the default root user accounts to grant the PROXY privilege for\nany other user account, and it also allows the default root user accounts to\ngrant others the privilege to do the same.\n\nAuthentication Options\n----------------------\n\nThe authentication options for the GRANT statement are the same as those for\nthe CREATE USER statement.\n\nIDENTIFIED BY \'password\'\n------------------------\n\nThe optional IDENTIFIED BY clause can be used to provide an account with a\npassword. The password should be specified in plain text. It will be hashed by\nthe PASSWORD function prior to being stored.\n\nFor example, if our password is mariadb, then we can create the user with:\n\nGRANT USAGE ON *.* TO foo2@test IDENTIFIED BY \'mariadb\';\n\nIf you do not specify a password with the IDENTIFIED BY clause, the user will\nbe able to connect without a password. A blank password is not a wildcard to\nmatch any password. The user must connect without providing a password if no\npassword is set.\n\nIf the user account already exists and if you provide the IDENTIFIED BY\nclause, then the user\'s password will be changed. You must have the privileges\nneeded for the SET PASSWORD statement to change a user\'s password with GRANT.\n\nThe only authentication plugins that this clause supports are\nmysql_native_password and mysql_old_password.\n\nIDENTIFIED BY PASSWORD \'password_hash\'\n--------------------------------------\n\nThe optional IDENTIFIED BY PASSWORD clause can be used to provide an account\nwith a password that has already been hashed. The password should be specified\nas a hash that was provided by the PASSWORD function. It will be stored as-is.\n\nFor example, if our password is mariadb, then we can find the hash with:\n\nSELECT PASSWORD(\'mariadb\');\n+-------------------------------------------+\n| PASSWORD(\'mariadb\') |\n+-------------------------------------------+\n| *54958E764CE10E50764C2EECBB71D01F08549980 |\n+-------------------------------------------+\n1 row in set (0.00 sec)\n\nAnd then we can create a user with the hash:\n\nGRANT USAGE ON *.* TO foo2@test IDENTIFIED BY \n PASSWORD \'*54958E764CE10E50764C2EECBB71D01F08549980\';\n\nIf you do not specify a password with the IDENTIFIED BY clause, the user will\nbe able to connect without a password. A blank password is not a wildcard to\nmatch any password. The user must connect without providing a password if no\npassword is set.\n\nIf the user account already exists and if you provide the IDENTIFIED BY\nclause, then the user\'s password will be changed. You must have the privileges\nneeded for the SET PASSWORD statement to change a user\'s password with GRANT.\n\nThe only authentication plugins that this clause supports are\nmysql_native_password and mysql_old_password.\n\nIDENTIFIED {VIA|WITH} authentication_plugin\n-------------------------------------------\n\nThe optional IDENTIFIED VIA authentication_plugin allows you to specify that\nthe account should be authenticated by a specific authentication plugin. The\nplugin name must be an active authentication plugin as per SHOW PLUGINS. If it\ndoesn\'t show up in that output, then you will need to install it with INSTALL\nPLUGIN or INSTALL SONAME.\n\nFor example, this could be used with the PAM authentication plugin:\n\nGRANT USAGE ON *.* TO foo2@test IDENTIFIED VIA pam;\n\nSome authentication plugins allow additional arguments to be specified after a\nUSING or AS keyword. For example, the PAM authentication plugin accepts a\nservice name:\n\nGRANT USAGE ON *.* TO foo2@test IDENTIFIED VIA pam USING \'mariadb\';\n\nThe exact meaning of the additional argument would depend on the specific\nauthentication plugin.\n\nMariaDB starting with 10.4.0\n----------------------------\nThe USING or AS keyword can also be used to provide a plain-text password to a\nplugin if it\'s provided as an argument to the PASSWORD() function. This is\nonly valid for authentication plugins that have implemented a hook for the\nPASSWORD() function. For example, the ed25519 authentication plugin supports\nthis:\n\nCREATE USER safe@\'%\' IDENTIFIED VIA ed25519 \n USING PASSWORD(\'secret\');\n\nMariaDB starting with 10.4.3\n----------------------------\nOne can specify many authentication plugins, they all work as alternatives\nways of authenticating a user:\n\nCREATE USER safe@\'%\' IDENTIFIED VIA ed25519 \n USING PASSWORD(\'secret\') OR unix_socket;\n\nBy default, when you create a user without specifying an authentication\nplugin, MariaDB uses the mysql_native_password plugin.\n\nResource Limit Options\n----------------------\n\nIt is possible to set per-account limits for certain server resources. The\nfollowing table shows the values that can be set per account:\n\n+--------------------------------------+--------------------------------------+') WHERE help_topic_id = 107; -update help_topic set description = CONCAT(description, '\n| Limit Type | Decription |\n+--------------------------------------+--------------------------------------+\n| MAX_QUERIES_PER_HOUR | Number of statements that the |\n| | account can issue per hour |\n| | (including updates) |\n+--------------------------------------+--------------------------------------+\n| MAX_UPDATES_PER_HOUR | Number of updates (not queries) |\n| | that the account can issue per hour |\n+--------------------------------------+--------------------------------------+\n| MAX_CONNECTIONS_PER_HOUR | Number of connections that the |\n| | account can start per hour |\n+--------------------------------------+--------------------------------------+\n| MAX_USER_CONNECTIONS | Number of simultaneous connections |\n| | that can be accepted from the same |\n| | account; if it is 0, |\n| | max_connections will be used |\n| | instead; if max_connections is 0, |\n| | there is no limit for this |\n| | account\'s simultaneous connections. |\n+--------------------------------------+--------------------------------------+\n| MAX_STATEMENT_TIME | Timeout, in seconds, for statements |\n| | executed by the user. See also |\n| | Aborting Statements that Exceed a |\n| | Certain Time to Execute. |\n+--------------------------------------+--------------------------------------+\n\nIf any of these limits are set to 0, then there is no limit for that resource\nfor that user.\n\nTo set resource limits for an account, if you do not want to change that\naccount\'s privileges, you can issue a GRANT statement with the USAGE\nprivilege, which has no meaning. The statement can name some or all limit\ntypes, in any order.\n\nHere is an example showing how to set resource limits:\n\nGRANT USAGE ON *.* TO \'someone\'@\'localhost\' WITH\n MAX_USER_CONNECTIONS 0\n MAX_QUERIES_PER_HOUR 200;\n\nThe resources are tracked per account, which means \'user\'@\'server\'; not per\nuser name or per connection.\n\nThe count can be reset for all users using FLUSH USER_RESOURCES, FLUSH\nPRIVILEGES or mysqladmin reload.\n\nUsers with the CONNECTION ADMIN privilege (in MariaDB 10.5.2 and later) or the\nSUPER privilege are not restricted by max_user_connections, max_connections,\nor max_password_errors.\n\nPer account resource limits are stored in the user table, in the mysql\ndatabase. Columns used for resources limits are named max_questions,\nmax_updates, max_connections (for MAX_CONNECTIONS_PER_HOUR), and\nmax_user_connections (for MAX_USER_CONNECTIONS).\n\nTLS Options\n-----------\n\nBy default, MariaDB transmits data between the server and clients without\nencrypting it. This is generally acceptable when the server and client run on\nthe same host or in networks where security is guaranteed through other means.\nHowever, in cases where the server and client exist on separate networks or\nthey are in a high-risk network, the lack of encryption does introduce\nsecurity concerns as a malicious actor could potentially eavesdrop on the\ntraffic as it is sent over the network between them.\n\nTo mitigate this concern, MariaDB allows you to encrypt data in transit\nbetween the server and clients using the Transport Layer Security (TLS)\nprotocol. TLS was formerly known as Secure Socket Layer (SSL), but strictly\nspeaking the SSL protocol is a predecessor to TLS and, that version of the\nprotocol is now considered insecure. The documentation still uses the term SSL\noften and for compatibility reasons TLS-related server system and status\nvariables still use the prefix ssl_, but internally, MariaDB only supports its\nsecure successors.\n\nSee Secure Connections Overview for more information about how to determine\nwhether your MariaDB server has TLS support.\n\nYou can set certain TLS-related restrictions for specific user accounts. For\ninstance, you might use this with user accounts that require access to\nsensitive data while sending it across networks that you do not control. These\nrestrictions can be enabled for a user account with the CREATE USER, ALTER\nUSER, or GRANT statements. The following options are available:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| REQUIRE NONE | TLS is not required for this account, but can |\n| | still be used. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SSL | The account must use TLS, but no valid X509 |\n| | certificate is required. This option cannot |\n| | be combined with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE X509 | The account must use TLS and must have a |\n| | valid X509 certificate. This option implies |\n| | REQUIRE SSL. This option cannot be combined |\n| | with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE ISSUER \'issuer\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the Certificate |\n| | Authority must be the one specified via the |\n| | string issuer. This option implies REQUIRE |\n| | X509. This option can be combined with the |\n| | SUBJECT, and CIPHER options in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SUBJECT \'subject\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the |\n| | certificate\'s Subject must be the one |\n| | specified via the string subject. This option |\n| | implies REQUIRE X509. This option can be |\n| | combined with the ISSUER, and CIPHER options |\n| | in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE CIPHER \'cipher\' | The account must use TLS, but no valid X509 |\n| | certificate is required. Also, the encryption |\n| | used for the connection must use a specific |\n| | cipher method specified in the string cipher. |\n| | This option implies REQUIRE SSL. This option |\n| | can be combined with the ISSUER, and SUBJECT |\n| | options in any order. |\n+---------------------------+------------------------------------------------+\n\nThe REQUIRE keyword must be used only once for all specified options, and the\nAND keyword can be used to separate individual options, but it is not required.\n\nFor example, you can create a user account that requires these TLS options\nwith the following:\n\nGRANT USAGE ON *.* TO \'alice\'@\'%\'\n REQUIRE SUBJECT \'/CN=alice/O=My Dom, Inc./C=US/ST=Oregon/L=Portland\'\n AND ISSUER \'/C=FI/ST=Somewhere/L=City/ O=Some Company/CN=Peter\nParker/emailAddress=p.parker@marvel.com\'\n AND CIPHER \'SHA-DES-CBC3-EDH-RSA\';\n\nIf any of these options are set for a specific user account, then any client\nwho tries to connect with that user account will have to be configured to\nconnect with TLS.\n\nSee Securing Connections for Client and Server for information on how to\nenable TLS on the client and server.\n\nRoles\n-----\n\nSyntax\n------\n\nGRANT role TO grantee [, grantee ... ]\n[ WITH ADMIN OPTION ]\n\ngrantee:\n rolename\n username [authentication_option]\n\nThe GRANT statement is also used to grant the use a role to one or more users\nor other roles. In order to be able to grant a role, the grantor doing so must\nhave permission to do so (see WITH ADMIN in the CREATE ROLE article).\n\nSpecifying the WITH ADMIN OPTION permits the grantee to in turn grant the role\nto another.\n\nFor example, the following commands show how to grant the same role to a\ncouple different users.\n\nGRANT journalist TO hulda;\n\nGRANT journalist TO berengar WITH ADMIN OPTION;\n\nIf a user has been granted a role, they do not automatically obtain all\npermissions associated with that role. These permissions are only in use when\nthe user activates the role with the SET ROLE statement.\n\nTO PUBLIC\n---------\n\nMariaDB starting with 10.11\n---------------------------\n\nSyntax\n------\n\nGRANT ON . TO PUBLIC;\nREVOKE ON . FROM PUBLIC;\n\nGRANT ... TO PUBLIC grants privileges to all users with access to the server.\nThe privileges also apply to users created after the privileges are granted.\nThis can be useful when one only wants to state once that all users need to\nhave a certain set of privileges.\n\nWhen running SHOW GRANTS, a user will also see all privileges inherited from\nPUBLIC. SHOW GRANTS FOR PUBLIC will only show TO PUBLIC grants.\n\nGrant Examples\n--------------\n\nGranting Root-like Privileges\n-----------------------------\n\nYou can create a user that has privileges similar to the default root accounts\nby executing the following:\n\nCREATE USER \'alexander\'@\'localhost\';\nGRANT ALL PRIVILEGES ON *.* to \'alexander\'@\'localhost\' WITH GRANT OPTION;\n\nURL: https://mariadb.com/kb/en/grant/') WHERE help_topic_id = 107; +update help_topic set description = CONCAT(description, '\n| Limit Type | Decription |\n+--------------------------------------+--------------------------------------+\n| MAX_QUERIES_PER_HOUR | Number of statements that the |\n| | account can issue per hour |\n| | (including updates) |\n+--------------------------------------+--------------------------------------+\n| MAX_UPDATES_PER_HOUR | Number of updates (not queries) |\n| | that the account can issue per hour |\n+--------------------------------------+--------------------------------------+\n| MAX_CONNECTIONS_PER_HOUR | Number of connections that the |\n| | account can start per hour |\n+--------------------------------------+--------------------------------------+\n| MAX_USER_CONNECTIONS | Number of simultaneous connections |\n| | that can be accepted from the same |\n| | account; if it is 0, |\n| | max_connections will be used |\n| | instead; if max_connections is 0, |\n| | there is no limit for this |\n| | account\'s simultaneous connections. |\n+--------------------------------------+--------------------------------------+\n| MAX_STATEMENT_TIME | Timeout, in seconds, for statements |\n| | executed by the user. See also |\n| | Aborting Statements that Exceed a |\n| | Certain Time to Execute. |\n+--------------------------------------+--------------------------------------+\n\nIf any of these limits are set to 0, then there is no limit for that resource\nfor that user.\n\nTo set resource limits for an account, if you do not want to change that\naccount\'s privileges, you can issue a GRANT statement with the USAGE\nprivilege, which has no meaning. The statement can name some or all limit\ntypes, in any order.\n\nHere is an example showing how to set resource limits:\n\nGRANT USAGE ON *.* TO \'someone\'@\'localhost\' WITH\n MAX_USER_CONNECTIONS 0\n MAX_QUERIES_PER_HOUR 200;\n\nThe resources are tracked per account, which means \'user\'@\'server\'; not per\nuser name or per connection.\n\nThe count can be reset for all users using FLUSH USER_RESOURCES, FLUSH\nPRIVILEGES or mysqladmin reload.\n\nUsers with the CONNECTION ADMIN privilege (in MariaDB 10.5.2 and later) or the\nSUPER privilege are not restricted by max_user_connections, max_connections,\nor max_password_errors.\n\nPer account resource limits are stored in the user table, in the mysql\ndatabase. Columns used for resources limits are named max_questions,\nmax_updates, max_connections (for MAX_CONNECTIONS_PER_HOUR), and\nmax_user_connections (for MAX_USER_CONNECTIONS).\n\nTLS Options\n-----------\n\nBy default, MariaDB transmits data between the server and clients without\nencrypting it. This is generally acceptable when the server and client run on\nthe same host or in networks where security is guaranteed through other means.\nHowever, in cases where the server and client exist on separate networks or\nthey are in a high-risk network, the lack of encryption does introduce\nsecurity concerns as a malicious actor could potentially eavesdrop on the\ntraffic as it is sent over the network between them.\n\nTo mitigate this concern, MariaDB allows you to encrypt data in transit\nbetween the server and clients using the Transport Layer Security (TLS)\nprotocol. TLS was formerly known as Secure Socket Layer (SSL), but strictly\nspeaking the SSL protocol is a predecessor to TLS and, that version of the\nprotocol is now considered insecure. The documentation still uses the term SSL\noften and for compatibility reasons TLS-related server system and status\nvariables still use the prefix ssl_, but internally, MariaDB only supports its\nsecure successors.\n\nSee Secure Connections Overview for more information about how to determine\nwhether your MariaDB server has TLS support.\n\nYou can set certain TLS-related restrictions for specific user accounts. For\ninstance, you might use this with user accounts that require access to\nsensitive data while sending it across networks that you do not control. These\nrestrictions can be enabled for a user account with the CREATE USER, ALTER\nUSER, or GRANT statements. The following options are available:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| REQUIRE NONE | TLS is not required for this account, but can |\n| | still be used. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SSL | The account must use TLS, but no valid X509 |\n| | certificate is required. This option cannot |\n| | be combined with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE X509 | The account must use TLS and must have a |\n| | valid X509 certificate. This option implies |\n| | REQUIRE SSL. This option cannot be combined |\n| | with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE ISSUER \'issuer\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the Certificate |\n| | Authority must be the one specified via the |\n| | string issuer. This option implies REQUIRE |\n| | X509. This option can be combined with the |\n| | SUBJECT, and CIPHER options in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SUBJECT \'subject\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the |\n| | certificate\'s Subject must be the one |\n| | specified via the string subject. This option |\n| | implies REQUIRE X509. This option can be |\n| | combined with the ISSUER, and CIPHER options |\n| | in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE CIPHER \'cipher\' | The account must use TLS, but no valid X509 |\n| | certificate is required. Also, the encryption |\n| | used for the connection must use a specific |\n| | cipher method specified in the string cipher. |\n| | This option implies REQUIRE SSL. This option |\n| | can be combined with the ISSUER, and SUBJECT |\n| | options in any order. |\n+---------------------------+------------------------------------------------+\n\nThe REQUIRE keyword must be used only once for all specified options, and the\nAND keyword can be used to separate individual options, but it is not required.\n\nFor example, you can create a user account that requires these TLS options\nwith the following:\n\nGRANT USAGE ON *.* TO \'alice\'@\'%\'\n REQUIRE SUBJECT \'/CN=alice/O=My Dom, Inc./C=US/ST=Oregon/L=Portland\'\n AND ISSUER \'/C=FI/ST=Somewhere/L=City/ O=Some Company/CN=Peter\nParker/emailAddress=p.parker@marvel.com\'\n AND CIPHER \'SHA-DES-CBC3-EDH-RSA\';\n\nIf any of these options are set for a specific user account, then any client\nwho tries to connect with that user account will have to be configured to\nconnect with TLS.\n\nSee Securing Connections for Client and Server for information on how to\nenable TLS on the client and server.\n\nRoles\n-----\n\nSyntax\n------\n\nGRANT role TO grantee [, grantee ... ]\n[ WITH ADMIN OPTION ]\n\ngrantee:\n rolename\n username [authentication_option]\n\nThe GRANT statement is also used to grant the use of a role to one or more\nusers or other roles. In order to be able to grant a role, the grantor doing\nso must have permission to do so (see WITH ADMIN in the CREATE ROLE article).\n\nSpecifying the WITH ADMIN OPTION permits the grantee to in turn grant the role\nto another.\n\nFor example, the following commands show how to grant the same role to a\ncouple different users.\n\nGRANT journalist TO hulda;\n\nGRANT journalist TO berengar WITH ADMIN OPTION;\n\nIf a user has been granted a role, they do not automatically obtain all\npermissions associated with that role. These permissions are only in use when\nthe user activates the role with the SET ROLE statement.\n\nTO PUBLIC\n---------\n\nMariaDB starting with 10.11\n---------------------------\n\nSyntax\n------\n\nGRANT ON . TO PUBLIC;\nREVOKE ON . FROM PUBLIC;\n\nGRANT ... TO PUBLIC grants privileges to all users with access to the server.\nThe privileges also apply to users created after the privileges are granted.\nThis can be useful when one only wants to state once that all users need to\nhave a certain set of privileges.\n\nWhen running SHOW GRANTS, a user will also see all privileges inherited from\nPUBLIC. SHOW GRANTS FOR PUBLIC will only show TO PUBLIC grants.\n\nGrant Examples\n--------------\n\nGranting Root-like Privileges\n-----------------------------\n\nYou can create a user that has privileges similar to the default root accounts\nby executing the following:\n\nCREATE USER \'alexander\'@\'localhost\';\nGRANT ALL PRIVILEGES ON *.* to \'alexander\'@\'localhost\' WITH GRANT OPTION;\n\nURL: https://mariadb.com/kb/en/grant/') WHERE help_topic_id = 107; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (108,10,'RENAME USER','Syntax\n------\n\nRENAME USER old_user TO new_user\n [, old_user TO new_user] ...\n\nDescription\n-----------\n\nThe RENAME USER statement renames existing MariaDB accounts. To use it, you\nmust have the global CREATE USER privilege or the UPDATE privilege for the\nmysql database. Each account is named using the same format as for the CREATE\nUSER statement; for example, \'jeffrey\'@\'localhost\'. If you specify only the\nuser name part of the account name, a host name part of \'%\' is used.\n\nIf any of the old user accounts do not exist or any of the new user accounts\nalready exist, ERROR 1396 (HY000) results. If an error occurs, RENAME USER\nwill still rename the accounts that do not result in an error.\n\nExamples\n--------\n\nCREATE USER \'donald\', \'mickey\';\nRENAME USER \'donald\' TO \'duck\'@\'localhost\', \'mickey\' TO \'mouse\'@\'localhost\';\n\nURL: https://mariadb.com/kb/en/rename-user/','','https://mariadb.com/kb/en/rename-user/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (109,10,'REVOKE','Privileges\n----------\n\nSyntax\n------\n\nREVOKE \n priv_type [(column_list)]\n [, priv_type [(column_list)]] ...\n ON [object_type] priv_level\n FROM user [, user] ...\n\nREVOKE ALL PRIVILEGES, GRANT OPTION\n FROM user [, user] ...\n\nDescription\n-----------\n\nThe REVOKE statement enables system administrators to revoke privileges (or\nroles - see section below) from MariaDB accounts. Each account is named using\nthe same format as for the GRANT statement; for example,\n\'jeffrey\'@\'localhost\'. If you specify only the user name part of the account\nname, a host name part of \'%\' is used. For details on the levels at which\nprivileges exist, the allowable priv_type and priv_level values, and the\nsyntax for specifying users and passwords, see GRANT.\n\nTo use the first REVOKE syntax, you must have the GRANT OPTION privilege, and\nyou must have the privileges that you are revoking.\n\nTo revoke all privileges, use the second syntax, which drops all global,\ndatabase, table, column, and routine privileges for the named user or users:\n\nREVOKE ALL PRIVILEGES, GRANT OPTION FROM user [, user] ...\n\nTo use this REVOKE syntax, you must have the global CREATE USER privilege or\nthe UPDATE privilege for the mysql database. See GRANT.\n\nExamples\n--------\n\nREVOKE SUPER ON *.* FROM \'alexander\'@\'localhost\';\n\nRoles\n-----\n\nSyntax\n------\n\nREVOKE role [, role ...]\n FROM grantee [, grantee2 ... ]\n\nREVOKE ADMIN OPTION FOR role FROM grantee [, grantee2]\n\nDescription\n-----------\n\nREVOKE is also used to remove a role from a user or another role that it\'s\npreviously been assigned to. If a role has previously been set as a default\nrole, REVOKE does not remove the record of the default role from the\nmysql.user table. If the role is subsequently granted again, it will again be\nthe user\'s default. Use SET DEFAULT ROLE NONE to explicitly remove this.\n\nBefore MariaDB 10.1.13, the REVOKE role statement was not permitted in\nprepared statements.\n\nExample\n-------\n\nREVOKE journalist FROM hulda\n\nURL: https://mariadb.com/kb/en/revoke/','','https://mariadb.com/kb/en/revoke/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (110,10,'SET PASSWORD','Syntax\n------\n\nSET PASSWORD [FOR user] =\n {\n PASSWORD(\'some password\')\n | OLD_PASSWORD(\'some password\')\n | \'encrypted password\'\n }\n\nDescription\n-----------\n\nThe SET PASSWORD statement assigns a password to an existing MariaDB user\naccount.\n\nIf the password is specified using the PASSWORD() or OLD_PASSWORD() function,\nthe literal text of the password should be given. If the password is specified\nwithout using either function, the password should be the already-encrypted\npassword value as returned by PASSWORD().\n\nOLD_PASSWORD() should only be used if your MariaDB/MySQL clients are very old\n(< 4.0.0).\n\nWith no FOR clause, this statement sets the password for the current user. Any\nclient that has connected to the server using a non-anonymous account can\nchange the password for that account.\n\nWith a FOR clause, this statement sets the password for a specific account on\nthe current server host. Only clients that have the UPDATE privilege for the\nmysql database can do this. The user value should be given in\nuser_name@host_name format, where user_name and host_name are exactly as they\nare listed in the User and Host columns of the mysql.user table (or view in\nMariaDB-10.4 onwards) entry.\n\nThe argument to PASSWORD() and the password given to MariaDB clients can be of\narbitrary length.\n\nAuthentication Plugin Support\n-----------------------------\n\nMariaDB starting with 10.4\n--------------------------\nIn MariaDB 10.4 and later, SET PASSWORD (with or without PASSWORD()) works for\naccounts authenticated via any authentication plugin that supports passwords\nstored in the mysql.global_priv table.\n\nThe ed25519, mysql_native_password, and mysql_old_password authentication\nplugins store passwords in the mysql.global_priv table.\n\nIf you run SET PASSWORD on an account that authenticates with one of these\nauthentication plugins that stores passwords in the mysql.global_priv table,\nthen the PASSWORD() function is evaluated by the specific authentication\nplugin used by the account. The authentication plugin hashes the password with\na method that is compatible with that specific authentication plugin.\n\nThe unix_socket, named_pipe, gssapi, and pam authentication plugins do not\nstore passwords in the mysql.global_priv table. These authentication plugins\nrely on other methods to authenticate the user.\n\nIf you attempt to run SET PASSWORD on an account that authenticates with one\nof these authentication plugins that doesn\'t store a password in the\nmysql.global_priv table, then MariaDB Server will raise a warning like the\nfollowing:\n\nSET PASSWORD is ignored for users authenticating via unix_socket plugin\n\nSee Authentication from MariaDB 10.4 for an overview of authentication changes\nin MariaDB 10.4.\n\nMariaDB until 10.3\n------------------\nIn MariaDB 10.3 and before, SET PASSWORD (with or without PASSWORD()) only\nworks for accounts authenticated via mysql_native_password or\nmysql_old_password authentication plugins\n\nPasswordless User Accounts\n--------------------------\n\nUser accounts do not always require passwords to login.\n\nThe unix_socket , named_pipe and gssapi authentication plugins do not require\na password to authenticate the user.\n\nThe pam authentication plugin may or may not require a password to\nauthenticate the user, depending on the specific configuration.\n\nThe mysql_native_password and mysql_old_password authentication plugins\nrequire passwords for authentication, but the password can be blank. In that\ncase, no password is required.\n\nIf you provide a password while attempting to log into the server as an\naccount that doesn\'t require a password, then MariaDB server will simply\nignore the password.\n\nMariaDB starting with 10.4\n--------------------------\nIn MariaDB 10.4 and later, a user account can be defined to use multiple\nauthentication plugins in a specific order of preference. This specific\nscenario may be more noticeable in these versions, since an account could be\nassociated with some authentication plugins that require a password, and some\nthat do not.\n\nExample\n-------\n\nFor example, if you had an entry with User and Host column values of \'bob\' and\n\'%.loc.gov\', you would write the statement like this:\n\nSET PASSWORD FOR \'bob\'@\'%.loc.gov\' = PASSWORD(\'newpass\');\n\nIf you want to delete a password for a user, you would do:\n\nSET PASSWORD FOR \'bob\'@localhost = PASSWORD(\"\");\n\nURL: https://mariadb.com/kb/en/set-password/','','https://mariadb.com/kb/en/set-password/'); @@ -332,10 +332,10 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (240,20,'~','Syntax\n------\n\n~\n\nDescription\n-----------\n\nBitwise NOT. Converts the value to 4 bytes binary and inverts all bits.\n\nExamples\n--------\n\nSELECT 3 & ~1;\n+--------+\n| 3 & ~1 |\n+--------+\n| 2 |\n+--------+\n\nSELECT 5 & ~1;\n+--------+\n| 5 & ~1 |\n+--------+\n| 4 |\n+--------+\n\nURL: https://mariadb.com/kb/en/bitwise-not/','','https://mariadb.com/kb/en/bitwise-not/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (241,20,'Parentheses','Parentheses are sometimes called precedence operators - this means that they\ncan be used to change the other operator\'s precedence in an expression. The\nexpressions that are written between parentheses are computed before the\nexpressions that are written outside. Parentheses must always contain an\nexpression (that is, they cannot be empty), and can be nested.\n\nFor example, the following expressions could return different results:\n\n* NOT a OR b\n* NOT (a OR b)\n\nIn the first case, NOT applies to a, so if a is FALSE or b is TRUE, the\nexpression returns TRUE. In the second case, NOT applies to the result of a OR\nb, so if at least one of a or b is TRUE, the expression is TRUE.\n\nWhen the precedence of operators is not intuitive, you can use parentheses to\nmake it immediately clear for whoever reads the statement.\n\nThe precedence of the NOT operator can also be affected by the\nHIGH_NOT_PRECEDENCE SQL_MODE flag.\n\nOther uses\n----------\n\nParentheses must always be used to enclose subqueries.\n\nParentheses can also be used in a JOIN statement between multiple tables to\ndetermine which tables must be joined first.\n\nAlso, parentheses are used to enclose the list of parameters to be passed to\nbuilt-in functions, user-defined functions and stored routines. However, when\nno parameter is passed to a stored procedure, parentheses are optional. For\nbuiltin functions and user-defined functions, spaces are not allowed between\nthe function name and the open parenthesis, unless the IGNORE_SPACE SQL_MODE\nis set. For stored routines (and for functions if IGNORE_SPACE is set) spaces\nare allowed before the open parenthesis, including tab characters and new line\ncharacters.\n\nSyntax errors\n-------------\n\nIf there are more open parentheses than closed parentheses, the error usually\nlooks like this:\n\nERROR 1064 (42000): You have an error in your SQL syntax; check the manual that\ncorresponds to your MariaDB server version for the right syntax to use near \'\'\na\nt line 1\n\nNote the empty string.\n\nIf there are more closed parentheses than open parentheses, the error usually\nlooks like this:\n\nERROR 1064 (42000): You have an error in your SQL syntax; check the manual that\ncorresponds to your MariaDB server version for the right syntax to use near \')\'\nat line 1\n\nNote the quoted closed parenthesis.\n\nURL: https://mariadb.com/kb/en/parentheses/','','https://mariadb.com/kb/en/parentheses/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (242,20,'TRUE FALSE','Description\n-----------\n\nThe constants TRUE and FALSE evaluate to 1 and 0, respectively. The constant\nnames can be written in any lettercase.\n\nExamples\n--------\n\nSELECT TRUE, true, FALSE, false;\n+------+------+-------+-------+\n| TRUE | TRUE | FALSE | FALSE |\n+------+------+-------+-------+\n| 1 | 1 | 0 | 0 |\n+------+------+-------+-------+\n\nURL: https://mariadb.com/kb/en/true-false/','','https://mariadb.com/kb/en/true-false/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (243,21,'ANALYZE TABLE','Syntax\n------\n\nANALYZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name [,tbl_name ...] \n [PERSISTENT FOR [ALL|COLUMNS ([col_name [,col_name ...]])]\n [INDEXES ([index_name [,index_name ...]])]]\n\nDescription\n-----------\n\nANALYZE TABLE analyzes and stores the key distribution for a table (index\nstatistics). This statement works with MyISAM, Aria and InnoDB tables. During\nthe analysis, InnoDB will allow reads/writes, and MyISAM/Aria reads/inserts.\nFor MyISAM tables, this statement is equivalent to using myisamchk --analyze.\n\nFor more information on how the analysis works within InnoDB, see InnoDB\nLimitations.\n\nMariaDB uses the stored key distribution to decide the order in which tables\nshould be joined when you perform a join on something other than a constant.\nIn addition, key distributions can be used when deciding which indexes to use\nfor a specific table within a query.\n\nThis statement requires SELECT and INSERT privileges for the table.\n\nBy default, ANALYZE TABLE statements are written to the binary log and will be\nreplicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure the\nstatement is not written to the binary log.\n\nFrom MariaDB 10.3.19, ANALYZE TABLE statements are not logged to the binary\nlog if read_only is set. See also Read-Only Replicas.\n\nANALYZE TABLE is also supported for partitioned tables. You can use ALTER\nTABLE ... ANALYZE PARTITION to analyze one or more partitions.\n\nThe Aria storage engine supports progress reporting for the ANALYZE TABLE\nstatement.\n\nEngine-Independent Statistics\n-----------------------------\n\nANALYZE TABLE supports engine-independent statistics. See Engine-Independent\nTable Statistics: Collecting Statistics with the ANALYZE TABLE Statement for\nmore information.\n\nURL: https://mariadb.com/kb/en/analyze-table/','','https://mariadb.com/kb/en/analyze-table/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (244,21,'CHECK TABLE','Syntax\n------\n\nCHECK TABLE tbl_name [, tbl_name] ... [option] ...\n\noption = {FOR UPGRADE | QUICK | FAST | MEDIUM | EXTENDED | CHANGED}\n\nDescription\n-----------\n\nCHECK TABLE checks a table or tables for errors. CHECK TABLE works for\nArchive, Aria, CSV, InnoDB, and MyISAM tables. For Aria and MyISAM tables, the\nkey statistics are updated as well. For CSV, see also Checking and Repairing\nCSV Tables.\n\nAs an alternative, myisamchk is a commandline tool for checking MyISAM tables\nwhen the tables are not being accessed.\n\nFor checking dynamic columns integrity, COLUMN_CHECK() can be used.\n\nCHECK TABLE can also check views for problems, such as tables that are\nreferenced in the view definition that no longer exist.\n\nCHECK TABLE is also supported for partitioned tables. You can use ALTER TABLE\n... CHECK PARTITION to check one or more partitions.\n\nThe meaning of the different options are as follows - note that this can vary\na bit between storage engines:\n\n+-----+----------------------------------------------------------------------+\n| FOR | Do a very quick check if the storage format for the table has |\n| UPG | changed so that one needs to do a REPAIR. This is only needed when |\n| ADE | one upgrades between major versions of MariaDB or MySQL. This is |\n| | usually done by running mysql_upgrade. |\n+-----+----------------------------------------------------------------------+\n| FAS | Only check tables that has not been closed properly or are marked |\n| | as corrupt. Only supported by the MyISAM and Aria engines. For |\n| | other engines the table is checked normally |\n+-----+----------------------------------------------------------------------+\n| CHA | Check only tables that has changed since last REPAIR / CHECK. Only |\n| GED | supported by the MyISAM and Aria engines. For other engines the |\n| | table is checked normally. |\n+-----+----------------------------------------------------------------------+\n| QUI | Do a fast check. For MyISAM and Aria engine this means we skip |\n| K | checking the delete link chain which may take some time. |\n+-----+----------------------------------------------------------------------+\n| MED | Scan also the data files. Checks integrity between data and index |\n| UM | files with checksums. In most cases this should find all possible |\n| | errors. |\n+-----+----------------------------------------------------------------------+\n| EXT | Does a full check to verify every possible error. For MyISAM and |\n| NDE | Aria we verify for each row that all it keys exists and points to |\n| | the row. This may take a long time on big tables! |\n+-----+----------------------------------------------------------------------+\n\nFor most cases running CHECK TABLE without options or MEDIUM should be good\nenough.\n\nThe Aria storage engine supports progress reporting for this statement.\n\nIf you want to know if two tables are identical, take a look at CHECKSUM TABLE.\n\nInnoDB\n------\n\nIf CHECK TABLE finds an error in an InnoDB table, MariaDB might shutdown to\nprevent the error propagation. In this case, the problem will be reported in\nthe error log. Otherwise the table or an index might be marked as corrupted,\nto prevent use. This does not happen with some minor problems, like a wrong\nnumber of entries in a secondary index. Those problems are reported in the\noutput of CHECK TABLE.\n\nEach tablespace contains a header with metadata. This header is not checked by\nthis statement.\n\nDuring the execution of CHECK TABLE, other threads may be blocked.\n\nURL: https://mariadb.com/kb/en/check-table/','','https://mariadb.com/kb/en/check-table/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (243,21,'ANALYZE TABLE','Syntax\n------\n\nANALYZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name [,tbl_name ...] \n [PERSISTENT FOR [ALL|COLUMNS ([col_name [,col_name ...]])]\n [INDEXES ([index_name [,index_name ...]])]]\n\nDescription\n-----------\n\nANALYZE TABLE analyzes and stores the key distribution for a table (index\nstatistics). This statement works with MyISAM, Aria and InnoDB tables. During\nthe analysis, InnoDB will allow reads/writes, and MyISAM/Aria reads/inserts.\nFor MyISAM tables, this statement is equivalent to using myisamchk --analyze.\n\nFor more information on how the analysis works within InnoDB, see InnoDB\nLimitations.\n\nMariaDB uses the stored key distribution to decide the order in which tables\nshould be joined when you perform a join on something other than a constant.\nIn addition, key distributions can be used when deciding which indexes to use\nfor a specific table within a query.\n\nThis statement requires SELECT and INSERT privileges for the table.\n\nBy default, ANALYZE TABLE statements are written to the binary log and will be\nreplicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure the\nstatement is not written to the binary log.\n\nFrom MariaDB 10.3.19, ANALYZE TABLE statements are not logged to the binary\nlog if read_only is set. See also Read-Only Replicas.\n\nANALYZE TABLE is also supported for partitioned tables. You can use ALTER\nTABLE ... ANALYZE PARTITION to analyze one or more partitions.\n\nThe Aria storage engine supports progress reporting for the ANALYZE TABLE\nstatement.\n\nEngine-Independent Statistics\n-----------------------------\n\nANALYZE TABLE supports engine-independent statistics. See Engine-Independent\nTable Statistics: Collecting Statistics with the ANALYZE TABLE Statement for\nmore information.\n\nUseful Variables\n----------------\n\nFor calculating the number of duplicates, ANALYZE TABLE uses a buffer of\nsort_buffer_size bytes per column. You can slightly increase the speed of\nANALYZE TABLE by increasing this variable.\n\nURL: https://mariadb.com/kb/en/analyze-table/','','https://mariadb.com/kb/en/analyze-table/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (244,21,'CHECK TABLE','Syntax\n------\n\nCHECK TABLE tbl_name [, tbl_name] ... [option] ...\n\noption = {FOR UPGRADE | QUICK | FAST | MEDIUM | EXTENDED | CHANGED}\n\nDescription\n-----------\n\nCHECK TABLE checks a table or tables for errors. CHECK TABLE works for\nArchive, Aria, CSV, InnoDB and MyISAM tables. For Aria and MyISAM tables, the\nkey statistics are updated as well. For CSV, see also Checking and Repairing\nCSV Tables.\n\nAs an alternative, myisamchk is a commandline tool for checking MyISAM tables\nwhen the tables are not being accessed. For Aria tables, there is a similar\ntool: aria_chk.\n\nFor checking dynamic columns integrity, COLUMN_CHECK() can be used.\n\nCHECK TABLE can also check views for problems, such as tables that are\nreferenced in the view definition that no longer exist.\n\nCHECK TABLE is also supported for partitioned tables. You can use ALTER TABLE\n... CHECK PARTITION to check one or more partitions.\n\nThe meaning of the different options are as follows - note that this can vary\na bit between storage engines:\n\n+-----+----------------------------------------------------------------------+\n| FOR | Do a very quick check if the storage format for the table has |\n| UPG | changed so that one needs to do a REPAIR. This is only needed when |\n| ADE | one upgrades between major versions of MariaDB or MySQL. This is |\n| | usually done by running mysql_upgrade. |\n+-----+----------------------------------------------------------------------+\n| FAS | Only check tables that has not been closed properly or are marked |\n| | as corrupt. Only supported by the MyISAM and Aria engines. For |\n| | other engines the table is checked normally |\n+-----+----------------------------------------------------------------------+\n| CHA | Check only tables that has changed since last REPAIR / CHECK. Only |\n| GED | supported by the MyISAM and Aria engines. For other engines the |\n| | table is checked normally. |\n+-----+----------------------------------------------------------------------+\n| QUI | Do a fast check. For MyISAM and Aria, this means skipping the check |\n| K | of the delete link chain, which may take some time. |\n+-----+----------------------------------------------------------------------+\n| MED | Scan also the data files. Checks integrity between data and index |\n| UM | files with checksums. In most cases this should find all possible |\n| | errors. |\n+-----+----------------------------------------------------------------------+\n| EXT | Does a full check to verify every possible error. For MyISAM and |\n| NDE | Aria, verify for each row that all it keys exists and points to the |\n| | row. This may take a long time on large tables. Ignored by InnoDB |\n| | before MariaDB 10.6.11, MariaDB 10.7.7, MariaDB 10.8.6 and MariaDB |\n| | 10.9.4. |\n+-----+----------------------------------------------------------------------+\n\nFor most cases running CHECK TABLE without options or MEDIUM should be good\nenough.\n\nThe Aria storage engine supports progress reporting for this statement.\n\nIf you want to know if two tables are identical, take a look at CHECKSUM TABLE.\n\nInnoDB\n------\n\nIf CHECK TABLE finds an error in an InnoDB table, MariaDB might shutdown to\nprevent the error propagation. In this case, the problem will be reported in\nthe error log. Otherwise the table or an index might be marked as corrupted,\nto prevent use. This does not happen with some minor problems, like a wrong\nnumber of entries in a secondary index. Those problems are reported in the\noutput of CHECK TABLE.\n\nEach tablespace contains a header with metadata. This header is not checked by\nthis statement.\n\nDuring the execution of CHECK TABLE, other threads may be blocked.\n\nURL: https://mariadb.com/kb/en/check-table/','','https://mariadb.com/kb/en/check-table/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (245,21,'CHECK VIEW','Syntax\n------\n\nCHECK VIEW view_name\n\nDescription\n-----------\n\nThe CHECK VIEW statement was introduced in MariaDB 10.0.18 to assist with\nfixing MDEV-6916, an issue introduced in MariaDB 5.2 where the view algorithms\nwere swapped. It checks whether the view algorithm is correct. It is run as\npart of mysql_upgrade, and should not normally be required in regular use.\n\nURL: https://mariadb.com/kb/en/check-view/','','https://mariadb.com/kb/en/check-view/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (246,21,'CHECKSUM TABLE','Syntax\n------\n\nCHECKSUM TABLE tbl_name [, tbl_name] ... [ QUICK | EXTENDED ]\n\nDescription\n-----------\n\nCHECKSUM TABLE reports a table checksum. This is very useful if you want to\nknow if two tables are the same (for example on a master and slave).\n\nWith QUICK, the live table checksum is reported if it is available, or NULL\notherwise. This is very fast. A live checksum is enabled by specifying the\nCHECKSUM=1 table option when you create the table; currently, this is\nsupported only for Aria and MyISAM tables.\n\nWith EXTENDED, the entire table is read row by row and the checksum is\ncalculated. This can be very slow for large tables.\n\nIf neither QUICK nor EXTENDED is specified, MariaDB returns a live checksum if\nthe table storage engine supports it and scans the table otherwise.\n\nCHECKSUM TABLE requires the SELECT privilege for the table.\n\nFor a nonexistent table, CHECKSUM TABLE returns NULL and generates a warning.\n\nThe table row format affects the checksum value. If the row format changes,\nthe checksum will change. This means that when a table created with a\nMariaDB/MySQL version is upgraded to another version, the checksum value will\nprobably change.\n\nTwo identical tables should always match to the same checksum value; however,\nalso for non-identical tables there is a very slight chance that they will\nreturn the same value as the hashing algorithm is not completely\ncollision-free.\n\nDifferences Between MariaDB and MySQL\n-------------------------------------\n\nCHECKSUM TABLE may give a different result as MariaDB doesn\'t ignore NULLs in\nthe columns as MySQL 5.1 does (Later MySQL versions should calculate checksums\nthe same way as MariaDB). You can get the \'old style\' checksum in MariaDB by\nstarting mysqld with the --old option. Note however that that the MyISAM and\nAria storage engines in MariaDB are using the new checksum internally, so if\nyou are using --old, the CHECKSUM command will be slower as it needs to\ncalculate the checksum row by row. Starting from MariaDB Server 10.9, --old is\ndeprecated and will be removed in a future release. Set --old-mode or OLD_MODE\nto COMPAT_5_1_CHECKSUM to get \'old style\' checksum.\n\nURL: https://mariadb.com/kb/en/checksum-table/','','https://mariadb.com/kb/en/checksum-table/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (246,21,'CHECKSUM TABLE','Syntax\n------\n\nCHECKSUM TABLE tbl_name [, tbl_name] ... [ QUICK | EXTENDED ]\n\nDescription\n-----------\n\nCHECKSUM TABLE reports a table checksum. This is very useful if you want to\nknow if two tables are the same (for example on a master and slave).\n\nWith QUICK, the live table checksum is reported if it is available, or NULL\notherwise. This is very fast. A live checksum is enabled by specifying the\nCHECKSUM=1 table option when you create the table; currently, this is\nsupported only for Aria and MyISAM tables.\n\nWith EXTENDED, the entire table is read row by row and the checksum is\ncalculated. This can be very slow for large tables.\n\nIf neither QUICK nor EXTENDED is specified, MariaDB returns a live checksum if\nthe table storage engine supports it and scans the table otherwise.\n\nCHECKSUM TABLE requires the SELECT privilege for the table.\n\nFor a nonexistent table, CHECKSUM TABLE returns NULL and generates a warning.\n\nThe table row format affects the checksum value. If the row format changes,\nthe checksum will change. This means that when a table created with a\nMariaDB/MySQL version is upgraded to another version, the checksum value will\nprobably change.\n\nTwo identical tables should always match to the same checksum value; however,\nalso for non-identical tables there is a very slight chance that they will\nreturn the same value as the hashing algorithm is not completely\ncollision-free.\n\nIdentical Tables\n----------------\n\nIdentical tables mean that the CREATE statement is identical and that the\nfollowing variable, which affects the storage formats, was the same when the\ntables were created:\n\n* mysql56-temporal-format\n\nDifferences Between MariaDB and MySQL\n-------------------------------------\n\nCHECKSUM TABLE may give a different result as MariaDB doesn\'t ignore NULLs in\nthe columns as MySQL 5.1 does (Later MySQL versions should calculate checksums\nthe same way as MariaDB). You can get the \'old style\' checksum in MariaDB by\nstarting mysqld with the --old option. Note however that that the MyISAM and\nAria storage engines in MariaDB are using the new checksum internally, so if\nyou are using --old, the CHECKSUM command will be slower as it needs to\ncalculate the checksum row by row. Starting from MariaDB Server 10.9, --old is\ndeprecated and will be removed in a future release. Set --old-mode or OLD_MODE\nto COMPAT_5_1_CHECKSUM to get \'old style\' checksum.\n\nURL: https://mariadb.com/kb/en/checksum-table/','','https://mariadb.com/kb/en/checksum-table/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (247,21,'OPTIMIZE TABLE','Syntax\n------\n\nOPTIMIZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE\n tbl_name [, tbl_name] ...\n [WAIT n | NOWAIT]\n\nDescription\n-----------\n\nOPTIMIZE TABLE has two main functions. It can either be used to defragment\ntables, or to update the InnoDB fulltext index.\n\nMariaDB starting with 10.3.0\n----------------------------\n\nWAIT/NOWAIT\n-----------\n\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nDefragmenting\n-------------\n\nOPTIMIZE TABLE works for InnoDB (before MariaDB 10.1.1, only if the\ninnodb_file_per_table server system variable is set), Aria, MyISAM and ARCHIVE\ntables, and should be used if you have deleted a large part of a table or if\nyou have made many changes to a table with variable-length rows (tables that\nhave VARCHAR, VARBINARY, BLOB, or TEXT columns). Deleted rows are maintained\nin a linked list and subsequent INSERT operations reuse old row positions.\n\nThis statement requires SELECT and INSERT privileges for the table.\n\nBy default, OPTIMIZE TABLE statements are written to the binary log and will\nbe replicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure\nthe statement is not written to the binary log.\n\nFrom MariaDB 10.3.19, OPTIMIZE TABLE statements are not logged to the binary\nlog if read_only is set. See also Read-Only Replicas.\n\nOPTIMIZE TABLE is also supported for partitioned tables. You can use ALTER\nTABLE ... OPTIMIZE PARTITION to optimize one or more partitions.\n\nYou can use OPTIMIZE TABLE to reclaim the unused space and to defragment the\ndata file. With other storage engines, OPTIMIZE TABLE does nothing by default,\nand returns this message: \" The storage engine for the table doesn\'t support\noptimize\". However, if the server has been started with the --skip-new option,\nOPTIMIZE TABLE is linked to ALTER TABLE, and recreates the table. This\noperation frees the unused space and updates index statistics.\n\nThe Aria storage engine supports progress reporting for this statement.\n\nIf a MyISAM table is fragmented, concurrent inserts will not be performed\nuntil an OPTIMIZE TABLE statement is executed on that table, unless the\nconcurrent_insert server system variable is set to ALWAYS.\n\nUpdating an InnoDB fulltext index\n---------------------------------\n\nWhen rows are added or deleted to an InnoDB fulltext index, the index is not\nimmediately re-organized, as this can be an expensive operation. Change\nstatistics are stored in a separate location . The fulltext index is only\nfully re-organized when an OPTIMIZE TABLE statement is run.\n\nBy default, an OPTIMIZE TABLE will defragment a table. In order to use it to\nupdate fulltext index statistics, the innodb_optimize_fulltext_only system\nvariable must be set to 1. This is intended to be a temporary setting, and\nshould be reset to 0 once the fulltext index has been re-organized.\n\nSince fulltext re-organization can take a long time, the\ninnodb_ft_num_word_optimize variable limits the re-organization to a number of\nwords (2000 by default). You can run multiple OPTIMIZE statements to fully\nre-organize the index.\n\nDefragmenting InnoDB tablespaces\n--------------------------------\n\nMariaDB 10.1.1 merged the Facebook/Kakao defragmentation patch, allowing one\nto use OPTIMIZE TABLE to defragment InnoDB tablespaces. For this functionality\nto be enabled, the innodb_defragment system variable must be enabled. No new\ntables are created and there is no need to copy data from old tables to new\ntables. Instead, this feature loads n pages (determined by\ninnodb-defragment-n-pages) and tries to move records so that pages would be\nfull of records and then frees pages that are fully empty after the operation.\nNote that tablespace files (including ibdata1) will not shrink as the result\nof defragmentation, but one will get better memory utilization in the InnoDB\nbuffer pool as there are fewer data pages in use.\n\nSee Defragmenting InnoDB Tablespaces for more details.\n\nURL: https://mariadb.com/kb/en/optimize-table/','','https://mariadb.com/kb/en/optimize-table/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (248,21,'REPAIR TABLE','Syntax\n------\n\nREPAIR [NO_WRITE_TO_BINLOG | LOCAL] TABLE\n tbl_name [, tbl_name] ...\n [QUICK] [EXTENDED] [USE_FRM]\n\nDescription\n-----------\n\nREPAIR TABLE repairs a possibly corrupted table. By default, it has the same\neffect as\n\nmyisamchk --recover tbl_name\n\nor\n\naria_chk --recover tbl_name\n\nSee aria_chk and myisamchk for more.\n\nREPAIR TABLE works for Archive, Aria, CSV and MyISAM tables. For InnoDB, see\nrecovery modes. For CSV, see also Checking and Repairing CSV Tables. For\nArchive, this statement also improves compression. If the storage engine does\nnot support this statement, a warning is issued.\n\nThis statement requires SELECT and INSERT privileges for the table.\n\nBy default, REPAIR TABLE statements are written to the binary log and will be\nreplicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure the\nstatement is not written to the binary log.\n\nFrom MariaDB 10.3.19, REPAIR TABLE statements are not logged to the binary log\nif read_only is set. See also Read-Only Replicas.\n\nWhen an index is recreated, the storage engine may use a configurable buffer\nin the process. Incrementing the buffer speeds up the index creation. Aria and\nMyISAM allocate a buffer whose size is defined by aria_sort_buffer_size or\nmyisam_sort_buffer_size, also used for ALTER TABLE.\n\nREPAIR TABLE is also supported for partitioned tables. However, the USE_FRM\noption cannot be used with this statement on a partitioned table.\n\nALTER TABLE ... REPAIR PARTITION can be used to repair one or more partitions.\n\nThe Aria storage engine supports progress reporting for this statement.\n\nURL: https://mariadb.com/kb/en/repair-table/','','https://mariadb.com/kb/en/repair-table/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (249,21,'REPAIR VIEW','Syntax\n------\n\nREPAIR [NO_WRITE_TO_BINLOG | LOCAL] VIEW view_name[, view_name] ... [FROM\nMYSQL]\n\nDescription\n-----------\n\nThe REPAIR VIEW statement was introduced to assist with fixing MDEV-6916, an\nissue introduced in MariaDB 5.2 where the view algorithms were swapped\ncompared to their MySQL on disk representation. It checks whether the view\nalgorithm is correct. It is run as part of mysql_upgrade, and should not\nnormally be required in regular use.\n\nBy default it corrects the checksum and if necessary adds the mariadb-version\nfield. If the optional FROM MYSQL clause is used, and no mariadb-version field\nis present, the MERGE and TEMPTABLE algorithms are toggled.\n\nBy default, REPAIR VIEW statements are written to the binary log and will be\nreplicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure the\nstatement is not written to the binary log.\n\nURL: https://mariadb.com/kb/en/repair-view/','','https://mariadb.com/kb/en/repair-view/'); @@ -400,7 +400,7 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (307,24,'REPEAT LOOP','Syntax\n------\n\n[begin_label:] REPEAT\n statement_list\nUNTIL search_condition\nEND REPEAT [end_label]\n\nThe statement list within a REPEAT statement is repeated until the\nsearch_condition is true. Thus, a REPEAT always enters the loop at least once.\nstatement_list consists of one or more statements, each terminated by a\nsemicolon (i.e., ;) statement delimiter.\n\nA REPEAT statement can be labeled. end_label cannot be given unless\nbegin_label also is present. If both are present, they must be the same.\n\nSee Delimiters in the mysql client for more on client delimiter usage.\n\nDELIMITER //\n\nCREATE PROCEDURE dorepeat(p1 INT)\n BEGIN\n SET @x = 0;\n REPEAT SET @x = @x + 1; UNTIL @x > p1 END REPEAT;\n END\n//\n\nCALL dorepeat(1000)//\n\nSELECT @x//\n+------+\n| @x |\n+------+\n| 1001 |\n+------+\n\nURL: https://mariadb.com/kb/en/repeat-loop/','','https://mariadb.com/kb/en/repeat-loop/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (308,24,'RESIGNAL','Syntax\n------\n\nRESIGNAL [error_condition]\n [SET error_property\n [, error_property] ...]\n\nerror_condition:\n SQLSTATE [VALUE] \'sqlstate_value\'\n | condition_name\n\nerror_property:\n error_property_name = \n\nerror_property_name:\n CLASS_ORIGIN\n | SUBCLASS_ORIGIN\n | MESSAGE_TEXT\n | MYSQL_ERRNO\n | CONSTRAINT_CATALOG\n | CONSTRAINT_SCHEMA\n | CONSTRAINT_NAME\n | CATALOG_NAME\n | SCHEMA_NAME\n | TABLE_NAME\n | COLUMN_NAME\n | CURSOR_NAME\n\nDescription\n-----------\n\nThe syntax of RESIGNAL and its semantics are very similar to SIGNAL. This\nstatement can only be used within an error HANDLER. It produces an error, like\nSIGNAL. RESIGNAL clauses are the same as SIGNAL, except that they all are\noptional, even SQLSTATE. All the properties which are not specified in\nRESIGNAL, will be identical to the properties of the error that was received\nby the error HANDLER. For a description of the clauses, see diagnostics area.\n\nNote that RESIGNAL does not empty the diagnostics area: it just appends\nanother error condition.\n\nRESIGNAL, without any clauses, produces an error which is identical to the\nerror that was received by HANDLER.\n\nIf used out of a HANDLER construct, RESIGNAL produces the following error:\n\nERROR 1645 (0K000): RESIGNAL when handler not active\n\nIn MariaDB 5.5, if a HANDLER contained a CALL to another procedure, that\nprocedure could use RESIGNAL. Since MariaDB 10.0, trying to do this raises the\nabove error.\n\nFor a list of SQLSTATE values and MariaDB error codes, see MariaDB Error Codes.\n\nThe following procedure tries to query two tables which don\'t exist, producing\na 1146 error in both cases. Those errors will trigger the HANDLER. The first\ntime the error will be ignored and the client will not receive it, but the\nsecond time, the error is re-signaled, so the client will receive it.\n\nCREATE PROCEDURE test_error( )\nBEGIN\n DECLARE CONTINUE HANDLER\n FOR 1146\n BEGIN\n IF @hide_errors IS FALSE THEN\n RESIGNAL;\n END IF;\n END;\n SET @hide_errors = TRUE;\n SELECT \'Next error will be ignored\' AS msg;\n SELECT `c` FROM `temptab_one`;\n SELECT \'Next error won\'\'t be ignored\' AS msg;\n SET @hide_errors = FALSE;\n SELECT `c` FROM `temptab_two`;\nEND;\n\nCALL test_error( );\n\n+----------------------------+\n| msg |\n+----------------------------+\n| Next error will be ignored |\n+----------------------------+\n\n+-----------------------------+\n| msg |\n+-----------------------------+\n| Next error won\'t be ignored |\n+-----------------------------+\n\nERROR 1146 (42S02): Table \'test.temptab_two\' doesn\'t exist\n\nThe following procedure re-signals an error, modifying only the error message\nto clarify the cause of the problem.\n\nCREATE PROCEDURE test_error()\nBEGIN\n DECLARE CONTINUE HANDLER\n FOR 1146\n BEGIN\n RESIGNAL SET\n MESSAGE_TEXT = \'`temptab` does not exist\';\n END;\n SELECT `c` FROM `temptab`;\nEND;\n\nCALL test_error( );\nERROR 1146 (42S02): `temptab` does not exist\n\nAs explained above, this works on MariaDB 5.5, but produces a 1645 error since\n10.0.\n\nCREATE PROCEDURE handle_error()\nBEGIN\n RESIGNAL;\nEND;\nCREATE PROCEDURE p()\nBEGIN\n DECLARE EXIT HANDLER FOR SQLEXCEPTION CALL p();\n SIGNAL SQLSTATE \'45000\';\nEND;\n\nURL: https://mariadb.com/kb/en/resignal/','','https://mariadb.com/kb/en/resignal/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (309,24,'RETURN','Syntax\n------\n\nRETURN expr\n\nThe RETURN statement terminates execution of a stored function and returns the\nvalue expr to the function caller. There must be at least one RETURN statement\nin a stored function. If the function has multiple exit points, all exit\npoints must have a RETURN.\n\nThis statement is not used in stored procedures, triggers, or events. LEAVE\ncan be used instead.\n\nThe following example shows that RETURN can return the result of a scalar\nsubquery:\n\nCREATE FUNCTION users_count() RETURNS BOOL\n READS SQL DATA\nBEGIN\n RETURN (SELECT COUNT(DISTINCT User) FROM mysql.user);\nEND;\n\nURL: https://mariadb.com/kb/en/return/','','https://mariadb.com/kb/en/return/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (310,24,'SELECT INTO','Syntax\n------\n\nSELECT col_name [, col_name] ...\n INTO var_name [, var_name] ...\n table_expr\n\nDescription\n-----------\n\nSELECT ... INTO enables selected columns to be stored directly into variables.\nNo resultset is produced. The query should return a single row. If the query\nreturns no rows, a warning with error code 1329 occurs (No data), and the\nvariable values remain unchanged. If the query returns multiple rows, error\n1172 occurs (Result consisted of more than one row). If it is possible that\nthe statement may retrieve multiple rows, you can use LIMIT 1 to limit the\nresult set to a single row.\n\nThe INTO clause can also be specified at the end of the statement.\n\nIn the context of such statements that occur as part of events executed by the\nEvent Scheduler, diagnostics messages (not only errors, but also warnings) are\nwritten to the error log, and, on Windows, to the application event log.\n\nThis statement can be used with both local variables and user-defined\nvariables.\n\nFor the complete syntax, see SELECT.\n\nAnother way to set a variable\'s value is the SET statement.\n\nSELECT ... INTO results are not stored in the query cache even if SQL_CACHE is\nspecified.\n\nExamples\n--------\n\nSELECT id, data INTO @x,@y \nFROM test.t1 LIMIT 1;\n\nURL: https://mariadb.com/kb/en/selectinto/','','https://mariadb.com/kb/en/selectinto/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (310,24,'SELECT INTO','Syntax\n------\n\nSELECT col_name [, col_name] ...\n INTO var_name [, var_name] ...\n table_expr\n\nDescription\n-----------\n\nSELECT ... INTO enables selected columns to be stored directly into variables.\nNo resultset is produced. The query should return a single row. If the query\nreturns no rows, a warning with error code 1329 occurs (No data), and the\nvariable values remain unchanged. If the query returns multiple rows, error\n1172 occurs (Result consisted of more than one row). If it is possible that\nthe statement may retrieve multiple rows, you can use LIMIT 1 to limit the\nresult set to a single row.\n\nThe INTO clause can also be specified at the end of the statement.\n\nIn the context of such statements that occur as part of events executed by the\nEvent Scheduler, diagnostics messages (not only errors, but also warnings) are\nwritten to the error log, and, on Windows, to the application event log.\n\nThis statement can be used with both local variables and user-defined\nvariables.\n\nFor the complete syntax, see SELECT.\n\nAnother way to set a variable\'s value is the SET statement.\n\nSELECT ... INTO results are not stored in the query cache even if SQL_CACHE is\nspecified.\n\nExamples\n--------\n\nSELECT id, data INTO @x,@y \nFROM test.t1 LIMIT 1;\nSELECT * from t1 where t1.a=@x and t1.b=@y\n\nIf you want to use this construct with UNION you have to use the syntax:\n\nSELECT * INTO @x FROM (SELECT t1.a FROM t1 UNION SELECT t2.a FROM t2);\n\nURL: https://mariadb.com/kb/en/selectinto/','','https://mariadb.com/kb/en/selectinto/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (311,24,'SIGNAL','Syntax\n------\n\nSIGNAL error_condition\n [SET error_property\n [, error_property] ...]\n\nerror_condition:\n SQLSTATE [VALUE] \'sqlstate_value\'\n | condition_name\n\nerror_property:\n error_property_name = \n\nerror_property_name:\n CLASS_ORIGIN\n | SUBCLASS_ORIGIN\n | MESSAGE_TEXT\n | MYSQL_ERRNO\n | CONSTRAINT_CATALOG\n | CONSTRAINT_SCHEMA\n | CONSTRAINT_NAME\n | CATALOG_NAME\n | SCHEMA_NAME\n | TABLE_NAME\n | COLUMN_NAME\n | CURSOR_NAME\n\nSIGNAL empties the diagnostics area and produces a custom error. This\nstatement can be used anywhere, but is generally useful when used inside a\nstored program. When the error is produced, it can be caught by a HANDLER. If\nnot, the current stored program, or the current statement, will terminate with\nthe specified error.\n\nSometimes an error HANDLER just needs to SIGNAL the same error it received,\noptionally with some changes. Usually the RESIGNAL statement is the most\nconvenient way to do this.\n\nerror_condition can be an SQLSTATE value or a named error condition defined\nvia DECLARE CONDITION. SQLSTATE must be a constant string consisting of five\ncharacters. These codes are standard to ODBC and ANSI SQL. For customized\nerrors, the recommended SQLSTATE is \'45000\'. For a list of SQLSTATE values\nused by MariaDB, see the MariaDB Error Codes page. The SQLSTATE can be read\nvia the API method mysql_sqlstate( ).\n\nTo specify error properties user-defined variables and local variables can be\nused, as well as character set conversions (but you can\'t set a collation).\n\nThe error properties, their type and their default values are explained in the\ndiagnostics area page.\n\nErrors\n------\n\nIf the SQLSTATE is not valid, the following error like this will be produced:\n\nERROR 1407 (42000): Bad SQLSTATE: \'123456\'\n\nIf a property is specified more than once, an error like this will be produced:\n\nERROR 1641 (42000): Duplicate condition information item \'MESSAGE_TEXT\'\n\nIf you specify a condition name which is not declared, an error like this will\nbe produced:\n\nERROR 1319 (42000): Undefined CONDITION: cond_name\n\nIf MYSQL_ERRNO is out of range, you will get an error like this:\n\nERROR 1231 (42000): Variable \'MYSQL_ERRNO\' can\'t be set to the value of \'0\'\n\nExamples\n--------\n\nHere\'s what happens if SIGNAL is used in the client to generate errors:\n\nSIGNAL SQLSTATE \'01000\';\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n\n+---------+------+------------------------------------------+\n| Level | Code | Message |\n+---------+------+------------------------------------------+\n| Warning | 1642 | Unhandled user-defined warning condition |\n+---------+------+------------------------------------------+\n1 row in set (0.06 sec)\n\nSIGNAL SQLSTATE \'02000\';\nERROR 1643 (02000): Unhandled user-defined not found condition\n\nHow to specify MYSQL_ERRNO and MESSAGE_TEXT properties:\n\nSIGNAL SQLSTATE \'45000\' SET MYSQL_ERRNO=30001, MESSAGE_TEXT=\'H\nello, world!\';\n\nERROR 30001 (45000): Hello, world!\n\nThe following code shows how to use user variables, local variables and\ncharacter set conversion with SIGNAL:\n\nCREATE PROCEDURE test_error(x INT)\nBEGIN\n DECLARE errno SMALLINT UNSIGNED DEFAULT 31001;\n SET @errmsg = \'Hello, world!\';\n IF x = 1 THEN\n SIGNAL SQLSTATE \'45000\' SET\n MYSQL_ERRNO = errno,\n MESSAGE_TEXT = @errmsg;\n ELSE\n SIGNAL SQLSTATE \'45000\' SET\n MYSQL_ERRNO = errno,\n MESSAGE_TEXT = _utf8\'Hello, world!\';\n END IF;\nEND;\n\nHow to use named error conditions:\n\nCREATE PROCEDURE test_error(n INT)\nBEGIN\n DECLARE `too_big` CONDITION FOR SQLSTATE \'45000\';\n IF n > 10 THEN\n SIGNAL `too_big`;\n END IF;\nEND;\n\nIn this example, we\'ll define a HANDLER for an error code. When the error\noccurs, we SIGNAL a more informative error which makes sense for our procedure:\n\nCREATE PROCEDURE test_error()\nBEGIN\n DECLARE EXIT HANDLER\n FOR 1146\n BEGIN\n SIGNAL SQLSTATE \'45000\' SET\n MESSAGE_TEXT = \'Temporary tables not found; did you call init()\nprocedure?\';\n END;\n -- this will produce a 1146 error\n SELECT `c` FROM `temptab`;\nEND;\n\nURL: https://mariadb.com/kb/en/signal/','','https://mariadb.com/kb/en/signal/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (312,24,'WHILE','Syntax\n------\n\n[begin_label:] WHILE search_condition DO\n statement_list\nEND WHILE [end_label]\n\nDescription\n-----------\n\nThe statement list within a WHILE statement is repeated as long as the\nsearch_condition is true. statement_list consists of one or more statements.\nIf the loop must be executed at least once, REPEAT ... LOOP can be used\ninstead.\n\nA WHILE statement can be labeled. end_label cannot be given unless begin_label\nalso is present. If both are present, they must be the same.\n\nExamples\n--------\n\nCREATE PROCEDURE dowhile()\nBEGIN\n DECLARE v1 INT DEFAULT 5;\n\nWHILE v1 > 0 DO\n ...\n SET v1 = v1 - 1;\n END WHILE;\nEND\n\nURL: https://mariadb.com/kb/en/while/','','https://mariadb.com/kb/en/while/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (313,24,'Cursor Overview','Description\n-----------\n\nA cursor is a structure that allows you to go over records sequentially, and\nperform processing based on the result.\n\nMariaDB permits cursors inside stored programs, and MariaDB cursors are\nnon-scrollable, read-only and asensitive.\n\n* Non-scrollable means that the rows can only be fetched in the order\nspecified by the SELECT statement. Rows cannot be skipped, you cannot jump to\na specific row, and you cannot fetch rows in reverse order.\n* Read-only means that data cannot be updated through the cursor.\n* Asensitive means that the cursor points to the actual underlying data. This\nkind of cursor is quicker than the alternative, an insensitive cursor, as no\ndata is copied to a temporary table. However, changes to the data being used\nby the cursor will affect the cursor data.\n\nCursors are created with a DECLARE CURSOR statement and opened with an OPEN\nstatement. Rows are read with a FETCH statement before the cursor is finally\nclosed with a CLOSE statement.\n\nWhen FETCH is issued and there are no more rows to extract, the following\nerror is produced:\n\nERROR 1329 (02000): No data - zero rows fetched, selected, or processed\n\nTo avoid problems, a DECLARE HANDLER statement is generally used. The HANDLER\nshould handler the 1329 error, or the \'02000\' SQLSTATE, or the NOT FOUND error\nclass.\n\nOnly SELECT statements are allowed for cursors, and they cannot be contained\nin a variable - so, they cannot be composed dynamically. However, it is\npossible to SELECT from a view. Since the CREATE VIEW statement can be\nexecuted as a prepared statement, it is possible to dynamically create the\nview that is queried by the cursor.\n\nFrom MariaDB 10.3.0, cursors can have parameters. Cursor parameters can appear\nin any part of the DECLARE CURSOR select_statement where a stored procedure\nvariable is allowed (select list, WHERE, HAVING, LIMIT etc). See DECLARE\nCURSOR and OPEN for syntax, and below for an example:\n\nExamples\n--------\n\nCREATE TABLE c1(i INT);\n\nCREATE TABLE c2(i INT);\n\nCREATE TABLE c3(i INT);\n\nDELIMITER //\n\nCREATE PROCEDURE p1()\nBEGIN\n DECLARE done INT DEFAULT FALSE;\n DECLARE x, y INT;\n DECLARE cur1 CURSOR FOR SELECT i FROM test.c1;\n DECLARE cur2 CURSOR FOR SELECT i FROM test.c2;\n DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;\n\nOPEN cur1;\n OPEN cur2;\n\nread_loop: LOOP\n FETCH cur1 INTO x;\n FETCH cur2 INTO y;\n IF done THEN\n LEAVE read_loop;\n END IF;\n IF x < y THEN\n INSERT INTO test.c3 VALUES (x);\n ELSE\n INSERT INTO test.c3 VALUES (y);\n END IF;\n END LOOP;\n\nCLOSE cur1;\n CLOSE cur2;\nEND; //\n\nDELIMITER ;\n\nINSERT INTO c1 VALUES(5),(50),(500);\n\nINSERT INTO c2 VALUES(10),(20),(30);\n\nCALL p1;\n\nSELECT * FROM c3;\n+------+\n| i |\n+------+\n| 5 |\n| 20 |\n| 30 |\n+------+\n\nFrom MariaDB 10.3.0\n\nDROP PROCEDURE IF EXISTS p1;\nDROP TABLE IF EXISTS t1;\nCREATE TABLE t1 (a INT, b VARCHAR(10));\n\nINSERT INTO t1 VALUES (1,\'old\'),(2,\'old\'),(3,\'old\'),(4,\'old\'),(5,\'old\');\n\nDELIMITER //\n\nCREATE PROCEDURE p1(min INT,max INT)\nBEGIN\n DECLARE done INT DEFAULT FALSE;\n DECLARE va INT;\n DECLARE cur CURSOR(pmin INT, pmax INT) FOR SELECT a FROM t1 WHERE a BETWEEN\npmin AND pmax;\n DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=TRUE;\n OPEN cur(min,max);\n read_loop: LOOP\n FETCH cur INTO va;\n IF done THEN\n LEAVE read_loop;\n END IF;\n INSERT INTO t1 VALUES (va,\'new\');\n END LOOP;\n CLOSE cur;\nEND;\n//\n\nDELIMITER ;\n\nCALL p1(2,4);\n\nSELECT * FROM t1;\n+------+------+\n| a | b |\n+------+------+\n| 1 | old |\n| 2 | old |\n| 3 | old |\n| 4 | old |\n| 5 | old |\n| 2 | new |\n| 3 | new |\n| 4 | new |\n+------+------+\n\nURL: https://mariadb.com/kb/en/cursor-overview/','','https://mariadb.com/kb/en/cursor-overview/'); @@ -432,20 +432,20 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (339,26,'SHOW EXPLAIN','Syntax\n------\n\nSHOW EXPLAIN [FORMAT=JSON] FOR ;\nEXPLAIN [FORMAT=JSON] FOR CONNECTION ;\n\nDescription\n-----------\n\nThe SHOW EXPLAIN command allows one to get an EXPLAIN (that is, a description\nof a query plan) of a query running in a certain connection.\n\nSHOW EXPLAIN FOR ;\n\nwill produce an EXPLAIN output for the query that connection number\nconnection_id is running. The connection id can be obtained with SHOW\nPROCESSLIST.\n\nSHOW EXPLAIN FOR 1;\n+------+-------------+-------+-------+---------------+------+---------+------+-\n-------+-------------+\n| id | select_type | table | type | possible_keys | key | key_len | ref |\nrows | Extra |\n+------+-------------+-------+-------+---------------+------+---------+------+-\n-------+-------------+\n| 1 | SIMPLE | tbl | index | NULL | a | 5 | NULL |\n1000107 | Using index |\n+------+-------------+-------+-------+---------------+------+---------+------+-\n-------+-------------+\n1 row in set, 1 warning (0.00 sec)\n\nThe output is always accompanied with a warning which shows the query the\ntarget connection is running (this shows what the EXPLAIN is for):\n\nSHOW WARNINGS;\n+-------+------+------------------------+\n| Level | Code | Message |\n+-------+------+------------------------+\n| Note | 1003 | select sum(a) from tbl |\n+-------+------+------------------------+\n1 row in set (0.00 sec)\n\nEXPLAIN FOR CONNECTION\n----------------------\n\nMariaDB starting with 10.9\n--------------------------\nThe EXPLAIN FOR CONNECTION syntax was added for MySQL compatibility.\n\nFORMAT=JSON\n-----------\n\nMariaDB starting with 10.9\n--------------------------\nSHOW EXPLAIN [FORMAT=JSON] FOR extends SHOW EXPLAIN to return\nmore detailed JSON output.\n\nPossible Errors\n---------------\n\nThe output can be only produced if the target connection is currently running\na query, which has a ready query plan. If this is not the case, the output\nwill be:\n\nSHOW EXPLAIN FOR 2;\nERROR 1932 (HY000): Target is not running an EXPLAINable command\n\nYou will get this error when:\n\n* the target connection is not running a command for which one can run EXPLAIN\n* the target connection is running a command for which one can run EXPLAIN, but\nthere is no query plan yet (for example, tables are open and locks are\n acquired before the query plan is produced)\n\nDifferences Between SHOW EXPLAIN and EXPLAIN Outputs\n----------------------------------------------------\n\nBackground\n----------\n\nIn MySQL, EXPLAIN execution takes a slightly different route from the way the\nreal query (typically the SELECT) is optimized. This is unfortunate, and has\ncaused a number of bugs in EXPLAIN. (For example, see MDEV-326, MDEV-410, and\nlp:1013343. lp:992942 is not directly about EXPLAIN, but it also would not\nhave existed if MySQL didn\'t try to delete parts of a query plan in the middle\nof the query)\n\nSHOW EXPLAIN examines a running SELECT, and hence its output may be slightly\ndifferent from what EXPLAIN SELECT would produce. We did our best to make sure\nthat either the difference is negligible, or SHOW EXPLAIN\'s output is closer\nto reality than EXPLAIN\'s output.\n\nList of Recorded Differences\n----------------------------\n\n* SHOW EXPLAIN may have Extra=\'no matching row in const table\', where EXPLAIN\nwould produce Extra=\'Impossible WHERE ...\'\n* For queries with subqueries, SHOW EXPLAIN may print select_type==PRIMARY\nwhere regular EXPLAIN used to print select_type==SIMPLE, or vice versa.\n\nRequired Permissions\n--------------------\n\nRunning SHOW EXPLAIN requires the same permissions as running SHOW PROCESSLIST\nwould.\n\nURL: https://mariadb.com/kb/en/show-explain/','','https://mariadb.com/kb/en/show-explain/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (340,26,'BACKUP STAGE','MariaDB starting with 10.4.1\n----------------------------\nThe BACKUP STAGE commands were introduced in MariaDB 10.4.1.\n\nThe BACKUP STAGE commands are a set of commands to make it possible to make an\nefficient external backup tool.\n\nSyntax\n------\n\nBACKUP STAGE [START | FLUSH | BLOCK_DDL | BLOCK_COMMIT | END ]\n\nIn the following text, a transactional table means InnoDB or \"InnoDB-like\nengine with redo log that can lock redo purges and can be copied without locks\nby an outside process\".\n\nGoals with BACKUP STAGE Commands\n--------------------------------\n\n* To be able to do a majority of the backup with the minimum possible server\nlocks. Especially for transactional tables (InnoDB, MyRocks etc) there is only\nneed for a very short block of new commits while copying statistics and log\ntables.\n* DDL are only needed to be blocked for a very short duration of the backup\nwhile mariabackup is copying the tables affected by DDL during the initial\npart of the backup.\n* Most non transactional tables (those that are not in use) will be copied\nduring BACKUP STAGE START. The exceptions are system statistic and log tables\nthat are not blocked during the backup until BLOCK_COMMIT.\n* Should work efficiently with backup tools that use disk snapshots.\n* Should work as efficiently as possible for all table types that store data\non the local disks.\n* As little copying as possible under higher level stages/locks. For example,\n.frm (dictionary) and .trn (trigger) files should be copying while copying the\ntable data.\n\nBACKUP STAGE Commands\n---------------------\n\nBACKUP STAGE START\n------------------\n\nThe START stage is designed for the following tasks:\n\n* Blocks purge of redo files for storage engines that needs this (Aria)\n* Start logging of DDL commands into \'datadir\'/ddl.log. This may take a short\ntime as the command has to wait until there are no active DDL commands.\n\nBACKUP STAGE FLUSH\n------------------\n\nThe FLUSH stage is designed for the following tasks:\n\n* FLUSH all changes for inactive non-transactional tables, except for\nstatistics and log tables.\n* Close all tables that are not in use, to ensure they are marked as closed\nfor the backup.\n* BLOCK all new write locks for all non transactional tables (except\nstatistics and log tables). The command will not wait for tables that are in\nuse by read-only transactions.\n\nDDLs don\'t have to be blocked at this stage as they can\'t cause the table to\nbe in an inconsistent state. This is true also for non-transactional tables.\n\nBACKUP STAGE BLOCK_DDL\n----------------------\n\nThe BLOCK_DDL stage is designed for the following tasks:\n\n* Wait for all statements using write locked non-transactional tables to end.\n* Blocks CREATE TABLE, DROP TABLE, TRUNCATE TABLE, and RENAME TABLE.\n* Blocks also start off a new ALTER TABLE and the final rename phase of ALTER\nTABLE. Running ALTER TABLES are not blocked.\n\nBACKUP STAGE BLOCK_COMMIT\n-------------------------\n\nThe BLOCK_COMMIT stage is designed for the following tasks:\n\n* Lock the binary log and commit/rollback to ensure that no changes are\ncommitted to any tables. If there are active commits or data to be copied to\nthe binary log this will be allowed to finish. Active transactions will not\naffect BLOCK_COMMIT.\n* This doesn\'t lock temporary tables that are not used by replication. However\nthese will be blocked when it\'s time to write to the binary log.\n* Lock system log tables and statistics tables, flush them and mark them\nclosed.\n\nWhen the BLOCK_COMMIT\'s stages return, this is the \'backup time\'. Everything\ncommitted will be in the backup and everything not committed will roll back.\n\nTransactional engines will continue to do changes to the redo log during the\nBLOCK COMMIT stage, but this is not important as all of these will roll back\nlater as the changes will not be committed.\n\nBACKUP STAGE END\n----------------\n\nThe END stage is designed for the following tasks:\n\n* End DDL logging\n* Free resources\n\nUsing BACKUP STAGE Commands with Backup Tools\n---------------------------------------------\n\nUsing BACKUP STAGE Commands with Mariabackup\n--------------------------------------------\n\nThe BACKUP STAGE commands are a set of commands to make it possible to make an\nefficient external backup tool. How Mariabackup uses these commands depends on\nwhether you are using the version that is bundled with MariaDB Community\nServer or the version that is bundled with MariaDB Enterprise Server. See\nMariabackup and BACKUP STAGE Commands for some examples on how Mariabackup\nuses these commands.\n\nIf you would like to use a version of Mariabackup that uses the BACKUP STAGE\ncommands in an efficient way, then one option is to use MariaDB Enterprise\nBackup that is bundled with MariaDB Enterprise Server.\n\nUsing BACKUP STAGE Commands with Storage Snapshots\n--------------------------------------------------\n\nThe BACKUP STAGE commands are a set of commands to make it possible to make an\nefficient external backup tool. These commands could even be used by tools\nthat perform backups by taking a snapshot of a file system, SAN, or some other\nkind of storage device. See Storage Snapshots and BACKUP STAGE Commands for\nsome examples on how to use each BACKUP STAGE command in an efficient way.\n\nPrivileges\n----------\n\nBACKUP STAGE requires the RELOAD privilege.\n\nNotes\n-----\n\n* Only one connection can run BACKUP STAGE START. If a second connection\ntries, it will wait until the first one has executed BACKUP STAGE END.\n* If the user skips a BACKUP STAGE, then all intermediate backup stages will\nautomatically be run. This will allow us to add new stages within the BACKUP\nSTAGE hierarchy in the future with even more precise locks without causing\nproblems for tools using an earlier version of the BACKUP STAGE implementation.\n* One can use the max_statement_time or lock_wait_timeout system variables to\nensure that a BACKUP STAGE command doesn\'t block the server too long.\n* DDL logging will only be available in MariaDB Enterprise Server 10.2 and\nlater.\n* A disconnect will automatically release backup stages.\n* There is no easy way to see which is the current stage.\n\nURL: https://mariadb.com/kb/en/backup-stage/','','https://mariadb.com/kb/en/backup-stage/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (341,26,'BACKUP LOCK','MariaDB starting with 10.4.2\n----------------------------\nThe BACKUP LOCK command was introduced in MariaDB 10.4.2.\n\nBACKUP LOCK blocks a table from DDL statements. This is mainly intended to be\nused by tools like mariabackup that need to ensure there are no DDLs on a\ntable while the table files are opened. For example, for an Aria table that\nstores data in 3 files with extensions .frm, .MAI and .MAD. Normal read/write\noperations can continue as normal.\n\nSyntax\n------\n\nTo lock a table:\n\nBACKUP LOCK table_name\n\nTo unlock a table:\n\nBACKUP UNLOCK\n\nUsage in a Backup Tool\n----------------------\n\nBACKUP LOCK [database.]table_name;\n - Open all files related to a table (for example, t.frm, t.MAI and t.MYD)\nBACKUP UNLOCK;\n- Copy data\n- Close files\n\nThis ensures that all files are from the same generation, that is created at\nthe same time by the MariaDB server. This works, because the open files will\npoint to the original table files which will not be affected if there is any\nALTER TABLE while copying the files.\n\nPrivileges\n----------\n\nBACKUP LOCK requires the RELOAD privilege.\n\nNotes\n-----\n\n* The idea is that the BACKUP LOCK should be held for as short a time as\npossible by the backup tool. The time to take an uncontested lock is very\nshort! One can easily do 50,000 locks/unlocks per second on low end hardware.\n* One should use different connections for BACKUP STAGE commands and BACKUP\nLOCK.\n\nImplementation\n--------------\n\n* Internally, BACKUP LOCK is implemented by taking an MDLSHARED_HIGH_PRIO MDL\nlock on the table object, which protects the table from any DDL operations.\n\nURL: https://mariadb.com/kb/en/backup-lock/','','https://mariadb.com/kb/en/backup-lock/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (342,26,'FLUSH','Syntax\n------\n\nFLUSH [NO_WRITE_TO_BINLOG | LOCAL]\n flush_option [, flush_option] ...\n\nor when flushing tables:\n\nFLUSH [NO_WRITE_TO_BINLOG | LOCAL] TABLES [table_list] [table_flush_option]\n\nwhere table_list is a list of tables separated by , (comma).\n\nDescription\n-----------\n\nThe FLUSH statement clears or reloads various internal caches used by MariaDB.\nTo execute FLUSH, you must have the RELOAD privilege. See GRANT.\n\nThe RESET statement is similar to FLUSH. See RESET.\n\nYou cannot issue a FLUSH statement from within a stored function or a trigger.\nDoing so within a stored procedure is permitted, as long as it is not called\nby a stored function or trigger. See Stored Routine Limitations, Stored\nFunction Limitations and Trigger Limitations.\n\nIf a listed table is a view, an error like the following will be produced:\n\nERROR 1347 (HY000): \'test.v\' is not BASE TABLE\n\nBy default, FLUSH statements are written to the binary log and will be\nreplicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure the\nstatement is not written to the binary log.\n\nThe different flush options are:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| CHANGED_PAGE_BITMAPS | XtraDB only. Internal command used for backup |\n| | purposes. See the Information Schema |\n| | CHANGED_PAGE_BITMAPS Table. |\n+---------------------------+------------------------------------------------+\n| CLIENT_STATISTICS | Reset client statistics (see SHOW |\n| | CLIENT_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| DES_KEY_FILE | Reloads the DES key file (Specified with the |\n| | --des-key-file startup option). |\n+---------------------------+------------------------------------------------+\n| HOSTS | Flush the hostname cache (used for converting |\n| | ip to host names and for unblocking blocked |\n| | hosts. See max_connect_errors) |\n+---------------------------+------------------------------------------------+\n| INDEX_STATISTICS | Reset index statistics (see SHOW |\n| | INDEX_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| [ERROR | ENGINE | | Close and reopen the specified log type, or |\n| GENERAL | SLOW | BINARY | all log types if none are specified. FLUSH |\n| | RELAY] LOGS | RELAY LOGS [connection-name] can be used to |\n| | flush the relay logs for a specific |\n| | connection. Only one connection can be |\n| | specified per FLUSH command. See Multi-source |\n| | replication. FLUSH ENGINE LOGS will delete |\n| | all unneeded Aria redo logs. Since MariaDB |\n| | 10.1.30 and MariaDB 10.2.11, FLUSH BINARY |\n| | LOGS DELETE_DOMAIN_ID=(list-of-domains) can |\n| | be used to discard obsolete GTID domains from |\n| | the server\'s binary log state. In order for |\n| | this to be successful, no event group from |\n| | the listed GTID domains can be present in |\n| | existing binary log files. If some still |\n| | exist, then they must be purged prior to |\n| | executing this command. If the command |\n| | completes successfully, then it also rotates |\n| | the binary log. |\n+---------------------------+------------------------------------------------+\n| MASTER | Deprecated option, use RESET MASTER instead. |\n+---------------------------+------------------------------------------------+\n| PRIVILEGES | Reload all privileges from the privilege |\n| | tables in the mysql database. If the server |\n| | is started with --skip-grant-table option, |\n| | this will activate the privilege tables again. |\n+---------------------------+------------------------------------------------+\n| QUERY CACHE | Defragment the query cache to better utilize |\n| | its memory. If you want to reset the query |\n| | cache, you can do it with RESET QUERY CACHE. |\n+---------------------------+------------------------------------------------+\n| QUERY_RESPONSE_TIME | See the QUERY_RESPONSE_TIME plugin. |\n+---------------------------+------------------------------------------------+\n| SLAVE | Deprecated option, use RESET REPLICA or RESET |\n| | SLAVE instead. |\n+---------------------------+------------------------------------------------+\n| SSL | Used to dynamically reinitialize the server\'s |\n| | TLS context by reloading the files defined by |\n| | several TLS system variables. See FLUSH SSL |\n| | for more information. This command was first |\n| | added in MariaDB 10.4.1. |\n+---------------------------+------------------------------------------------+\n| STATUS | Resets all server status variables that can |\n| | be reset to 0. Not all global status |\n| | variables support this, so not all global |\n| | values are reset. See FLUSH STATUS for more |\n| | information. |\n+---------------------------+------------------------------------------------+\n| TABLE | Close tables given as options or all open |\n| | tables if no table list was used. From |\n| | MariaDB 10.4.1, using without any table list |\n| | will only close tables not in use, and tables |\n| | not locked by the FLUSH TABLES connection. If |\n| | there are no locked tables, FLUSH TABLES will |\n| | be instant and will not cause any waits, as |\n| | it no longer waits for tables in use. When a |\n| | table list is provided, from MariaDB 10.4.1, |\n| | the server will wait for the end of any |\n| | transactions that are using the tables. |\n| | Previously, FLUSH TABLES only waited for the |\n| | statements to complete. |\n+---------------------------+------------------------------------------------+\n| TABLES | Same as FLUSH TABLE. |\n+---------------------------+------------------------------------------------+\n| TABLES ... FOR EXPORT | For InnoDB tables, flushes table changes to |\n| | disk to permit binary table copies while the |\n| | server is running. See FLUSH TABLES ... FOR |\n| | EXPORT for more. |\n+---------------------------+------------------------------------------------+\n| TABLES WITH READ LOCK | Closes all open tables. New tables are only |\n| | allowed to be opened with read locks until an |\n| | UNLOCK TABLES is given. |\n+---------------------------+------------------------------------------------+\n| TABLES WITH READ LOCK | As TABLES WITH READ LOCK but also disable all |\n| AND DISABLE CHECKPOINT | checkpoint writes by transactional table |\n| | engines. This is useful when doing a disk |\n| | snapshot of all tables. |\n+---------------------------+------------------------------------------------+\n| TABLE_STATISTICS | Reset table statistics (see SHOW |\n| | TABLE_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| USER_RESOURCES | Resets all per hour user resources. This |\n| | enables clients that have exhausted their |\n| | resources to connect again. |\n+---------------------------+------------------------------------------------+\n| USER_STATISTICS | Reset user statistics (see SHOW |\n| | USER_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| USER_VARIABLES | Reset user variables (see User-defined |\n| | variables). |\n+---------------------------+------------------------------------------------+\n\nYou can also use the mysqladmin client to flush things. Use mysqladmin --help\nto examine what flush commands it supports.\n\nFLUSH RELAY LOGS\n----------------\n\nFLUSH RELAY LOGS \'connection_name\';\n\nCompatibility with MySQL\n------------------------\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nas using the channel_name directly after the FLUSH command.\n\nFor example, one can now use:\n\nFLUSH RELAY LOGS FOR CHANNEL \'connection_name\';\n\nFLUSH STATUS\n------------\n\nServer status variables can be reset by executing the following:\n\nFLUSH STATUS;\n\nGlobal Status Variables that Support FLUSH STATUS\n-------------------------------------------------\n\nNot all global status variables support being reset by FLUSH STATUS.\nCurrently, the following status variables are reset by FLUSH STATUS:\n\n* Aborted_clients\n* Aborted_connects\n* Binlog_cache_disk_use\n* Binlog_cache_use\n* Binlog_stmt_cache_disk_use\n* Binlog_stmt_cache_use\n* Connection_errors_accept\n* Connection_errors_internal\n* Connection_errors_max_connections\n* Connection_errors_peer_address\n* Connection_errors_select\n* Connection_errors_tcpwrap\n* Created_tmp_files\n* Delayed_errors\n* Delayed_writes\n* Feature_check_constraint\n* Feature_delay_key_write\n* Max_used_connections\n* Opened_plugin_libraries\n* Performance_schema_accounts_lost\n* Performance_schema_cond_instances_lost\n* Performance_schema_digest_lost\n* Performance_schema_file_handles_lost\n* Performance_schema_file_instances_lost\n* Performance_schema_hosts_lost\n* Performance_schema_locker_lost\n* Performance_schema_mutex_instances_lost\n* Performance_schema_rwlock_instances_lost\n* Performance_schema_session_connect_attrs_lost\n* Performance_schema_socket_instances_lost\n* Performance_schema_stage_classes_lost\n* Performance_schema_statement_classes_lost\n* Performance_schema_table_handles_lost\n* Performance_schema_table_instances_lost\n* Performance_schema_thread_instances_lost\n* Performance_schema_users_lost\n* Qcache_hits\n* Qcache_inserts\n* Qcache_lowmem_prunes\n* Qcache_not_cached\n* Rpl_semi_sync_master_no_times\n* Rpl_semi_sync_master_no_tx\n* Rpl_semi_sync_master_timefunc_failures\n* Rpl_semi_sync_master_wait_pos_backtraverse\n* Rpl_semi_sync_master_yes_tx\n* Rpl_transactions_multi_engine\n* Server_audit_writes_failed\n* Slave_retried_transactions\n* Slow_launch_threads\n* Ssl_accept_renegotiates\n* Ssl_accepts\n* Ssl_callback_cache_hits\n* Ssl_client_connects\n* Ssl_connect_renegotiates\n* Ssl_ctx_verify_depth\n* Ssl_ctx_verify_mode\n* Ssl_finished_accepts\n* Ssl_finished_connects\n* Ssl_session_cache_hits\n* Ssl_session_cache_misses\n* Ssl_session_cache_overflows\n* Ssl_session_cache_size\n* Ssl_session_cache_timeouts\n* Ssl_sessions_reused\n* Ssl_used_session_cache_entries\n* Subquery_cache_hit\n* Subquery_cache_miss\n* Table_locks_immediate\n* Table_locks_waited\n* Tc_log_max_pages_used\n* Tc_log_page_waits\n* Transactions_gtid_foreign_engine\n* Transactions_multi_engine\n\nThe different usage of FLUSH TABLES\n-----------------------------------\n\nThe purpose of FLUSH TABLES\n---------------------------\n\nThe purpose of FLUSH TABLES is to clean up the open table cache and table\ndefinition cache from not in use tables. This frees up memory and file\ndescriptors. Normally this is not needed as the caches works on a FIFO bases,\nbut can be useful if the server seams to use up to much memory for some reason.\n\nThe purpose of FLUSH TABLES WITH READ LOCK \n-------------------------------------------\n\nFLUSH TABLES WITH READ LOCK is useful if you want to take a backup of some\ntables. When FLUSH TABLES WITH READ LOCK returns, all write access to tables\nare blocked and all tables are marked as \'properly closed\' on disk. The tables\ncan still be used for read operations.\n\nThe purpose of FLUSH TABLES table_list\n--------------------------------------\n\nFLUSH TABLES table_list is useful if you want to copy a table object/files to\nor from the server. This command puts a lock that stops new users of the table\nand will wait until everyone has stopped using the table. The table is then\nremoved from the table definition and table cache.\n\nNote that it\'s up to the user to ensure that no one is accessing the table\nbetween FLUSH TABLES and the table is copied to or from the server. This can\nbe secured by using LOCK TABLES.\n\nIf there are any tables locked by the connection that is using FLUSH TABLES','','https://mariadb.com/kb/en/flush/'); -update help_topic set description = CONCAT(description, '\nall the locked tables will be closed as part of the flush and reopened and\nrelocked before FLUSH TABLES returns. This allows one to copy the table after\nFLUSH TABLES returns without having any writes on the table. For now this\nworks works with most tables, except InnoDB as InnoDB may do background purges\non the table even while it\'s write locked.\n\nThe purpose of FLUSH TABLES table_list WITH READ LOCK\n-----------------------------------------------------\n\nFLUSH TABLES table_list WITH READ LOCK should work as FLUSH TABLES WITH READ\nLOCK, but only those tables that are listed will be properly closed. However\nin practice this works exactly like FLUSH TABLES WITH READ LOCK as the FLUSH\ncommand has anyway to wait for all WRITE operations to end because we are\ndepending on a global read lock for this code. In the future we should\nconsider fixing this to instead use meta data locks.\n\nImplementation of FLUSH TABLES commands in MariaDB 10.4.8 and above\n-------------------------------------------------------------------\n\nImplementation of FLUSH TABLES\n------------------------------\n\n* Free memory and file descriptors not in use\n\nImplementation of FLUSH TABLES WITH READ LOCK\n---------------------------------------------\n\n* Lock all tables read only for simple old style backup.\n* All background writes are suspended and tables are marked as closed.\n* No statement requiring table changes are allowed for any user until UNLOCK\nTABLES.\n\nInstead of using FLUSH TABLE WITH READ LOCK one should in most cases instead\nuse BACKUP STAGE BLOCK_COMMIT.\n\nImplementation of FLUSH TABLES table_list\n-----------------------------------------\n\n* Free memory and file descriptors for tables not in use from table list.\n* Lock given tables as read only.\n* Wait until all translations has ended that uses any of the given tables.\n* Wait until all background writes are suspended and tables are marked as\nclosed.\n\nImplementation of FLUSH TABLES table_list FOR EXPORT\n----------------------------------------------------\n\n* Free memory and file descriptors for tables not in use from table list\n* Lock given tables as read.\n* Wait until all background writes are suspended and tables are marked as\nclosed.\n* Check that all tables supports FOR EXPORT\n* No changes to these tables allowed until UNLOCK TABLES\n\nThis is basically the same behavior as in old MariaDB version if one first\nlock the tables, then do FLUSH TABLES. The tables will be copyable until\nUNLOCK TABLES.\n\nFLUSH SSL\n---------\n\nMariaDB starting with 10.4\n--------------------------\nThe FLUSH SSL command was first added in MariaDB 10.4.\n\nIn MariaDB 10.4 and later, the FLUSH SSL command can be used to dynamically\nreinitialize the server\'s TLS context. This is most useful if you need to\nreplace a certificate that is about to expire without restarting the server.\n\nThis operation is performed by reloading the files defined by the following\nTLS system variables:\n\n* ssl_cert\n* ssl_key\n* ssl_ca\n* ssl_capath\n* ssl_crl\n* ssl_crlpath\n\nThese TLS system variables are not dynamic, so their values can not be changed\nwithout restarting the server.\n\nIf you want to dynamically reinitialize the server\'s TLS context, then you\nneed to change the certificate and key files at the relevant paths defined by\nthese TLS system variables, without actually changing the values of the\nvariables. See MDEV-19341 for more information.\n\nReducing Memory Usage\n---------------------\n\nTo flush some of the global caches that take up memory, you could execute the\nfollowing command:\n\nFLUSH LOCAL HOSTS,\n QUERY CACHE,\n TABLE_STATISTICS,\n INDEX_STATISTICS,\n USER_STATISTICS;\n\nURL: https://mariadb.com/kb/en/flush/') WHERE help_topic_id = 342; +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (342,26,'FLUSH','Syntax\n------\n\nFLUSH [NO_WRITE_TO_BINLOG | LOCAL]\n flush_option [, flush_option] ...\n\nor when flushing tables:\n\nFLUSH [NO_WRITE_TO_BINLOG | LOCAL] TABLES [table_list] [table_flush_option]\n\nwhere table_list is a list of tables separated by , (comma).\n\nDescription\n-----------\n\nThe FLUSH statement clears or reloads various internal caches used by MariaDB.\nTo execute FLUSH, you must have the RELOAD privilege. See GRANT.\n\nThe RESET statement is similar to FLUSH. See RESET.\n\nYou cannot issue a FLUSH statement from within a stored function or a trigger.\nDoing so within a stored procedure is permitted, as long as it is not called\nby a stored function or trigger. See Stored Routine Limitations, Stored\nFunction Limitations and Trigger Limitations.\n\nIf a listed table is a view, an error like the following will be produced:\n\nERROR 1347 (HY000): \'test.v\' is not BASE TABLE\n\nBy default, FLUSH statements are written to the binary log and will be\nreplicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure the\nstatement is not written to the binary log.\n\nThe different flush options are:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| CHANGED_PAGE_BITMAPS | XtraDB only. Internal command used for backup |\n| | purposes. See the Information Schema |\n| | CHANGED_PAGE_BITMAPS Table. |\n+---------------------------+------------------------------------------------+\n| CLIENT_STATISTICS | Reset client statistics (see SHOW |\n| | CLIENT_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| DES_KEY_FILE | Reloads the DES key file (Specified with the |\n| | --des-key-file startup option). |\n+---------------------------+------------------------------------------------+\n| HOSTS | Flush the hostname cache (used for converting |\n| | ip to host names and for unblocking blocked |\n| | hosts. See max_connect_errors and |\n| | performance_schema.host_cache |\n+---------------------------+------------------------------------------------+\n| INDEX_STATISTICS | Reset index statistics (see SHOW |\n| | INDEX_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| [ERROR | ENGINE | | Close and reopen the specified log type, or |\n| GENERAL | SLOW | BINARY | all log types if none are specified. FLUSH |\n| | RELAY] LOGS | RELAY LOGS [connection-name] can be used to |\n| | flush the relay logs for a specific |\n| | connection. Only one connection can be |\n| | specified per FLUSH command. See Multi-source |\n| | replication. FLUSH ENGINE LOGS will delete |\n| | all unneeded Aria redo logs. Since MariaDB |\n| | 10.1.30 and MariaDB 10.2.11, FLUSH BINARY |\n| | LOGS DELETE_DOMAIN_ID=(list-of-domains) can |\n| | be used to discard obsolete GTID domains from |\n| | the server\'s binary log state. In order for |\n| | this to be successful, no event group from |\n| | the listed GTID domains can be present in |\n| | existing binary log files. If some still |\n| | exist, then they must be purged prior to |\n| | executing this command. If the command |\n| | completes successfully, then it also rotates |\n| | the binary log. |\n+---------------------------+------------------------------------------------+\n| MASTER | Deprecated option, use RESET MASTER instead. |\n+---------------------------+------------------------------------------------+\n| PRIVILEGES | Reload all privileges from the privilege |\n| | tables in the mysql database. If the server |\n| | is started with --skip-grant-table option, |\n| | this will activate the privilege tables again. |\n+---------------------------+------------------------------------------------+\n| QUERY CACHE | Defragment the query cache to better utilize |\n| | its memory. If you want to reset the query |\n| | cache, you can do it with RESET QUERY CACHE. |\n+---------------------------+------------------------------------------------+\n| QUERY_RESPONSE_TIME | See the QUERY_RESPONSE_TIME plugin. |\n+---------------------------+------------------------------------------------+\n| SLAVE | Deprecated option, use RESET REPLICA or RESET |\n| | SLAVE instead. |\n+---------------------------+------------------------------------------------+\n| SSL | Used to dynamically reinitialize the server\'s |\n| | TLS context by reloading the files defined by |\n| | several TLS system variables. See FLUSH SSL |\n| | for more information. This command was first |\n| | added in MariaDB 10.4.1. |\n+---------------------------+------------------------------------------------+\n| STATUS | Resets all server status variables that can |\n| | be reset to 0. Not all global status |\n| | variables support this, so not all global |\n| | values are reset. See FLUSH STATUS for more |\n| | information. |\n+---------------------------+------------------------------------------------+\n| TABLE | Close tables given as options or all open |\n| | tables if no table list was used. From |\n| | MariaDB 10.4.1, using without any table list |\n| | will only close tables not in use, and tables |\n| | not locked by the FLUSH TABLES connection. If |\n| | there are no locked tables, FLUSH TABLES will |\n| | be instant and will not cause any waits, as |\n| | it no longer waits for tables in use. When a |\n| | table list is provided, from MariaDB 10.4.1, |\n| | the server will wait for the end of any |\n| | transactions that are using the tables. |\n| | Previously, FLUSH TABLES only waited for the |\n| | statements to complete. |\n+---------------------------+------------------------------------------------+\n| TABLES | Same as FLUSH TABLE. |\n+---------------------------+------------------------------------------------+\n| TABLES ... FOR EXPORT | For InnoDB tables, flushes table changes to |\n| | disk to permit binary table copies while the |\n| | server is running. See FLUSH TABLES ... FOR |\n| | EXPORT for more. |\n+---------------------------+------------------------------------------------+\n| TABLES WITH READ LOCK | Closes all open tables. New tables are only |\n| | allowed to be opened with read locks until an |\n| | UNLOCK TABLES is given. |\n+---------------------------+------------------------------------------------+\n| TABLES WITH READ LOCK | As TABLES WITH READ LOCK but also disable all |\n| AND DISABLE CHECKPOINT | checkpoint writes by transactional table |\n| | engines. This is useful when doing a disk |\n| | snapshot of all tables. |\n+---------------------------+------------------------------------------------+\n| TABLE_STATISTICS | Reset table statistics (see SHOW |\n| | TABLE_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| USER_RESOURCES | Resets all per hour user resources. This |\n| | enables clients that have exhausted their |\n| | resources to connect again. |\n+---------------------------+------------------------------------------------+\n| USER_STATISTICS | Reset user statistics (see SHOW |\n| | USER_STATISTICS). |\n+---------------------------+------------------------------------------------+\n| USER_VARIABLES | Reset user variables (see User-defined |\n| | variables). |\n+---------------------------+------------------------------------------------+\n\nYou can also use the mysqladmin client to flush things. Use mysqladmin --help\nto examine what flush commands it supports.\n\nFLUSH RELAY LOGS\n----------------\n\nFLUSH RELAY LOGS \'connection_name\';\n\nCompatibility with MySQL\n------------------------\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nas using the channel_name directly after the FLUSH command.\n\nFor example, one can now use:\n\nFLUSH RELAY LOGS FOR CHANNEL \'connection_name\';\n\nFLUSH STATUS\n------------\n\nServer status variables can be reset by executing the following:\n\nFLUSH STATUS;\n\nGlobal Status Variables that Support FLUSH STATUS\n-------------------------------------------------\n\nNot all global status variables support being reset by FLUSH STATUS.\nCurrently, the following status variables are reset by FLUSH STATUS:\n\n* Aborted_clients\n* Aborted_connects\n* Binlog_cache_disk_use\n* Binlog_cache_use\n* Binlog_stmt_cache_disk_use\n* Binlog_stmt_cache_use\n* Connection_errors_accept\n* Connection_errors_internal\n* Connection_errors_max_connections\n* Connection_errors_peer_address\n* Connection_errors_select\n* Connection_errors_tcpwrap\n* Created_tmp_files\n* Delayed_errors\n* Delayed_writes\n* Feature_check_constraint\n* Feature_delay_key_write\n* Max_used_connections\n* Opened_plugin_libraries\n* Performance_schema_accounts_lost\n* Performance_schema_cond_instances_lost\n* Performance_schema_digest_lost\n* Performance_schema_file_handles_lost\n* Performance_schema_file_instances_lost\n* Performance_schema_hosts_lost\n* Performance_schema_locker_lost\n* Performance_schema_mutex_instances_lost\n* Performance_schema_rwlock_instances_lost\n* Performance_schema_session_connect_attrs_lost\n* Performance_schema_socket_instances_lost\n* Performance_schema_stage_classes_lost\n* Performance_schema_statement_classes_lost\n* Performance_schema_table_handles_lost\n* Performance_schema_table_instances_lost\n* Performance_schema_thread_instances_lost\n* Performance_schema_users_lost\n* Qcache_hits\n* Qcache_inserts\n* Qcache_lowmem_prunes\n* Qcache_not_cached\n* Rpl_semi_sync_master_no_times\n* Rpl_semi_sync_master_no_tx\n* Rpl_semi_sync_master_timefunc_failures\n* Rpl_semi_sync_master_wait_pos_backtraverse\n* Rpl_semi_sync_master_yes_tx\n* Rpl_transactions_multi_engine\n* Server_audit_writes_failed\n* Slave_retried_transactions\n* Slow_launch_threads\n* Ssl_accept_renegotiates\n* Ssl_accepts\n* Ssl_callback_cache_hits\n* Ssl_client_connects\n* Ssl_connect_renegotiates\n* Ssl_ctx_verify_depth\n* Ssl_ctx_verify_mode\n* Ssl_finished_accepts\n* Ssl_finished_connects\n* Ssl_session_cache_hits\n* Ssl_session_cache_misses\n* Ssl_session_cache_overflows\n* Ssl_session_cache_size\n* Ssl_session_cache_timeouts\n* Ssl_sessions_reused\n* Ssl_used_session_cache_entries\n* Subquery_cache_hit\n* Subquery_cache_miss\n* Table_locks_immediate\n* Table_locks_waited\n* Tc_log_max_pages_used\n* Tc_log_page_waits\n* Transactions_gtid_foreign_engine\n* Transactions_multi_engine\n\nThe different usage of FLUSH TABLES\n-----------------------------------\n\nThe purpose of FLUSH TABLES\n---------------------------\n\nThe purpose of FLUSH TABLES is to clean up the open table cache and table\ndefinition cache from not in use tables. This frees up memory and file\ndescriptors. Normally this is not needed as the caches works on a FIFO bases,\nbut can be useful if the server seams to use up to much memory for some reason.\n\nThe purpose of FLUSH TABLES WITH READ LOCK \n-------------------------------------------\n\nFLUSH TABLES WITH READ LOCK is useful if you want to take a backup of some\ntables. When FLUSH TABLES WITH READ LOCK returns, all write access to tables\nare blocked and all tables are marked as \'properly closed\' on disk. The tables\ncan still be used for read operations.\n\nThe purpose of FLUSH TABLES table_list\n--------------------------------------\n\nFLUSH TABLES table_list is useful if you want to copy a table object/files to\nor from the server. This command puts a lock that stops new users of the table\nand will wait until everyone has stopped using the table. The table is then\nremoved from the table definition and table cache.\n\nNote that it\'s up to the user to ensure that no one is accessing the table\nbetween FLUSH TABLES and the table is copied to or from the server. This can\nbe secured by using LOCK TABLES.\n','','https://mariadb.com/kb/en/flush/'); +update help_topic set description = CONCAT(description, '\nIf there are any tables locked by the connection that is using FLUSH TABLES\nall the locked tables will be closed as part of the flush and reopened and\nrelocked before FLUSH TABLES returns. This allows one to copy the table after\nFLUSH TABLES returns without having any writes on the table. For now this\nworks works with most tables, except InnoDB as InnoDB may do background purges\non the table even while it\'s write locked.\n\nThe purpose of FLUSH TABLES table_list WITH READ LOCK\n-----------------------------------------------------\n\nFLUSH TABLES table_list WITH READ LOCK should work as FLUSH TABLES WITH READ\nLOCK, but only those tables that are listed will be properly closed. However\nin practice this works exactly like FLUSH TABLES WITH READ LOCK as the FLUSH\ncommand has anyway to wait for all WRITE operations to end because we are\ndepending on a global read lock for this code. In the future we should\nconsider fixing this to instead use meta data locks.\n\nImplementation of FLUSH TABLES commands in MariaDB 10.4.8 and above\n-------------------------------------------------------------------\n\nImplementation of FLUSH TABLES\n------------------------------\n\n* Free memory and file descriptors not in use\n\nImplementation of FLUSH TABLES WITH READ LOCK\n---------------------------------------------\n\n* Lock all tables read only for simple old style backup.\n* All background writes are suspended and tables are marked as closed.\n* No statement requiring table changes are allowed for any user until UNLOCK\nTABLES.\n\nInstead of using FLUSH TABLE WITH READ LOCK one should in most cases instead\nuse BACKUP STAGE BLOCK_COMMIT.\n\nImplementation of FLUSH TABLES table_list\n-----------------------------------------\n\n* Free memory and file descriptors for tables not in use from table list.\n* Lock given tables as read only.\n* Wait until all translations has ended that uses any of the given tables.\n* Wait until all background writes are suspended and tables are marked as\nclosed.\n\nImplementation of FLUSH TABLES table_list FOR EXPORT\n----------------------------------------------------\n\n* Free memory and file descriptors for tables not in use from table list\n* Lock given tables as read.\n* Wait until all background writes are suspended and tables are marked as\nclosed.\n* Check that all tables supports FOR EXPORT\n* No changes to these tables allowed until UNLOCK TABLES\n\nThis is basically the same behavior as in old MariaDB version if one first\nlock the tables, then do FLUSH TABLES. The tables will be copyable until\nUNLOCK TABLES.\n\nFLUSH SSL\n---------\n\nMariaDB starting with 10.4\n--------------------------\nThe FLUSH SSL command was first added in MariaDB 10.4.\n\nIn MariaDB 10.4 and later, the FLUSH SSL command can be used to dynamically\nreinitialize the server\'s TLS context. This is most useful if you need to\nreplace a certificate that is about to expire without restarting the server.\n\nThis operation is performed by reloading the files defined by the following\nTLS system variables:\n\n* ssl_cert\n* ssl_key\n* ssl_ca\n* ssl_capath\n* ssl_crl\n* ssl_crlpath\n\nThese TLS system variables are not dynamic, so their values can not be changed\nwithout restarting the server.\n\nIf you want to dynamically reinitialize the server\'s TLS context, then you\nneed to change the certificate and key files at the relevant paths defined by\nthese TLS system variables, without actually changing the values of the\nvariables. See MDEV-19341 for more information.\n\nReducing Memory Usage\n---------------------\n\nTo flush some of the global caches that take up memory, you could execute the\nfollowing command:\n\nFLUSH LOCAL HOSTS,\n QUERY CACHE,\n TABLE_STATISTICS,\n INDEX_STATISTICS,\n USER_STATISTICS;\n\nURL: https://mariadb.com/kb/en/flush/') WHERE help_topic_id = 342; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (343,26,'FLUSH QUERY CACHE','Description\n-----------\n\nYou can defragment the query cache to better utilize its memory with the FLUSH\nQUERY CACHE statement. The statement does not remove any queries from the\ncache.\n\nThe RESET QUERY CACHE statement removes all query results from the query\ncache. The FLUSH TABLES statement also does this.\n\nURL: https://mariadb.com/kb/en/flush-query-cache/','','https://mariadb.com/kb/en/flush-query-cache/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (344,26,'FLUSH TABLES FOR EXPORT','Syntax\n------\n\nFLUSH TABLES table_name [, table_name] FOR EXPORT\n\nDescription\n-----------\n\nFLUSH TABLES ... FOR EXPORT flushes changes to the specified tables to disk so\nthat binary copies can be made while the server is still running. This works\nfor Archive, Aria, CSV, InnoDB, MyISAM, MERGE, and XtraDB tables.\n\nThe table is read locked until one has issued UNLOCK TABLES.\n\nIf a storage engine does not support FLUSH TABLES FOR EXPORT, a 1031 error\n(SQLSTATE \'HY000\') is produced.\n\nIf FLUSH TABLES ... FOR EXPORT is in effect in the session, the following\nstatements will produce an error if attempted:\n\n* FLUSH TABLES WITH READ LOCK\n* FLUSH TABLES ... WITH READ LOCK\n* FLUSH TABLES ... FOR EXPORT\n* Any statement trying to update any table\n\nIf any of the following statements is in effect in the session, attempting\nFLUSH TABLES ... FOR EXPORT will produce an error.\n\n* FLUSH TABLES ... WITH READ LOCK\n* FLUSH TABLES ... FOR EXPORT\n* LOCK TABLES ... READ\n* LOCK TABLES ... WRITE\n\nFLUSH FOR EXPORT is not written to the binary log.\n\nThis statement requires the RELOAD and the LOCK TABLES privileges.\n\nIf one of the specified tables cannot be locked, none of the tables will be\nlocked.\n\nIf a table does not exist, an error like the following will be produced:\n\nERROR 1146 (42S02): Table \'test.xxx\' doesn\'t exist\n\nIf a table is a view, an error like the following will be produced:\n\nERROR 1347 (HY000): \'test.v\' is not BASE TABLE\n\nExample\n-------\n\nFLUSH TABLES test.t1 FOR EXPORT;\n# Copy files related to the table (see below)\nUNLOCK TABLES;\n\nFor a full description, please see copying MariaDB tables.\n\nURL: https://mariadb.com/kb/en/flush-tables-for-export/','','https://mariadb.com/kb/en/flush-tables-for-export/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (345,26,'SHOW RELAYLOG EVENTS','The terms master and slave have historically been used in replication, but the\nterms terms primary and replica are now preferred. The old terms are used\nstill used in parts of the documentation, and in MariaDB commands, although\nMariaDB 10.5 has begun the process of renaming. The documentation process is\nongoing. See MDEV-18777 to follow progress on this effort.\n\nSyntax\n------\n\nSHOW RELAYLOG [\'connection_name\'] EVENTS\n [IN \'log_name\'] [FROM pos] [LIMIT [offset,] row_count]\n [ FOR CHANNEL \'channel_name\']\n\nDescription\n-----------\n\nOn replicas, this command shows the events in the relay log. If \'log_name\' is\nnot specified, the first relay log is shown.\n\nSyntax for the LIMIT clause is the same as for SELECT ... LIMIT.\n\nUsing the LIMIT clause is highly recommended because the SHOW RELAYLOG EVENTS\ncommand returns the complete contents of the relay log, which can be quite\nlarge.\n\nThis command does not return events related to setting user and system\nvariables. If you need those, use mariadb-binlog/mysqlbinlog.\n\nOn the primary, this command does nothing.\n\nRequires the REPLICA MONITOR privilege (>= MariaDB 10.5.9), the REPLICATION\nSLAVE ADMIN privilege (>= MariaDB 10.5.2) or the REPLICATION SLAVE privilege\n(<= MariaDB 10.5.1).\n\nconnection_name\n---------------\n\nIf there is only one nameless primary, or the default primary (as specified by\nthe default_master_connection system variable) is intended, connection_name\ncan be omitted. If provided, the SHOW RELAYLOG statement will apply to the\nspecified primary. connection_name is case-insensitive.\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nas using the channel_name directly after SHOW RELAYLOG.\n\nURL: https://mariadb.com/kb/en/show-relaylog-events/','','https://mariadb.com/kb/en/show-relaylog-events/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (346,26,'SHOW SLAVE STATUS','Syntax\n------\n\nSHOW SLAVE [\"connection_name\"] STATUS [FOR CHANNEL \"connection_name\"]\nSHOW REPLICA [\"connection_name\"] STATUS -- From MariaDB 10.5.1\n\nor\n\nSHOW ALL SLAVES STATUS\nSHOW ALL REPLICAS STATUS -- From MariaDB 10.5.1\n\nDescription\n-----------\n\nThis statement is to be run on a replica and provides status information on\nessential parameters of the replica threads.\n\nThis statement requires the SUPER privilege, the REPLICATION_CLIENT privilege,\nor, from MariaDB 10.5.2, the REPLICATION SLAVE ADMIN privilege, or, from\nMariaDB 10.5.9, the REPLICA MONITOR privilege.\n\nMulti-Source\n------------\n\nThe ALL and \"connection_name\" options allow you to connect to many primaries\nat the same time.\n\nALL SLAVES (or ALL REPLICAS from MariaDB 10.5.1) gives you a list of all\nconnections to the primary nodes.\n\nThe rows will be sorted according to Connection_name.\n\nIf you specify a connection_name, you only get the information about that\nconnection. If connection_name is not used, then the name set by\ndefault_master_connection is used. If the connection name doesn\'t exist you\nwill get an error: There is no master connection for \'xxx\'.\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nas using the channel_name directly after SHOW SLAVE.\n\nColumn Descriptions\n-------------------\n\n+---------------+---------------------------------------+-------------------+\n| Name | Description | Added |\n+---------------+---------------------------------------+-------------------+\n| Connection_na | Name of the primary connection. | |\n| e | Returned with SHOW ALL SLAVES STATUS | |\n| | (or SHOW ALL REPLICAS STATUS from | |\n| | MariaDB 10.5.1) only. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_SQL_Sta | State of SQL thread. Returned with | |\n| e | SHOW ALL SLAVES STATUS (or SHOW ALL | |\n| | REPLICAS STATUS from MariaDB 10.5.1) | |\n| | only. See Slave SQL Thread States. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_IO_Stat | State of I/O thread. See Slave I/O | |\n| | Thread States. | |\n+---------------+---------------------------------------+-------------------+\n| Master_host | Master host that the replica is | |\n| | connected to. | |\n+---------------+---------------------------------------+-------------------+\n| Master_user | Account user name being used to | |\n| | connect to the primary. | |\n+---------------+---------------------------------------+-------------------+\n| Master_port | The port being used to connect to | |\n| | the primary. | |\n+---------------+---------------------------------------+-------------------+\n| Connect_Retry | Time in seconds between retries to | |\n| | connect. The default is 60. The | |\n| | CHANGE MASTER TO statement can set | |\n| | this. The master-retry-count option | |\n| | determines the maximum number of | |\n| | reconnection attempts. | |\n+---------------+---------------------------------------+-------------------+\n| Master_Log_Fi | Name of the primary binary log file | |\n| e | that the I/O thread is currently | |\n| | reading from. | |\n+---------------+---------------------------------------+-------------------+\n| Read_Master_L | Position up to which the I/O thread | |\n| g_Pos | has read in the current primary | |\n| | binary log file. | |\n+---------------+---------------------------------------+-------------------+\n| Relay_Log_Fil | Name of the relay log file that the | |\n| | SQL thread is currently processing. | |\n+---------------+---------------------------------------+-------------------+\n| Relay_Log_Pos | Position up to which the SQL thread | |\n| | has finished processing in the | |\n| | current relay log file. | |\n+---------------+---------------------------------------+-------------------+\n| Relay_Master_ | Name of the primary binary log file | |\n| og_File | that contains the most recent event | |\n| | executed by the SQL thread. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_IO_Runn | Whether the replica I/O thread is | |\n| ng | running and connected (Yes), running | |\n| | but not connected to a primary | |\n| | (Connecting) or not running (No). | |\n+---------------+---------------------------------------+-------------------+\n| Slave_SQL_Run | Whether or not the SQL thread is | |\n| ing | running. | |\n+---------------+---------------------------------------+-------------------+\n| Replicate_Do_ | Databases specified for replicating | |\n| B | with the replicate_do_db option. | |\n+---------------+---------------------------------------+-------------------+\n| Replicate_Ign | Databases specified for ignoring | |\n| re_DB | with the replicate_ignore_db option. | |\n+---------------+---------------------------------------+-------------------+\n| Replicate_Do_ | Tables specified for replicating | |\n| able | with the replicate_do_table option. | |\n+---------------+---------------------------------------+-------------------+\n| Replicate_Ign | Tables specified for ignoring with | |\n| re_Table | the replicate_ignore_table option. | |\n+---------------+---------------------------------------+-------------------+\n| Replicate_Wil | Tables specified for replicating | |\n| _Do_Table | with the replicate_wild_do_table | |\n| | option. | |\n+---------------+---------------------------------------+-------------------+\n| Replicate_Wil | Tables specified for ignoring with | |\n| _Ignore_Table | the replicate_wild_ignore_table | |\n| | option. | |\n+---------------+---------------------------------------+-------------------+\n| Last_Errno | Alias for Last_SQL_Errno (see below) | |\n+---------------+---------------------------------------+-------------------+\n| Last Error | Alias for Last_SQL_Error (see below) | |\n+---------------+---------------------------------------+-------------------+\n| Skip_Counter | Number of events that a replica | |\n| | skips from the master, as recorded | |\n| | in the sql_slave_skip_counter system | |\n| | variable. | |\n+---------------+---------------------------------------+-------------------+\n| Exec_Master_L | Position up to which the SQL thread | |\n| g_Pos | has processed in the current master | |\n| | binary log file. Can be used to | |\n| | start a new replica from a current | |\n| | replica with the CHANGE MASTER TO | |\n| | ... MASTER_LOG_POS option. | |\n+---------------+---------------------------------------+-------------------+\n| Relay_Log_Spa | Total size of all relay log files | |\n| e | combined. | |\n+---------------+---------------------------------------+-------------------+\n| Until_Conditi | | |\n| n | | |\n+---------------+---------------------------------------+-------------------+\n| Until_Log_Fil | The MASTER_LOG_FILE value of the | |\n| | START SLAVE UNTIL condition. | |\n+---------------+---------------------------------------+-------------------+\n| Until_Log_Pos | The MASTER_LOG_POS value of the | |\n| | START SLAVE UNTIL condition. | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_Al | Whether an SSL connection is | |\n| owed | permitted (Yes), not permitted (No) | |\n| | or permitted but without the replica | |\n| | having SSL support enabled (Ignored) | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_CA | The MASTER_SSL_CA option of the | |\n| File | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_CA | The MASTER_SSL_CAPATH option of the | |\n| Path | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_Ce | The MASTER_SSL_CERT option of the | |\n| t | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_Ci | The MASTER_SSL_CIPHER option of the | |\n| her | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_Ke | The MASTER_SSL_KEY option of the | |\n| | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Seconds_Behin | Difference between the timestamp | |\n| _Master | logged on the master for the event | |\n| | that the replica is currently | |\n| | processing, and the current | |\n| | timestamp on the replica. Zero if | |\n| | the replica is not currently | |\n| | processing an event. With parallel | |\n| | replication, seconds_behind_master | |\n| | is updated only after transactions | |\n| | commit. | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_Ve | The MASTER_SSL_VERIFY_SERVER_CERT | |\n| ify_Server_Ce | option of the CHANGE MASTER TO | |\n| t | statement. | |\n+---------------+---------------------------------------+-------------------+\n| Last_IO_Errno | Error code of the most recent error | |\n| | that caused the I/O thread to stop | |\n| | (also recorded in the replica\'s | |\n| | error log). 0 means no error. RESET | |\n| | SLAVE or RESET MASTER will reset | |\n| | this value. | |\n+---------------+---------------------------------------+-------------------+\n| Last_IO_Error | Error message of the most recent | |\n| | error that caused the I/O thread to | |\n| | stop (also recorded in the replica\'s | |\n| | error log). An empty string means no | |\n| | error. RESET SLAVE or RESET MASTER | |\n| | will reset this value. | |\n+---------------+---------------------------------------+-------------------+\n| Last_SQL_Errn | Error code of the most recent error | |\n| | that caused the SQL thread to stop | |\n| | (also recorded in the replica\'s | |\n| | error log). 0 means no error. RESET | |\n| | SLAVE or RESET MASTER will reset | |\n| | this value. | |\n+---------------+---------------------------------------+-------------------+\n| Last_SQL_Erro | Error message of the most recent | |\n| | error that caused the SQL thread to | |\n| | stop (also recorded in the replica\'s | |','','https://mariadb.com/kb/en/show-replica-status/'); update help_topic set description = CONCAT(description, '\n| | error log). An empty string means no | |\n| | error. RESET SLAVE or RESET MASTER | |\n| | will reset this value. | |\n+---------------+---------------------------------------+-------------------+\n| Replicate_Ign | List of server_ids that are | |\n| re_Server_Ids | currently being ignored for | |\n| | replication purposes, or an empty | |\n| | string for none, as specified in the | |\n| | IGNORE_SERVER_IDS option of the | |\n| | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Master_Server | The master\'s server_id value. | |\n| Id | | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_Cr | The MASTER_SSL_CRL option of the | |\n| | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Master_SSL_Cr | The MASTER_SSL_CRLPATH option of the | |\n| path | CHANGE MASTER TO statement. | |\n+---------------+---------------------------------------+-------------------+\n| Using_Gtid | Whether or not global transaction | |\n| | ID\'s are being used for replication | |\n| | (can be No, Slave_Pos, or | |\n| | Current_Pos). | |\n+---------------+---------------------------------------+-------------------+\n| Gtid_IO_Pos | Current global transaction ID value. | |\n+---------------+---------------------------------------+-------------------+\n| Retried_trans | Number of retried transactions for | |\n| ctions | this connection. Returned with SHOW | |\n| | ALL SLAVES STATUS only. | |\n+---------------+---------------------------------------+-------------------+\n| Max_relay_log | Max relay log size for this | |\n| size | connection. Returned with SHOW ALL | |\n| | SLAVES STATUS only. | |\n+---------------+---------------------------------------+-------------------+\n| Executed_log_ | How many log entries the replica has | |\n| ntries | executed. Returned with SHOW ALL | |\n| | SLAVES STATUS only. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_receive | How many heartbeats we have got from | |\n| _heartbeats | the master. Returned with SHOW ALL | |\n| | SLAVES STATUS only. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_heartbe | How often to request a heartbeat | |\n| t_period | packet from the master (in seconds). | |\n| | Returned with SHOW ALL SLAVES STATUS | |\n| | only. | |\n+---------------+---------------------------------------+-------------------+\n| Gtid_Slave_Po | GTID of the last event group | |\n| | replicated on a replica server, for | |\n| | each replication domain, as stored | |\n| | in the gtid_slave_pos system | |\n| | variable. Returned with SHOW ALL | |\n| | SLAVES STATUS only. | |\n+---------------+---------------------------------------+-------------------+\n| SQL_Delay | Value specified by MASTER_DELAY in | MariaDB 10.2.3 |\n| | CHANGE MASTER (or 0 if none). | |\n+---------------+---------------------------------------+-------------------+\n| SQL_Remaining | When the replica is delaying the | MariaDB 10.2.3 |\n| Delay | execution of an event due to | |\n| | MASTER_DELAY, this is the number of | |\n| | seconds of delay remaining before | |\n| | the event will be applied. | |\n| | Otherwise, the value is NULL. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_SQL_Run | The state of the SQL driver threads, | MariaDB 10.2.3 |\n| ing_State | same as in SHOW PROCESSLIST. When | |\n| | the replica is delaying the | |\n| | execution of an event due to | |\n| | MASTER_DELAY, this field displays: | |\n| | \"Waiting until MASTER_DELAY seconds | |\n| | after master executed event\". | |\n+---------------+---------------------------------------+-------------------+\n| Slave_DDL_Gro | This status variable counts the | MariaDB 10.3.7 |\n| ps | occurrence of DDL statements. This | |\n| | is a replica-side counter for | |\n| | optimistic parallel replication. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_Non_Tra | This status variable counts the | MariaDB 10.3.7 |\n| sactional_Gro | occurrence of non-transactional | |\n| ps | event groups. This is a | |\n| | replica-side counter for optimistic | |\n| | parallel replication. | |\n+---------------+---------------------------------------+-------------------+\n| Slave_Transac | This status variable counts the | MariaDB 10.3.7 |\n| ional_Groups | occurrence of transactional event | |\n| | groups. This is a replica-side | |\n| | counter for optimistic parallel | |\n| | replication. | |\n+---------------+---------------------------------------+-------------------+\n\nSHOW REPLICA STATUS\n-------------------\n\nMariaDB starting with 10.5.1\n----------------------------\nSHOW REPLICA STATUS is an alias for SHOW SLAVE STATUS from MariaDB 10.5.1.\n\nExamples\n--------\n\nIf you issue this statement using the mysql client, you can use a \\G statement\nterminator rather than a semicolon to obtain a more readable vertical layout.\n\nSHOW SLAVE STATUS\\G\n*************************** 1. row ***************************\n Slave_IO_State: Waiting for master to send event\n Master_Host: db01.example.com\n Master_User: replicant\n Master_Port: 3306\n Connect_Retry: 60\n Master_Log_File: mariadb-bin.000010\n Read_Master_Log_Pos: 548\n Relay_Log_File: relay-bin.000004\n Relay_Log_Pos: 837\n Relay_Master_Log_File: mariadb-bin.000010\n Slave_IO_Running: Yes\n Slave_SQL_Running: Yes\n Replicate_Do_DB:\n Replicate_Ignore_DB:\n Replicate_Do_Table:\n Replicate_Ignore_Table:\n Replicate_Wild_Do_Table:\n Replicate_Wild_Ignore_Table:\n Last_Errno: 0\n Last_Error:\n Skip_Counter: 0\n Exec_Master_Log_Pos: 548\n Relay_Log_Space: 1497\n Until_Condition: None\n Until_Log_File:\n Until_Log_Pos: 0\n Master_SSL_Allowed: No\n Master_SSL_CA_File:\n Master_SSL_CA_Path:\n Master_SSL_Cert:\n Master_SSL_Cipher:\n Master_SSL_Key:\n Seconds_Behind_Master: 0\nMaster_SSL_Verify_Server_Cert: No\n Last_IO_Errno: 0\n Last_IO_Error:\n Last_SQL_Errno: 0\n Last_SQL_Error:\n Replicate_Ignore_Server_Ids:\n Master_Server_Id: 101\n Master_SSL_Crl:\n Master_SSL_Crlpath:\n Using_Gtid: No\n Gtid_IO_Pos:\n\nSHOW ALL SLAVES STATUS\\G\n*************************** 1. row ***************************\n Connection_name:\n Slave_SQL_State: Slave has read all relay log; waiting for the\nslave I/O thread to update it\n Slave_IO_State: Waiting for master to send event\n Master_Host: db01.example.com\n Master_User: replicant\n Master_Port: 3306\n Connect_Retry: 60\n Master_Log_File: mariadb-bin.000010\n Read_Master_Log_Pos: 3608\n Relay_Log_File: relay-bin.000004\n Relay_Log_Pos: 3897\n Relay_Master_Log_File: mariadb-bin.000010\n Slave_IO_Running: Yes\n Slave_SQL_Running: Yes\n Replicate_Do_DB:\n Replicate_Ignore_DB:\n Replicate_Do_Table:\n Replicate_Ignore_Table:\n Replicate_Wild_Do_Table:\n Replicate_Wild_Ignore_Table:\n Last_Errno: 0\n Last_Error:\n Skip_Counter: 0\n Exec_Master_Log_Pos: 3608\n Relay_Log_Space: 4557\n Until_Condition: None\n Until_Log_File:\n Until_Log_Pos: 0\n Master_SSL_Allowed: No\n Master_SSL_CA_File:\n Master_SSL_CA_Path:\n Master_SSL_Cert:\n Master_SSL_Cipher:\n Master_SSL_Key:\n Seconds_Behind_Master: 0\nMaster_SSL_Verify_Server_Cert: No\n Last_IO_Errno: 0\n Last_IO_Error:\n Last_SQL_Errno: 0\n Last_SQL_Error:\n Replicate_Ignore_Server_Ids:\n Master_Server_Id: 101\n Master_SSL_Crl:\n Master_SSL_Crlpath:\n Using_Gtid: No\n Gtid_IO_Pos:\n Retried_transactions: 0\n Max_relay_log_size: 104857600\n Executed_log_entries: 40\n Slave_received_heartbeats: 11\n Slave_heartbeat_period: 1800.000\n Gtid_Slave_Pos: 0-101-2320\n\nYou can also access some of the variables directly from status variables:\n\nSET @@default_master_connection=\"test\" ;\nshow status like \"%slave%\"\n\nVariable_name Value\nCom_show_slave_hosts 0\nCom_show_slave_status 0\nCom_start_all_slaves 0\nCom_start_slave 0\nCom_stop_all_slaves 0\nCom_stop_slave 0\nRpl_semi_sync_slave_status OFF\nSlave_connections 0\nSlave_heartbeat_period 1800.000\nSlave_open_temp_tables 0\nSlave_received_heartbeats 0\nSlave_retried_transactions 0\nSlave_running OFF\nSlaves_connected 0\nSlaves_running 1\n\nURL: https://mariadb.com/kb/en/show-replica-status/') WHERE help_topic_id = 346; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (347,26,'SHOW MASTER STATUS','Syntax\n------\n\nSHOW MASTER STATUS\nSHOW BINLOG STATUS -- From MariaDB 10.5.2\n\nDescription\n-----------\n\nProvides status information about the binary log files of the primary.\n\nThis statement requires the SUPER privilege, the REPLICATION_CLIENT privilege,\nor, from MariaDB 10.5.2, the BINLOG MONITOR privilege.\n\nTo see information about the current GTIDs in the binary log, use the\ngtid_binlog_pos variable.\n\nSHOW MASTER STATUS was renamed to SHOW BINLOG STATUS in MariaDB 10.5.2, but\nthe old name remains an alias for compatibility purposes.\n\nExample\n-------\n\nSHOW MASTER STATUS;\n+--------------------+----------+--------------+------------------+\n| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |\n+--------------------+----------+--------------+------------------+\n| mariadb-bin.000016 | 475 | | |\n+--------------------+----------+--------------+------------------+\nSELECT @@global.gtid_binlog_pos;\n+--------------------------+\n| @@global.gtid_binlog_pos |\n+--------------------------+\n| 0-1-2 |\n+--------------------------+\n\nURL: https://mariadb.com/kb/en/show-binlog-status/','','https://mariadb.com/kb/en/show-binlog-status/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (348,26,'SHOW SLAVE HOSTS','Syntax\n------\n\nSHOW SLAVE HOSTS\nSHOW REPLICA HOSTS -- from MariaDB 10.5.1\n\nDescription\n-----------\n\nThis command is run on the primary and displays a list of replicas that are\ncurrently registered with it. Only replicas started with the\n--report-host=host_name option are visible in this list.\n\nThe list is displayed on any server (not just the primary server). The output\nlooks like this:\n\nSHOW SLAVE HOSTS;\n+------------+-----------+------+-----------+\n| Server_id | Host | Port | Master_id |\n+------------+-----------+------+-----------+\n| 192168010 | iconnect2 | 3306 | 192168011 |\n| 1921680101 | athena | 3306 | 192168011 |\n+------------+-----------+------+-----------+\n\n* Server_id: The unique server ID of the replica server, as configured in the\nserver\'s option file, or on the command line with --server-id=value.\n* Host: The host name of the replica server, as configured in the server\'s\noption file, or on the command line with --report-host=host_name. Note that\nthis can differ from the machine name as configured in the operating system.\n* Port: The port the replica server is listening on.\n* Master_id: The unique server ID of the primary server that the replica\nserver is replicating from.\n\nSome MariaDB and MySQL versions report another variable, rpl_recovery_rank.\nThis variable was never used, and was eventually removed in MariaDB 10.1.2 .\n\nRequires the REPLICATION MASTER ADMIN privilege (>= MariaDB 10.5.2) or the\nREPLICATION SLAVE privilege (<= MariaDB 10.5.1).\n\nSHOW REPLICA HOSTS\n------------------\n\nMariaDB starting with 10.5.1\n----------------------------\nSHOW REPLICA HOSTS is an alias for SHOW SLAVE HOSTS from MariaDB 10.5.1.\n\nURL: https://mariadb.com/kb/en/show-replica-hosts/','','https://mariadb.com/kb/en/show-replica-hosts/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (348,26,'SHOW SLAVE HOSTS','Syntax\n------\n\nSHOW SLAVE HOSTS\nSHOW REPLICA HOSTS -- from MariaDB 10.5.1\n\nDescription\n-----------\n\nThis command is run on the primary and displays a list of replicas that are\ncurrently registered with it. Only replicas started with the\n--report-host=host_name option are visible in this list.\n\nThe output looks like this:\n\nSHOW SLAVE HOSTS;\n+------------+-----------+------+-----------+\n| Server_id | Host | Port | Master_id |\n+------------+-----------+------+-----------+\n| 192168010 | iconnect2 | 3306 | 192168011 |\n| 1921680101 | athena | 3306 | 192168011 |\n+------------+-----------+------+-----------+\n\n* Server_id: The unique server ID of the replica server, as configured in the\nserver\'s option file, or on the command line with --server-id=value.\n* Host: The host name of the replica server, as configured in the server\'s\noption file, or on the command line with --report-host=host_name. Note that\nthis can differ from the machine name as configured in the operating system.\n* Port: The port the replica server is listening on.\n* Master_id: The unique server ID of the primary server that the replica\nserver is replicating from.\n\nSome MariaDB and MySQL versions report another variable, rpl_recovery_rank.\nThis variable was never used, and was eventually removed in MariaDB 10.1.2 .\n\nRequires the REPLICATION MASTER ADMIN privilege (>= MariaDB 10.5.2) or the\nREPLICATION SLAVE privilege (<= MariaDB 10.5.1).\n\nSHOW REPLICA HOSTS\n------------------\n\nMariaDB starting with 10.5.1\n----------------------------\nSHOW REPLICA HOSTS is an alias for SHOW SLAVE HOSTS from MariaDB 10.5.1.\n\nURL: https://mariadb.com/kb/en/show-replica-hosts/','','https://mariadb.com/kb/en/show-replica-hosts/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (349,26,'SHOW PLUGINS','Syntax\n------\n\nSHOW PLUGINS;\n\nDescription\n-----------\n\nSHOW PLUGINS displays information about installed plugins. The Library column\nindicates the plugin library - if it is NULL, the plugin is built-in and\ncannot be uninstalled.\n\nThe PLUGINS table in the information_schema database contains more detailed\ninformation.\n\nFor specific information about storage engines (a particular type of plugin),\nsee the information_schema.ENGINES table and the SHOW ENGINES statement.\n\nExamples\n--------\n\nSHOW PLUGINS;\n+----------------------------+----------+--------------------+-------------+---\n-----+\n| Name | Status | Type | Library |\nLicense |\n+----------------------------+----------+--------------------+-------------+---\n-----+\n| binlog | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| mysql_native_password | ACTIVE | AUTHENTICATION | NULL |\nGPL |\n| mysql_old_password | ACTIVE | AUTHENTICATION | NULL |\nGPL |\n| MRG_MyISAM | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| MyISAM | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| CSV | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| MEMORY | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| FEDERATED | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| PERFORMANCE_SCHEMA | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| Aria | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| InnoDB | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| INNODB_TRX | ACTIVE | INFORMATION SCHEMA | NULL |\nGPL |\n...\n| INNODB_SYS_FOREIGN | ACTIVE | INFORMATION SCHEMA | NULL |\nGPL |\n| INNODB_SYS_FOREIGN_COLS | ACTIVE | INFORMATION SCHEMA | NULL |\nGPL |\n| SPHINX | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| ARCHIVE | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| BLACKHOLE | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| FEEDBACK | DISABLED | INFORMATION SCHEMA | NULL |\nGPL |\n| partition | ACTIVE | STORAGE ENGINE | NULL |\nGPL |\n| pam | ACTIVE | AUTHENTICATION | auth_pam.so |\nGPL |\n+----------------------------+----------+--------------------+-------------+---\n-----+\n\nURL: https://mariadb.com/kb/en/show-plugins/','','https://mariadb.com/kb/en/show-plugins/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (350,26,'SHOW PLUGINS SONAME','Syntax\n------\n\nSHOW PLUGINS SONAME { library | LIKE \'pattern\' | WHERE expr };\n\nDescription\n-----------\n\nSHOW PLUGINS SONAME displays information about compiled-in and all server\nplugins in the plugin_dir directory, including plugins that haven\'t been\ninstalled.\n\nExamples\n--------\n\nSHOW PLUGINS SONAME \'ha_example.so\';\n+----------+---------------+----------------+---------------+---------+\n| Name | Status | Type | Library | License |\n+----------+---------------+----------------+---------------+---------+\n| EXAMPLE | NOT INSTALLED | STORAGE ENGINE | ha_example.so | GPL |\n| UNUSABLE | NOT INSTALLED | DAEMON | ha_example.so | GPL |\n+----------+---------------+----------------+---------------+---------+\n\nThere is also a corresponding information_schema table, called ALL_PLUGINS,\nwhich contains more complete information.\n\nURL: https://mariadb.com/kb/en/show-plugins-soname/','','https://mariadb.com/kb/en/show-plugins-soname/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (351,26,'SET','Syntax\n------\n\nSET variable_assignment [, variable_assignment] ...\n\nvariable_assignment:\n user_var_name = expr\n | [GLOBAL | SESSION] system_var_name = expr\n | [@@global. | @@session. | @@]system_var_name = expr\n\nOne can also set a user variable in any expression with this syntax:\n\nuser_var_name:= expr\n\nDescription\n-----------\n\nThe SET statement assigns values to different types of variables that affect\nthe operation of the server or your client. Older versions of MySQL employed\nSET OPTION, but this syntax was deprecated in favor of SET without OPTION, and\nwas removed in MariaDB 10.0.\n\nChanging a system variable by using the SET statement does not make the change\npermanently. To do so, the change must be made in a configuration file.\n\nFor setting variables on a per-query basis, see SET STATEMENT.\n\nSee SHOW VARIABLES for documentation on viewing server system variables.\n\nSee Server System Variables for a list of all the system variables.\n\nGLOBAL / SESSION\n----------------\n\nWhen setting a system variable, the scope can be specified as either GLOBAL or\nSESSION.\n\nA global variable change affects all new sessions. It does not affect any\ncurrently open sessions, including the one that made the change.\n\nA session variable change affects the current session only.\n\nIf the variable has a session value, not specifying either GLOBAL or SESSION\nwill be the same as specifying SESSION. If the variable only has a global\nvalue, not specifying GLOBAL or SESSION will apply to the change to the global\nvalue.\n\nDEFAULT\n-------\n\nSetting a global variable to DEFAULT will restore it to the server default,\nand setting a session variable to DEFAULT will restore it to the current\nglobal value.\n\nExamples\n--------\n\n* innodb_sync_spin_loops is a global variable.\n* skip_parallel_replication is a session variable.\n* max_error_count is both global and session.\n\nSELECT VARIABLE_NAME, SESSION_VALUE, GLOBAL_VALUE FROM\n INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE \n VARIABLE_NAME IN (\'max_error_count\', \'skip_parallel_replication\',\n\'innodb_sync_spin_loops\');\n+---------------------------+---------------+--------------+\n| VARIABLE_NAME | SESSION_VALUE | GLOBAL_VALUE |\n+---------------------------+---------------+--------------+\n| MAX_ERROR_COUNT | 64 | 64 |\n| SKIP_PARALLEL_REPLICATION | OFF | NULL |\n| INNODB_SYNC_SPIN_LOOPS | NULL | 30 |\n+---------------------------+---------------+--------------+\n\nSetting the session values:\n\nSET max_error_count=128;Query OK, 0 rows affected (0.000 sec)\n\nSET skip_parallel_replication=ON;Query OK, 0 rows affected (0.000 sec)\n\nSET innodb_sync_spin_loops=60;\nERROR 1229 (HY000): Variable \'innodb_sync_spin_loops\' is a GLOBAL variable \n and should be set with SET GLOBAL\n\nSELECT VARIABLE_NAME, SESSION_VALUE, GLOBAL_VALUE FROM\n INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE \n VARIABLE_NAME IN (\'max_error_count\', \'skip_parallel_replication\',\n\'innodb_sync_spin_loops\');\n+---------------------------+---------------+--------------+\n| VARIABLE_NAME | SESSION_VALUE | GLOBAL_VALUE |\n+---------------------------+---------------+--------------+\n| MAX_ERROR_COUNT | 128 | 64 |\n| SKIP_PARALLEL_REPLICATION | ON | NULL |\n| INNODB_SYNC_SPIN_LOOPS | NULL | 30 |\n+---------------------------+---------------+--------------+\n\nSetting the global values:\n\nSET GLOBAL max_error_count=256;\n\nSET GLOBAL skip_parallel_replication=ON;\nERROR 1228 (HY000): Variable \'skip_parallel_replication\' is a SESSION variable \n and can\'t be used with SET GLOBAL\n\nSET GLOBAL innodb_sync_spin_loops=120;\n\nSELECT VARIABLE_NAME, SESSION_VALUE, GLOBAL_VALUE FROM\n INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE \n VARIABLE_NAME IN (\'max_error_count\', \'skip_parallel_replication\',\n\'innodb_sync_spin_loops\');\n+---------------------------+---------------+--------------+\n| VARIABLE_NAME | SESSION_VALUE | GLOBAL_VALUE |\n+---------------------------+---------------+--------------+\n| MAX_ERROR_COUNT | 128 | 256 |\n| SKIP_PARALLEL_REPLICATION | ON | NULL |\n| INNODB_SYNC_SPIN_LOOPS | NULL | 120 |\n+---------------------------+---------------+--------------+\n\nSHOW VARIABLES will by default return the session value unless the variable is\nglobal only.\n\nSHOW VARIABLES LIKE \'max_error_count\';\n+-----------------+-------+\n| Variable_name | Value |\n+-----------------+-------+\n| max_error_count | 128 |\n+-----------------+-------+\n\nSHOW VARIABLES LIKE \'skip_parallel_replication\';\n+---------------------------+-------+\n| Variable_name | Value |\n+---------------------------+-------+\n| skip_parallel_replication | ON |\n+---------------------------+-------+\n\nSHOW VARIABLES LIKE \'innodb_sync_spin_loops\';\n+------------------------+-------+\n| Variable_name | Value |\n+------------------------+-------+\n| innodb_sync_spin_loops | 120 |\n+------------------------+-------+\n\nUsing the inplace syntax:\n\nSELECT (@a:=1);\n+---------+\n| (@a:=1) |\n+---------+\n| 1 |\n+---------+\n\nSELECT @a;\n+------+\n| @a |\n+------+\n| 1 |\n+------+\n\nURL: https://mariadb.com/kb/en/set/','','https://mariadb.com/kb/en/set/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (352,26,'SET CHARACTER SET','Syntax\n------\n\nSET {CHARACTER SET | CHARSET}\n {charset_name | DEFAULT}\n\nDescription\n-----------\n\nSets the character_set_client and character_set_results session system\nvariables to the specified character set and collation_connection to the value\nof collation_database, which implicitly sets character_set_connection to the\nvalue of character_set_database.\n\nThis maps all strings sent between the current client and the server with the\ngiven mapping.\n\nExample\n-------\n\nSHOW VARIABLES LIKE \'character_set\\_%\';\n+--------------------------+--------+\n| Variable_name | Value |\n+--------------------------+--------+\n| character_set_client | utf8 |\n| character_set_connection | utf8 |\n| character_set_database | latin1 |\n| character_set_filesystem | binary |\n| character_set_results | utf8 |\n| character_set_server | latin1 |\n| character_set_system | utf8 |\n+--------------------------+--------+\n\nSHOW VARIABLES LIKE \'collation%\';\n+----------------------+-------------------+\n| Variable_name | Value |\n+----------------------+-------------------+\n| collation_connection | utf8_general_ci |\n| collation_database | latin1_swedish_ci |\n| collation_server | latin1_swedish_ci |\n+----------------------+-------------------+\n\nSET CHARACTER SET utf8mb4;\n\nSHOW VARIABLES LIKE \'character_set\\_%\';\n+--------------------------+---------+\n| Variable_name | Value |\n+--------------------------+---------+\n| character_set_client | utf8mb4 |\n| character_set_connection | latin1 |\n| character_set_database | latin1 |\n| character_set_filesystem | binary |\n| character_set_results | utf8mb4 |\n| character_set_server | latin1 |\n| character_set_system | utf8 |\n+--------------------------+---------+\n\nSHOW VARIABLES LIKE \'collation%\';\n+----------------------+-------------------+\n| Variable_name | Value |\n+----------------------+-------------------+\n| collation_connection | latin1_swedish_ci |\n| collation_database | latin1_swedish_ci |\n| collation_server | latin1_swedish_ci |\n+----------------------+-------------------+\n\nURL: https://mariadb.com/kb/en/set-character-set/','','https://mariadb.com/kb/en/set-character-set/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (353,26,'SET NAMES','Syntax\n------\n\nSET NAMES {\'charset_name\'\n [COLLATE \'collation_name\'] | DEFAULT}\n\nDescription\n-----------\n\nSets the character_set_client, character_set_connection, character_set_results\nand, implicitly, the collation_connection session system variables to the\nspecified character set and collation.\n\nThis determines which character set the client will use to send statements to\nthe server, and the server will use for sending results back to the client.\n\nucs2, utf16, and utf32 are not valid character sets for SET NAMES, as they\ncannot be used as client character sets.\n\nThe collation clause is optional. If not defined (or if DEFAULT is specified),\nthe default collation for the character set will be used.\n\nQuotes are optional for the character set or collation clauses.\n\nExamples\n--------\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+-----------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+-----------------+\n| CHARACTER_SET_RESULTS | utf8 |\n| CHARACTER_SET_CONNECTION | utf8 |\n| CHARACTER_SET_CLIENT | utf8 |\n| COLLATION_CONNECTION | utf8_general_ci |\n+--------------------------+-----------------+\n\nSET NAMES big5;\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+-----------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+-----------------+\n| CHARACTER_SET_RESULTS | big5 |\n| CHARACTER_SET_CONNECTION | big5 |\n| CHARACTER_SET_CLIENT | big5 |\n| COLLATION_CONNECTION | big5_chinese_ci |\n+--------------------------+-----------------+\n\nSET NAMES \'latin1\' COLLATE \'latin1_bin\';\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+---------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+---------------+\n| CHARACTER_SET_RESULTS | latin1 |\n| CHARACTER_SET_CONNECTION | latin1 |\n| CHARACTER_SET_CLIENT | latin1 |\n| COLLATION_CONNECTION | latin1_bin |\n+--------------------------+---------------+\n\nSET NAMES DEFAULT;\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+-------------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+-------------------+\n| CHARACTER_SET_RESULTS | latin1 |\n| CHARACTER_SET_CONNECTION | latin1 |\n| CHARACTER_SET_CLIENT | latin1 |\n| COLLATION_CONNECTION | latin1_swedish_ci |\n+--------------------------+-------------------+\n\nURL: https://mariadb.com/kb/en/set-names/','','https://mariadb.com/kb/en/set-names/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (353,26,'SET NAMES','Syntax\n------\n\nSET NAMES {\'charset_name\'\n [COLLATE \'collation_name\'] | DEFAULT}\n\nDescription\n-----------\n\nSets the character_set_client, character_set_connection, character_set_results\nand, implicitly, the collation_connection session system variables to the\nspecified character set and collation.\n\nThis determines which character set the client will use to send statements to\nthe server, and the server will use for sending results back to the client.\n\nucs2, utf16, utf16le and utf32 are not valid character sets for SET NAMES, as\nthey cannot be used as client character sets.\n\nThe collation clause is optional. If not defined (or if DEFAULT is specified),\nthe default collation for the character set will be used.\n\nQuotes are optional for the character set or collation clauses.\n\nExamples\n--------\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+-----------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+-----------------+\n| CHARACTER_SET_RESULTS | utf8 |\n| CHARACTER_SET_CONNECTION | utf8 |\n| CHARACTER_SET_CLIENT | utf8 |\n| COLLATION_CONNECTION | utf8_general_ci |\n+--------------------------+-----------------+\n\nSET NAMES big5;\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+-----------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+-----------------+\n| CHARACTER_SET_RESULTS | big5 |\n| CHARACTER_SET_CONNECTION | big5 |\n| CHARACTER_SET_CLIENT | big5 |\n| COLLATION_CONNECTION | big5_chinese_ci |\n+--------------------------+-----------------+\n\nSET NAMES \'latin1\' COLLATE \'latin1_bin\';\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+---------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+---------------+\n| CHARACTER_SET_RESULTS | latin1 |\n| CHARACTER_SET_CONNECTION | latin1 |\n| CHARACTER_SET_CLIENT | latin1 |\n| COLLATION_CONNECTION | latin1_bin |\n+--------------------------+---------------+\n\nSET NAMES DEFAULT;\n\nSELECT VARIABLE_NAME, SESSION_VALUE \n FROM INFORMATION_SCHEMA.SYSTEM_VARIABLES WHERE\n VARIABLE_NAME LIKE \'character_set_c%\' OR\n VARIABLE_NAME LIKE \'character_set_re%\' OR\n VARIABLE_NAME LIKE \'collation_c%\';\n+--------------------------+-------------------+\n| VARIABLE_NAME | SESSION_VALUE |\n+--------------------------+-------------------+\n| CHARACTER_SET_RESULTS | latin1 |\n| CHARACTER_SET_CONNECTION | latin1 |\n| CHARACTER_SET_CLIENT | latin1 |\n| COLLATION_CONNECTION | latin1_swedish_ci |\n+--------------------------+-------------------+\n\nURL: https://mariadb.com/kb/en/set-names/','','https://mariadb.com/kb/en/set-names/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (354,26,'SET SQL_LOG_BIN','Syntax\n------\n\nSET [SESSION] sql_log_bin = {0|1}\n\nDescription\n-----------\n\nSets the sql_log_bin system variable, which disables or enables binary logging\nfor the current connection, if the client has the SUPER privilege. The\nstatement is refused with an error if the client does not have that privilege.\n\nBefore MariaDB 5.5 and before MySQL 5.6 one could also set sql_log_bin as a\nglobal variable. This was disabled as this was too dangerous as it could\ndamage replication.\n\nURL: https://mariadb.com/kb/en/set-sql_log_bin/','','https://mariadb.com/kb/en/set-sql_log_bin/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (355,26,'SET STATEMENT','MariaDB starting with 10.1.2\n----------------------------\nPer-query variables were introduced in MariaDB 10.1.2\n\nSET STATEMENT can be used to set the value of a system variable for the\nduration of the statement. It is also possible to set multiple variables.\n\nSyntax\n------\n\nSET STATEMENT var1=value1 [, var2=value2, ...] \n FOR \n\nwhere varN is a system variable (list of allowed variables is provided below),\nand valueN is a constant literal.\n\nDescription\n-----------\n\nSET STATEMENT var1=value1 FOR stmt\n\nis roughly equivalent to\n\nSET @save_value=@@var1;\nSET SESSION var1=value1;\nstmt;\nSET SESSION var1=@save_value;\n\nThe server parses the whole statement before executing it, so any variables\nset in this fashion that affect the parser may not have the expected effect.\nExamples include the charset variables, sql_mode=ansi_quotes, etc.\n\nExamples\n--------\n\nOne can limit statement execution time max_statement_time:\n\nSET STATEMENT max_statement_time=1000 FOR SELECT ... ;\n\nOne can switch on/off individual optimizations:\n\nSET STATEMENT optimizer_switch=\'materialization=off\' FOR SELECT ....;\n\nIt is possible to enable MRR/BKA for a query:\n\nSET STATEMENT join_cache_level=6, optimizer_switch=\'mrr=on\' FOR SELECT ...\n\nNote that it makes no sense to try to set a session variable inside a SET\nSTATEMENT:\n\n#USELESS STATEMENT\nSET STATEMENT sort_buffer_size = 100000 for SET SESSION sort_buffer_size =\n200000;\n\nFor the above, after setting sort_buffer_size to 200000 it will be reset to\nits original state (the state before the SET STATEMENT started) after the\nstatement execution.\n\nLimitations\n-----------\n\nThere are a number of variables that cannot be set on per-query basis. These\ninclude:\n\n* autocommit\n* character_set_client\n* character_set_connection\n* character_set_filesystem\n* collation_connection\n* default_master_connection\n* debug_sync\n* interactive_timeout\n* gtid_domain_id\n* last_insert_id\n* log_slow_filter\n* log_slow_rate_limit\n* log_slow_verbosity\n* long_query_time\n* min_examined_row_limit\n* profiling\n* profiling_history_size\n* query_cache_type\n* rand_seed1\n* rand_seed2\n* skip_replication\n* slow_query_log\n* sql_log_off\n* tx_isolation\n* wait_timeout\n\nSource\n------\n\n* The feature was originally implemented as a Google Summer of Code 2009\nproject by Joseph Lukas. \n* Percona Server 5.6 included it as Per-query variable statement\n* MariaDB ported the patch and fixed many bugs. The task in MariaDB Jira is\nMDEV-5231.\n\nURL: https://mariadb.com/kb/en/set-statement/','','https://mariadb.com/kb/en/set-statement/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (356,26,'SET Variable','Syntax\n------\n\nSET var_name = expr [, var_name = expr] ...\n\nDescription\n-----------\n\nThe SET statement in stored programs is an extended version of the general SET\nstatement. Referenced variables may be ones declared inside a stored program,\nglobal system variables, or user-defined variables.\n\nThe SET statement in stored programs is implemented as part of the\npre-existing SET syntax. This allows an extended syntax of SET a=x, b=y, ...\nwhere different variable types (locally declared variables, global and session\nserver variables, user-defined variables) can be mixed. This also allows\ncombinations of local variables and some options that make sense only for\nsystem variables; in that case, the options are recognized but ignored.\n\nSET can be used with both local variables and user-defined variables.\n\nWhen setting several variables using the columns returned by a query, SELECT\nINTO should be preferred.\n\nTo set many variables to the same value, the LAST_VALUE( ) function can be\nused.\n\nBelow is an example of how a user-defined variable may be set:\n\nSET @x = 1;\n\nURL: https://mariadb.com/kb/en/set-variable/','','https://mariadb.com/kb/en/set-variable/'); @@ -523,7 +523,7 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (425,27,'Subqueries and JOINs','A subquery can quite often, but not in all cases, be rewritten as a JOIN.\n\nRewriting Subqueries as JOINS\n-----------------------------\n\nA subquery using IN can be rewritten with the DISTINCT keyword, for example:\n\nSELECT * FROM table1 WHERE col1 IN (SELECT col1 FROM table2);\n\ncan be rewritten as:\n\nSELECT DISTINCT table1.* FROM table1, table2 WHERE table1.col1=table2.col1;\n\nNOT IN or NOT EXISTS queries can also be rewritten. For example, these two\nqueries returns the same result:\n\nSELECT * FROM table1 WHERE col1 NOT IN (SELECT col1 FROM table2);\nSELECT * FROM table1 WHERE NOT EXISTS (SELECT col1 FROM table2 WHERE\ntable1.col1=table2.col1);\n\nand both can be rewritten as:\n\nSELECT table1.* FROM table1 LEFT JOIN table2 ON table1.id=table2.id WHERE\ntable2.id IS NULL;\n\nSubqueries that can be rewritten as a LEFT JOIN are sometimes more efficient.\n\nUsing Subqueries instead of JOINS\n---------------------------------\n\nThere are some scenarios, though, which call for subqueries rather than joins:\n\n* When you want duplicates, but not false duplicates. Suppose Table_1\n has three rows — {1,1,2}\n — and Table_2 has two rows\n — {1,2,2}. If you need to list the rows\n in Table_1 which are also in Table_2, only this\n subquery-based SELECT statement will give the right answer\n (1,1,2):\n\nSELECT Table_1.column_1 \nFROM Table_1 \nWHERE Table_1.column_1 IN \n (SELECT Table_2.column_1\n FROM Table_2);\n\n* This SQL statement won\'t work:\n\nSELECT Table_1.column_1 \nFROM Table_1,Table_2 \nWHERE Table_1.column_1 = Table_2.column_1;\n\n* because the result will be {1,1,2,2}\n — and the duplication of 2 is an error. This SQL\n statement won\'t work either:\n\nSELECT DISTINCT Table_1.column_1 \nFROM Table_1,Table_2 \nWHERE Table_1.column_1 = Table_2.column_1;\n\n* because the result will be {1,2} — and\n the removal of the duplicated 1 is an error too.\n\n* When the outermost statement is not a query. The SQL statement:\n\nUPDATE Table_1 SET column_1 = (SELECT column_1 FROM Table_2);\n\n* can\'t be expressed using a join unless some rare SQL3 features are used.\n\n* When the join is over an expression. The SQL statement:\n\nSELECT * FROM Table_1 \nWHERE column_1 + 5 =\n (SELECT MAX(column_1) FROM Table_2);\n\n* is hard to express with a join. In fact, the only way we can think of is\n this SQL statement:\n\nSELECT Table_1.*\nFROM Table_1, \n (SELECT MAX(column_1) AS max_column_1 FROM Table_2) AS Table_2\nWHERE Table_1.column_1 + 5 = Table_2.max_column_1;\n\n* which still involves a parenthesized query, so nothing is gained from the\n transformation.\n\n* When you want to see the exception. For example, suppose the question is:\n what books are longer than Das Kapital? These two queries are effectively\n almost the same:\n\nSELECT DISTINCT Bookcolumn_1.* \nFROM Books AS Bookcolumn_1 JOIN Books AS Bookcolumn_2 USING(page_count) \nWHERE title = \'Das Kapital\';\n\nSELECT DISTINCT Bookcolumn_1.* \nFROM Books AS Bookcolumn_1 \nWHERE Bookcolumn_1.page_count > \n (SELECT DISTINCT page_count\n FROM Books AS Bookcolumn_2\n WHERE title = \'Das Kapital\');\n\n* The difference is between these two SQL statements is, if there are two\n editions of Das Kapital (with different page counts), then the self-join\n example will return the books which are longer than the shortest edition\n of Das Kapital. That might be the wrong answer, since the original\n question didn\'t ask for \"... longer than ANY book named Das Kapital\"\n (it seems to contain a false assumption that there\'s only one edition).\n\nURL: https://mariadb.com/kb/en/subqueries-and-joins/','','https://mariadb.com/kb/en/subqueries-and-joins/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (426,27,'Subquery Limitations','There are a number of limitations regarding subqueries, which are discussed\nbelow. The following tables and data will be used in the examples that follow:\n\nCREATE TABLE staff(name VARCHAR(10),age TINYINT);\n\nCREATE TABLE customer(name VARCHAR(10),age TINYINT);\n\nINSERT INTO staff VALUES \n(\'Bilhah\',37), (\'Valerius\',61), (\'Maia\',25);\n\nINSERT INTO customer VALUES \n(\'Thanasis\',48), (\'Valerius\',61), (\'Brion\',51);\n\nORDER BY and LIMIT\n------------------\n\nTo use ORDER BY or limit LIMIT in subqueries both must be used.. For example:\n\nSELECT * FROM staff WHERE name IN (SELECT name FROM customer ORDER BY name);\n+----------+------+\n| name | age |\n+----------+------+\n| Valerius | 61 |\n+----------+------+\n\nis valid, but\n\nSELECT * FROM staff WHERE name IN (SELECT NAME FROM customer ORDER BY name\nLIMIT 1);\nERROR 1235 (42000): This version of MariaDB doesn\'t \n yet support \'LIMIT & IN/ALL/ANY/SOME subquery\'\n\nis not.\n\nModifying and Selecting from the Same Table\n-------------------------------------------\n\nIt\'s not possible to both modify and select from the same table in a subquery.\nFor example:\n\nDELETE FROM staff WHERE name = (SELECT name FROM staff WHERE age=61);\nERROR 1093 (HY000): Table \'staff\' is specified twice, both \n as a target for \'DELETE\' and as a separate source for data\n\nRow Comparison Operations\n-------------------------\n\nThere is only partial support for row comparison operations. The expression in\n\nexpr op {ALL|ANY|SOME} subquery,\n\nmust be scalar and the subquery can only return a single column.\n\nHowever, because of the way IN is implemented (it is rewritten as a sequence\nof = comparisons and AND), the expression in\n\nexpression [NOT] IN subquery\n\nis permitted to be an n-tuple and the subquery can return rows of n-tuples.\n\nFor example:\n\nSELECT * FROM staff WHERE (name,age) NOT IN (\n SELECT name,age FROM customer WHERE age >=51]\n);\n+--------+------+\n| name | age |\n+--------+------+\n| Bilhah | 37 |\n| Maia | 25 |\n+--------+------+\n\nis permitted, but\n\nSELECT * FROM staff WHERE (name,age) = ALL (\n SELECT name,age FROM customer WHERE age >=51\n);\nERROR 1241 (21000): Operand should contain 1 column(s)\n\nis not.\n\nCorrelated Subqueries\n---------------------\n\nSubqueries in the FROM clause cannot be correlated subqueries. They cannot be\nevaluated for each row of the outer query since they are evaluated to produce\na result set during when the query is executed.\n\nStored Functions\n----------------\n\nA subquery can refer to a stored function which modifies data. This is an\nextension to the SQL standard, but can result in indeterminate outcomes. For\nexample, take:\n\nSELECT ... WHERE x IN (SELECT f() ...);\n\nwhere f() inserts rows. The function f() could be executed a different number\nof times depending on how the optimizer chooses to handle the query.\n\nThis sort of construct is therefore not safe to use in replication that is not\nrow-based, as there could be different results on the master and the slave.\n\nURL: https://mariadb.com/kb/en/subquery-limitations/','','https://mariadb.com/kb/en/subquery-limitations/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (427,27,'UNION','UNION is used to combine the results from multiple SELECT statements into a\nsingle result set.\n\nSyntax\n------\n\nSELECT ...\nUNION [ALL | DISTINCT] SELECT ...\n[UNION [ALL | DISTINCT] SELECT ...]\n[ORDER BY [column [, column ...]]]\n[LIMIT {[offset,] row_count | row_count OFFSET offset}]\n\nDescription\n-----------\n\nUNION is used to combine the results from multiple SELECT statements into a\nsingle result set.\n\nThe column names from the first SELECT statement are used as the column names\nfor the results returned. Selected columns listed in corresponding positions\nof each SELECT statement should have the same data type. (For example, the\nfirst column selected by the first statement should have the same type as the\nfirst column selected by the other statements.)\n\nIf they don\'t, the type and length of the columns in the result take into\naccount the values returned by all of the SELECTs, so there is no need for\nexplicit casting. Note that currently this is not the case for recursive CTEs\n- see MDEV-12325.\n\nTable names can be specified as db_name.tbl_name. This permits writing UNIONs\nwhich involve multiple databases. See Identifier Qualifiers for syntax details.\n\nUNION queries cannot be used with aggregate functions.\n\nEXCEPT and UNION have the same operation precedence and INTERSECT has a higher\nprecedence, unless running in Oracle mode, in which case all three have the\nsame precedence.\n\nALL/DISTINCT\n------------\n\nThe ALL keyword causes duplicate rows to be preserved. The DISTINCT keyword\n(the default if the keyword is omitted) causes duplicate rows to be removed by\nthe results.\n\nUNION ALL and UNION DISTINCT can both be present in a query. In this case,\nUNION DISTINCT will override any UNION ALLs to its left.\n\nMariaDB starting with 10.1.1\n----------------------------\nUntil MariaDB 10.1.1, all UNION ALL statements required the server to create a\ntemporary table. Since MariaDB 10.1.1, the server can in most cases execute\nUNION ALL without creating a temporary table, improving performance (see\nMDEV-334).\n\nORDER BY and LIMIT\n------------------\n\nIndividual SELECTs can contain their own ORDER BY and LIMIT clauses. In this\ncase, the individual queries need to be wrapped between parentheses. However,\nthis does not affect the order of the UNION, so they only are useful to limit\nthe record read by one SELECT.\n\nThe UNION can have global ORDER BY and LIMIT clauses, which affect the whole\nresultset. If the columns retrieved by individual SELECT statements have an\nalias (AS), the ORDER BY must use that alias, not the real column names.\n\nHIGH_PRIORITY\n-------------\n\nSpecifying a query as HIGH_PRIORITY will not work inside a UNION. If applied\nto the first SELECT, it will be ignored. Applying to a later SELECT results in\na syntax error:\n\nERROR 1234 (42000): Incorrect usage/placement of \'HIGH_PRIORITY\'\n\nSELECT ... INTO ...\n-------------------\n\nIndividual SELECTs cannot be written INTO DUMPFILE or INTO OUTFILE. If the\nlast SELECT statement specifies INTO DUMPFILE or INTO OUTFILE, the entire\nresult of the UNION will be written. Placing the clause after any other SELECT\nwill result in a syntax error.\n\nIf the result is a single row, SELECT ... INTO @var_name can also be used.\n\nMariaDB starting with 10.4.0\n----------------------------\n\nParentheses\n-----------\n\nFrom MariaDB 10.4.0, parentheses can be used to specify precedence. Before\nthis, a syntax error would be returned.\n\nExamples\n--------\n\nUNION between tables having different column names:\n\n(SELECT e_name AS name, email FROM employees)\nUNION\n(SELECT c_name AS name, email FROM customers);\n\nSpecifying the UNION\'s global order and limiting total rows:\n\n(SELECT name, email FROM employees)\nUNION\n(SELECT name, email FROM customers)\nORDER BY name LIMIT 10;\n\nAdding a constant row:\n\n(SELECT \'John Doe\' AS name, \'john.doe@example.net\' AS email)\nUNION\n(SELECT name, email FROM customers);\n\nDiffering types:\n\nSELECT CAST(\'x\' AS CHAR(1)) UNION SELECT REPEAT(\'y\',4);\n+----------------------+\n| CAST(\'x\' AS CHAR(1)) |\n+----------------------+\n| x |\n| yyyy |\n+----------------------+\n\nReturning the results in order of each individual SELECT by use of a sort\ncolumn:\n\n(SELECT 1 AS sort_column, e_name AS name, email FROM employees)\nUNION\n(SELECT 2, c_name AS name, email FROM customers) ORDER BY sort_column;\n\nDifference between UNION, EXCEPT and INTERSECT. INTERSECT ALL and EXCEPT ALL\nare available from MariaDB 10.5.0.\n\nCREATE TABLE seqs (i INT);\nINSERT INTO seqs VALUES (1),(2),(2),(3),(3),(4),(5),(6);\n\nSELECT i FROM seqs WHERE i <= 3 UNION SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 UNION ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n| 3 |\n| 3 |\n| 3 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n| 3 |\n+------+\n\nParentheses for specifying precedence, from MariaDB 10.4.0\n\nCREATE OR REPLACE TABLE t1 (a INT);\nCREATE OR REPLACE TABLE t2 (b INT);\nCREATE OR REPLACE TABLE t3 (c INT);\n\nINSERT INTO t1 VALUES (1),(2),(3),(4);\nINSERT INTO t2 VALUES (5),(6);\nINSERT INTO t3 VALUES (1),(6);\n\n((SELECT a FROM t1) UNION (SELECT b FROM t2)) INTERSECT (SELECT c FROM t3);\n+------+\n| a |\n+------+\n| 1 |\n| 6 |\n+------+\n\n(SELECT a FROM t1) UNION ((SELECT b FROM t2) INTERSECT (SELECT c FROM t3));\n+------+\n| a |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 6 |\n+------+\n\nURL: https://mariadb.com/kb/en/union/','','https://mariadb.com/kb/en/union/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (428,27,'EXCEPT','MariaDB starting with 10.3.0\n----------------------------\nEXCEPT was introduced in MariaDB 10.3.0.\n\nThe result of EXCEPT is all records of the left SELECT result set except\nrecords which are in right SELECT result set, i.e. it is subtraction of two\nresult sets. From MariaDB 10.6.1, MINUS is a synonym.\n\nSyntax\n------\n\nSELECT ...\n(INTERSECT [ALL | DISTINCT] | EXCEPT [ALL | DISTINCT] | UNION [ALL |\nDISTINCT]) SELECT ...\n[(INTERSECT [ALL | DISTINCT] | EXCEPT [ALL | DISTINCT] | UNION [ALL |\nDISTINCT]) SELECT ...]\n[ORDER BY [column [, column ...]]]\n[LIMIT {[offset,] row_count | row_count OFFSET offset}]\n\nPlease note:\n\n* Brackets for explicit operation precedence are not supported; use a subquery\nin the FROM clause as a workaround).\n\nDescription\n-----------\n\nMariaDB has supported EXCEPT and INTERSECT in addition to UNION since MariaDB\n10.3.\n\nAll behavior for naming columns, ORDER BY and LIMIT is the same as for UNION.\n\nEXCEPT implicitly supposes a DISTINCT operation.\n\nThe result of EXCEPT is all records of the left SELECT result except records\nwhich are in right SELECT result set, i.e. it is subtraction of two result\nsets.\n\nEXCEPT and UNION have the same operation precedence and INTERSECT has a higher\nprecedence, unless running in Oracle mode, in which case all three have the\nsame precedence.\n\nMariaDB starting with 10.4.0\n----------------------------\n\nParentheses\n-----------\n\nFrom MariaDB 10.4.0, parentheses can be used to specify precedence. Before\nthis, a syntax error would be returned.\n\nMariaDB starting with 10.5.0\n----------------------------\n\nALL/DISTINCT\n------------\n\nEXCEPT ALL and EXCEPT DISTINCT were introduced in MariaDB 10.5.0. The ALL\noperator leaves duplicates intact, while the DISTINCT operator removes\nduplicates. DISTINCT is the default behavior if neither operator is supplied,\nand the only behavior prior to MariaDB 10.5.\n\nExamples\n--------\n\nShow customers which are not employees:\n\n(SELECT e_name AS name, email FROM customers)\nEXCEPT\n(SELECT c_name AS name, email FROM employees);\n\nDifference between UNION, EXCEPT and INTERSECT. INTERSECT ALL and EXCEPT ALL\nare available from MariaDB 10.5.0.\n\nCREATE TABLE seqs (i INT);\nINSERT INTO seqs VALUES (1),(2),(2),(3),(3),(4),(5),(6);\n\nSELECT i FROM seqs WHERE i <= 3 UNION SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 UNION ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n| 3 |\n| 3 |\n| 3 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n| 3 |\n+------+\n\nParentheses for specifying precedence, from MariaDB 10.4.0\n\nCREATE OR REPLACE TABLE t1 (a INT);\nCREATE OR REPLACE TABLE t2 (b INT);\nCREATE OR REPLACE TABLE t3 (c INT);\n\nINSERT INTO t1 VALUES (1),(2),(3),(4);\nINSERT INTO t2 VALUES (5),(6);\nINSERT INTO t3 VALUES (1),(6);\n\n((SELECT a FROM t1) UNION (SELECT b FROM t2)) EXCEPT (SELECT c FROM t3);\n+------+\n| a |\n+------+\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n+------+\n\n(SELECT a FROM t1) UNION ((SELECT b FROM t2) EXCEPT (SELECT c FROM t3));\n+------+\n| a |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n+------+\n\nURL: https://mariadb.com/kb/en/except/','','https://mariadb.com/kb/en/except/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (428,27,'EXCEPT','MariaDB starting with 10.3.0\n----------------------------\nEXCEPT was introduced in MariaDB 10.3.0.\n\nThe result of EXCEPT is all records of the left SELECT result set except\nrecords which are in right SELECT result set, i.e. it is subtraction of two\nresult sets. From MariaDB 10.6.1, MINUS is a synonym.\n\nSyntax\n------\n\nSELECT ...\n(INTERSECT [ALL | DISTINCT] | EXCEPT [ALL | DISTINCT] | UNION [ALL |\nDISTINCT]) SELECT ...\n[(INTERSECT [ALL | DISTINCT] | EXCEPT [ALL | DISTINCT] | UNION [ALL |\nDISTINCT]) SELECT ...]\n[ORDER BY [{col_name | expr | position} [ASC | DESC] [, {col_name | expr |\nposition} [ASC | DESC] ...]]]\n[LIMIT {[offset,] row_count | row_count OFFSET offset}\n| OFFSET start { ROW | ROWS }\n| FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES } ]\n\nPlease note:\n\n* Brackets for explicit operation precedence are not supported; use a subquery\nin the FROM clause as a workaround).\n\nDescription\n-----------\n\nMariaDB has supported EXCEPT and INTERSECT in addition to UNION since MariaDB\n10.3.\n\nThe queries before and after EXCEPT must be SELECT or VALUES statements.\n\nAll behavior for naming columns, ORDER BY and LIMIT is the same as for UNION.\nNote that the alternative SELECT ... OFFSET ... FETCH syntax is only\nsupported. This allows us to use the WITH TIES clause.\n\nEXCEPT implicitly supposes a DISTINCT operation.\n\nThe result of EXCEPT is all records of the left SELECT result except records\nwhich are in right SELECT result set, i.e. it is subtraction of two result\nsets.\n\nEXCEPT and UNION have the same operation precedence and INTERSECT has a higher\nprecedence, unless running in Oracle mode, in which case all three have the\nsame precedence.\n\nMariaDB starting with 10.4.0\n----------------------------\n\nParentheses\n-----------\n\nFrom MariaDB 10.4.0, parentheses can be used to specify precedence. Before\nthis, a syntax error would be returned.\n\nMariaDB starting with 10.5.0\n----------------------------\n\nALL/DISTINCT\n------------\n\nEXCEPT ALL and EXCEPT DISTINCT were introduced in MariaDB 10.5.0. The ALL\noperator leaves duplicates intact, while the DISTINCT operator removes\nduplicates. DISTINCT is the default behavior if neither operator is supplied,\nand the only behavior prior to MariaDB 10.5.\n\nExamples\n--------\n\nShow customers which are not employees:\n\n(SELECT e_name AS name, email FROM customers)\nEXCEPT\n(SELECT c_name AS name, email FROM employees);\n\nDifference between UNION, EXCEPT and INTERSECT. INTERSECT ALL and EXCEPT ALL\nare available from MariaDB 10.5.0.\n\nCREATE TABLE seqs (i INT);\nINSERT INTO seqs VALUES (1),(2),(2),(3),(3),(4),(5),(6);\n\nSELECT i FROM seqs WHERE i <= 3 UNION SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 UNION ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n| 3 |\n| 3 |\n| 3 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n| 3 |\n+------+\n\nParentheses for specifying precedence, from MariaDB 10.4.0\n\nCREATE OR REPLACE TABLE t1 (a INT);\nCREATE OR REPLACE TABLE t2 (b INT);\nCREATE OR REPLACE TABLE t3 (c INT);\n\nINSERT INTO t1 VALUES (1),(2),(3),(4);\nINSERT INTO t2 VALUES (5),(6);\nINSERT INTO t3 VALUES (1),(6);\n\n((SELECT a FROM t1) UNION (SELECT b FROM t2)) EXCEPT (SELECT c FROM t3);\n+------+\n| a |\n+------+\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n+------+\n\n(SELECT a FROM t1) UNION ((SELECT b FROM t2) EXCEPT (SELECT c FROM t3));\n+------+\n| a |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n+------+\n\nHere is an example that makes use of the SEQUENCE storage engine and the\nVALUES statement, to generate a numeric sequence and remove some arbitrary\nnumbers from it:\n\n(SELECT seq FROM seq_1_to_10) EXCEPT VALUES (2), (3), (4);\n+-----+\n| seq |\n+-----+\n| 1 |\n| 5 |\n| 6 |\n| 7 |\n| 8 |\n| 9 |\n| 10 |\n+-----+\n\nURL: https://mariadb.com/kb/en/except/','','https://mariadb.com/kb/en/except/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (429,27,'INTERSECT','MariaDB starting with 10.3.0\n----------------------------\nINTERSECT was introduced in MariaDB 10.3.0.\n\nThe result of an intersect is the intersection of right and left SELECT\nresults, i.e. only records that are present in both result sets will be\nincluded in the result of the operation.\n\nSyntax\n------\n\nSELECT ...\n(INTERSECT [ALL | DISTINCT] | EXCEPT [ALL | DISTINCT] | UNION [ALL |\nDISTINCT]) SELECT ...\n[(INTERSECT [ALL | DISTINCT] | EXCEPT [ALL | DISTINCT] | UNION [ALL |\nDISTINCT]) SELECT ...]\n[ORDER BY [column [, column ...]]]\n[LIMIT {[offset,] row_count | row_count OFFSET offset}]\n\nDescription\n-----------\n\nMariaDB has supported INTERSECT (as well as EXCEPT) in addition to UNION since\nMariaDB 10.3.\n\nAll behavior for naming columns, ORDER BY and LIMIT is the same as for UNION.\n\nINTERSECT implicitly supposes a DISTINCT operation.\n\nThe result of an intersect is the intersection of right and left SELECT\nresults, i.e. only records that are present in both result sets will be\nincluded in the result of the operation.\n\nINTERSECT has higher precedence than UNION and EXCEPT (unless running running\nin Oracle mode, in which case all three have the same precedence). If possible\nit will be executed linearly but if not it will be translated to a subquery in\nthe FROM clause:\n\n(select a,b from t1)\nunion\n(select c,d from t2)\nintersect\n(select e,f from t3)\nunion\n(select 4,4);\n\nwill be translated to:\n\n(select a,b from t1)\nunion\nselect c,d from\n ((select c,d from t2)\n intersect\n (select e,f from t3)) dummy_subselect\nunion\n(select 4,4)\n\nMariaDB starting with 10.4.0\n----------------------------\n\nParentheses\n-----------\n\nFrom MariaDB 10.4.0, parentheses can be used to specify precedence. Before\nthis, a syntax error would be returned.\n\nMariaDB starting with 10.5.0\n----------------------------\n\nALL/DISTINCT\n------------\n\nINTERSECT ALL and INTERSECT DISTINCT were introduced in MariaDB 10.5.0. The\nALL operator leaves duplicates intact, while the DISTINCT operator removes\nduplicates. DISTINCT is the default behavior if neither operator is supplied,\nand the only behavior prior to MariaDB 10.5.\n\nExamples\n--------\n\nShow customers which are employees:\n\n(SELECT e_name AS name, email FROM employees)\nINTERSECT\n(SELECT c_name AS name, email FROM customers);\n\nDifference between UNION, EXCEPT and INTERSECT. INTERSECT ALL and EXCEPT ALL\nare available from MariaDB 10.5.0.\n\nCREATE TABLE seqs (i INT);\nINSERT INTO seqs VALUES (1),(2),(2),(3),(3),(4),(5),(6);\n\nSELECT i FROM seqs WHERE i <= 3 UNION SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 UNION ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n| 3 |\n| 3 |\n| 3 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 INTERSECT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 3 |\n| 3 |\n+------+\n\nParentheses for specifying precedence, from MariaDB 10.4.0\n\nCREATE OR REPLACE TABLE t1 (a INT);\nCREATE OR REPLACE TABLE t2 (b INT);\nCREATE OR REPLACE TABLE t3 (c INT);\n\nINSERT INTO t1 VALUES (1),(2),(3),(4);\nINSERT INTO t2 VALUES (5),(6);\nINSERT INTO t3 VALUES (1),(6);\n\n((SELECT a FROM t1) UNION (SELECT b FROM t2)) INTERSECT (SELECT c FROM t3);\n+------+\n| a |\n+------+\n| 1 |\n| 6 |\n+------+\n\n(SELECT a FROM t1) UNION ((SELECT b FROM t2) INTERSECT (SELECT c FROM t3));\n+------+\n| a |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 6 |\n+------+\n\nURL: https://mariadb.com/kb/en/intersect/','','https://mariadb.com/kb/en/intersect/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (430,27,'Precedence Control in Table Operations','MariaDB starting with 10.4.0\n----------------------------\nBeginning in MariaDB 10.4, you can control the ordering of execution on table\noperations using parentheses.\n\nSyntax\n------\n\n( expression )\n[ORDER BY [column[, column...]]]\n[LIMIT {[offset,] row_count | row_count OFFSET offset}]\n\nDescription\n-----------\n\nUsing parentheses in your SQL allows you to control the order of execution for\nSELECT statements and Table Value Constructor, including UNION, EXCEPT, and\nINTERSECT operations. MariaDB executes the parenthetical expression before the\nrest of the statement. You can then use ORDER BY and LIMIT clauses the further\norganize the result-set.\n\nNote: In practice, the Optimizer may rearrange the exact order in which\nMariaDB executes different parts of the statement. When it calculates the\nresult-set, however, it returns values as though the parenthetical expression\nwere executed first.\n\nExample\n-------\n\nCREATE TABLE test.t1 (num INT);\n\nINSERT INTO test.t1 VALUES (1),(2),(3);\n\n(SELECT * FROM test.t1 \n UNION \n VALUES (10)) \nINTERSECT \nVALUES (1),(3),(10),(11);\n+------+\n| num |\n+------+\n| 1 |\n| 3 |\n| 10 |\n+------+\n\n((SELECT * FROM test.t1 \n UNION\n VALUES (10))\n INTERSECT \n VALUES (1),(3),(10),(11)) \nORDER BY 1 DESC;\n+------+\n| num |\n+------+\n| 10 |\n| 3 |\n| 1 |\n+------+\n\nURL: https://mariadb.com/kb/en/precedence-control-in-table-operations/','','https://mariadb.com/kb/en/precedence-control-in-table-operations/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (431,27,'LIMIT','Description\n-----------\n\nUse the LIMIT clause to restrict the number of returned rows. When you use a\nsingle integer n with LIMIT, the first n rows will be returned. Use the ORDER\nBY clause to control which rows come first. You can also select a number of\nrows after an offset using either of the following:\n\nLIMIT offset, row_count\nLIMIT row_count OFFSET offset\n\nWhen you provide an offset m with a limit n, the first m rows will be ignored,\nand the following n rows will be returned.\n\nExecuting an UPDATE with the LIMIT clause is not safe for replication. LIMIT 0\nis an exception to this rule (see MDEV-6170).\n\nThere is a LIMIT ROWS EXAMINED optimization which provides the means to\nterminate the execution of SELECT statements which examine too many rows, and\nthus use too many resources. See LIMIT ROWS EXAMINED.\n\nMulti-Table Updates\n-------------------\n\nMariaDB starting with 10.3.2\n----------------------------\nUntil MariaDB 10.3.1, it was not possible to use LIMIT (or ORDER BY) in a\nmulti-table UPDATE statement. This restriction was lifted in MariaDB 10.3.2.\n\nGROUP_CONCAT\n------------\n\nMariaDB starting with 10.3.2\n----------------------------\nStarting from MariaDB 10.3.3, it is possible to use LIMIT with GROUP_CONCAT().\n\nExamples\n--------\n\nCREATE TABLE members (name VARCHAR(20));\nINSERT INTO members VALUES(\'Jagdish\'),(\'Kenny\'),(\'Rokurou\'),(\'Immaculada\');\n\nSELECT * FROM members;\n+------------+\n| name |\n+------------+\n| Jagdish |\n| Kenny |\n| Rokurou |\n| Immaculada |\n+------------+\n\nSelect the first two names (no ordering specified):\n\nSELECT * FROM members LIMIT 2;\n+---------+\n| name |\n+---------+\n| Jagdish |\n| Kenny |\n+---------+\n\nAll the names in alphabetical order:\n\nSELECT * FROM members ORDER BY name;\n+------------+\n| name |\n+------------+\n| Immaculada |\n| Jagdish |\n| Kenny |\n| Rokurou |\n+------------+\n\nThe first two names, ordered alphabetically:\n\nSELECT * FROM members ORDER BY name LIMIT 2;\n+------------+\n| name |\n+------------+\n| Immaculada |\n| Jagdish |\n+------------+\n\nThe third name, ordered alphabetically (the first name would be offset zero,\nso the third is offset two):\n\nSELECT * FROM members ORDER BY name LIMIT 2,1;\n+-------+\n| name |\n+-------+\n| Kenny |\n+-------+\n\nFrom MariaDB 10.3.2, LIMIT can be used in a multi-table update:\n\nCREATE TABLE warehouse (product_id INT, qty INT);\nINSERT INTO warehouse VALUES (1,100),(2,100),(3,100),(4,100);\n\nCREATE TABLE store (product_id INT, qty INT);\nINSERT INTO store VALUES (1,5),(2,5),(3,5),(4,5);\n\nUPDATE warehouse,store SET warehouse.qty = warehouse.qty-2, store.qty =\nstore.qty+2 \n WHERE (warehouse.product_id = store.product_id AND store.product_id >= 1)\n ORDER BY store.product_id DESC LIMIT 2;\n\nSELECT * FROM warehouse;\n+------------+------+\n| product_id | qty |\n+------------+------+\n| 1 | 100 |\n| 2 | 100 |\n| 3 | 98 |\n| 4 | 98 |\n+------------+------+\n\nSELECT * FROM store;\n+------------+------+\n| product_id | qty |\n+------------+------+\n| 1 | 5 |\n| 2 | 5 |\n| 3 | 7 |\n| 4 | 7 |\n+------------+------+\n\nFrom MariaDB 10.3.3, LIMIT can be used with GROUP_CONCAT, so, for example,\ngiven the following table:\n\nCREATE TABLE d (dd DATE, cc INT);\n\nINSERT INTO d VALUES (\'2017-01-01\',1);\nINSERT INTO d VALUES (\'2017-01-02\',2);\nINSERT INTO d VALUES (\'2017-01-04\',3);\n\nthe following query:\n\nSELECT SUBSTRING_INDEX(GROUP_CONCAT(CONCAT_WS(\":\",dd,cc) ORDER BY cc\nDESC),\",\",1) FROM d;\n+----------------------------------------------------------------------------+\n| SUBSTRING_INDEX(GROUP_CONCAT(CONCAT_WS(\":\",dd,cc) ORDER BY cc DESC),\",\",1) |\n+----------------------------------------------------------------------------+\n| 2017-01-04:3 |\n+----------------------------------------------------------------------------+\n\ncan be more simply rewritten as:\n\nSELECT GROUP_CONCAT(CONCAT_WS(\":\",dd,cc) ORDER BY cc DESC LIMIT 1) FROM d;\n+-------------------------------------------------------------+\n| GROUP_CONCAT(CONCAT_WS(\":\",dd,cc) ORDER BY cc DESC LIMIT 1) |\n+-------------------------------------------------------------+\n| 2017-01-04:3 |\n+-------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/limit/','','https://mariadb.com/kb/en/limit/'); @@ -533,7 +533,7 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (435,27,'Non-Recursive Common Table Expressions Overview','Common Table Expressions (CTEs) are a standard SQL feature, and are\nessentially temporary named result sets. There are two kinds of CTEs:\nNon-Recursive, which this article covers; and Recursive.\n\nMariaDB starting with 10.2.1\n----------------------------\nCommon table expressions were introduced in MariaDB 10.2.1.\n\nNon-Recursive CTEs\n------------------\n\nThe WITH keyword signifies a CTE. It is given a name, followed by a body (the\nmain query) as follows:\n\nCTEs are similar to derived tables. For example\n\nWITH engineers AS \n ( SELECT * FROM employees\n WHERE dept = \'Engineering\' )\n\nSELECT * FROM engineers\nWHERE ...\n\nSELECT * FROM\n ( SELECT * FROM employees\n WHERE dept = \'Engineering\' ) AS engineers\nWHERE\n...\n\nA non-recursive CTE is basically a query-local VIEW. There are several\nadvantages and caveats to them. The syntax is more readable than nested FROM\n(SELECT ...). A CTE can refer to another and it can be referenced from\nmultiple places.\n\nA CTE referencing Another CTE\n-----------------------------\n\nUsing this format makes for a more readable SQL than a nested FROM(SELECT ...)\nclause. Below is an example of this:\n\nWITH engineers AS (\nSELECT * FROM employees\nWHERE dept IN(\'Development\',\'Support\') ),\neu_engineers AS ( SELECT * FROM engineers WHERE country IN(\'NL\',...) )\nSELECT\n...\nFROM eu_engineers;\n\nMultiple Uses of a CTE\n----------------------\n\nThis can be an \'anti-self join\', for example:\n\nWITH engineers AS (\nSELECT * FROM employees\nWHERE dept IN(\'Development\',\'Support\') )\n\nSELECT * FROM engineers E1\nWHERE NOT EXISTS\n (SELECT 1 FROM engineers E2\n WHERE E2.country=E1.country\n AND E2.name <> E1.name );\n\nOr, for year-over-year comparisons, for example:\n\nWITH sales_product_year AS (\nSELECT product, YEAR(ship_date) AS year,\nSUM(price) AS total_amt\nFROM item_sales\nGROUP BY product, year )\n\nSELECT *\nFROM sales_product_year CUR,\nsales_product_year PREV,\nWHERE CUR.product=PREV.product \nAND CUR.year=PREV.year + 1 \nAND CUR.total_amt > PREV.total_amt\n\nAnother use is to compare individuals against their group. Below is an example\nof how this might be executed:\n\nWITH sales_product_year AS (\nSELECT product,\nYEAR(ship_date) AS year,\nSUM(price) AS total_amt\nFROM item_sales\nGROUP BY product, year\n)\n\nSELECT * \nFROM sales_product_year S1\nWHERE\ntotal_amt > \n (SELECT 0.1 * SUM(total_amt)\n FROM sales_product_year S2\n WHERE S2.year = S1.year)\n\nURL: https://mariadb.com/kb/en/non-recursive-common-table-expressions-overview/','','https://mariadb.com/kb/en/non-recursive-common-table-expressions-overview/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (436,27,'Recursive Common Table Expressions Overview','MariaDB starting with 10.2.2\n----------------------------\nRecursive Common Table Expressions have been supported since MariaDB 10.2.2.\n\nCommon Table Expressions (CTEs) are a standard SQL feature, and are\nessentially temporary named result sets. CTEs first appeared in the SQL\nstandard in 1999, and the first implementations began appearing in 2007.\n\nThere are two kinds of CTEs:\n\n* Non-recursive\n* Recursive, which this article covers.\n\nSQL is generally poor at recursive structures.\n\nCTEs permit a query to reference itself. A recursive CTE will repeatedly\nexecute subsets of the data until it obtains the complete result set. This\nmakes it particularly useful for handing hierarchical or tree-structured data.\nmax_recursive_iterations avoids infinite loops.\n\nSyntax example\n--------------\n\nWITH RECURSIVE signifies a recursive CTE. It is given a name, followed by a\nbody (the main query) as follows:\n\nComputation\n-----------\n\nGiven the following structure:\n\nFirst execute the anchor part of the query:\n\nNext, execute the recursive part of the query:\n\nSummary so far\n--------------\n\nwith recursive R as (\n select anchor_data\n union [all]\n select recursive_part\n from R, ...\n)\nselect ...\n\n* Compute anchor_data\n* Compute recursive_part to get the new data\n* if (new data is non-empty) goto 2;\n\nCAST to avoid truncating data\n-----------------------------\n\nAs currently implemented by MariaDB and by the SQL Standard, data may be\ntruncated if not correctly cast. It is necessary to CAST the column to the\ncorrect width if the CTE\'s recursive part produces wider values for a column\nthan the CTE\'s nonrecursive part. Some other DBMS give an error in this\nsituation, and MariaDB\'s behavior may change in future - see MDEV-12325. See\nthe examples below.\n\nExamples\n--------\n\nTransitive closure - determining bus destinations\n-------------------------------------------------\n\nSample data:\n\nCREATE TABLE bus_routes (origin varchar(50), dst varchar(50));\nINSERT INTO bus_routes VALUES \n (\'New York\', \'Boston\'),\n (\'Boston\', \'New York\'),\n (\'New York\', \'Washington\'),\n (\'Washington\', \'Boston\'),\n (\'Washington\', \'Raleigh\');\n\nNow, we want to return the bus destinations with New York as the origin:\n\nWITH RECURSIVE bus_dst as ( \n SELECT origin as dst FROM bus_routes WHERE origin=\'New York\'\n UNION\n SELECT bus_routes.dst FROM bus_routes JOIN bus_dst ON bus_dst.dst=\nbus_routes.origin \n) \nSELECT * FROM bus_dst;\n+------------+\n| dst |\n+------------+\n| New York |\n| Boston |\n| Washington |\n| Raleigh |\n+------------+\n\nThe above example is computed as follows:\n\nFirst, the anchor data is calculated:\n\n* Starting from New York\n* Boston and Washington are added\n\nNext, the recursive part:\n\n* Starting from Boston and then Washington\n* Raleigh is added\n* UNION excludes nodes that are already present.\n\nComputing paths - determining bus routes\n----------------------------------------\n\nThis time, we are trying to get bus routes such as \"New York -> Washington ->\nRaleigh\".\n\nUsing the same sample data as the previous example:\n\nWITH RECURSIVE paths (cur_path, cur_dest) AS (\n SELECT origin, origin FROM bus_routes WHERE origin=\'New York\'\n UNION\n SELECT CONCAT(paths.cur_path, \',\', bus_routes.dst), bus_routes.dst\n FROM paths\n JOIN bus_routes\n ON paths.cur_dest = bus_routes.origin AND\n NOT FIND_IN_SET(bus_routes.dst, paths.cur_path)\n) \nSELECT * FROM paths;\n+-----------------------------+------------+\n| cur_path | cur_dest |\n+-----------------------------+------------+\n| New York | New York |\n| New York,Boston | Boston |\n| New York,Washington | Washington |\n| New York,Washington,Boston | Boston |\n| New York,Washington,Raleigh | Raleigh |\n+-----------------------------+------------+\n\nCAST to avoid data truncation\n-----------------------------\n\nIn the following example, data is truncated because the results are not\nspecifically cast to a wide enough type:\n\nWITH RECURSIVE tbl AS (\n SELECT NULL AS col\n UNION\n SELECT \"THIS NEVER SHOWS UP\" AS col FROM tbl\n)\nSELECT col FROM tbl\n+------+\n| col |\n+------+\n| NULL |\n| |\n+------+\n\nExplicitly use CAST to overcome this:\n\nWITH RECURSIVE tbl AS (\n SELECT CAST(NULL AS CHAR(50)) AS col\n UNION SELECT \"THIS NEVER SHOWS UP\" AS col FROM tbl\n) \nSELECT * FROM tbl;\n+---------------------+\n| col |\n+---------------------+\n| NULL |\n| THIS NEVER SHOWS UP |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/recursive-common-table-expressions-overview/','','https://mariadb.com/kb/en/recursive-common-table-expressions-overview/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (437,27,'SELECT WITH ROLLUP','Syntax\n------\n\nSee SELECT for the full syntax.\n\nDescription\n-----------\n\nThe WITH ROLLUP modifier adds extra rows to the resultset that represent\nsuper-aggregate summaries. The super-aggregated column is represented by a\nNULL value. Multiple aggregates over different columns will be added if there\nare multiple GROUP BY columns.\n\nThe LIMIT clause can be used at the same time, and is applied after the WITH\nROLLUP rows have been added.\n\nWITH ROLLUP cannot be used with ORDER BY. Some sorting is still possible by\nusing ASC or DESC clauses with the GROUP BY column, although the\nsuper-aggregate rows will always be added last.\n\nExamples\n--------\n\nThese examples use the following sample table\n\nCREATE TABLE booksales ( \n country VARCHAR(35), genre ENUM(\'fiction\',\'non-fiction\'), year YEAR, sales\nINT);\n\nINSERT INTO booksales VALUES\n (\'Senegal\',\'fiction\',2014,12234), (\'Senegal\',\'fiction\',2015,15647),\n (\'Senegal\',\'non-fiction\',2014,64980), (\'Senegal\',\'non-fiction\',2015,78901),\n (\'Paraguay\',\'fiction\',2014,87970), (\'Paraguay\',\'fiction\',2015,76940),\n (\'Paraguay\',\'non-fiction\',2014,8760), (\'Paraguay\',\'non-fiction\',2015,9030);\n\nThe addition of the WITH ROLLUP modifier in this example adds an extra row\nthat aggregates both years:\n\nSELECT year, SUM(sales) FROM booksales GROUP BY year;\n+------+------------+\n| year | SUM(sales) |\n+------+------------+\n| 2014 | 173944 |\n| 2015 | 180518 |\n+------+------------+\n2 rows in set (0.08 sec)\n\nSELECT year, SUM(sales) FROM booksales GROUP BY year WITH ROLLUP;\n+------+------------+\n| year | SUM(sales) |\n+------+------------+\n| 2014 | 173944 |\n| 2015 | 180518 |\n| NULL | 354462 |\n+------+------------+\n\nIn the following example, each time the genre, the year or the country change,\nanother super-aggregate row is added:\n\nSELECT country, year, genre, SUM(sales) \n FROM booksales GROUP BY country, year, genre;\n+----------+------+-------------+------------+\n| country | year | genre | SUM(sales) |\n+----------+------+-------------+------------+\n| Paraguay | 2014 | fiction | 87970 |\n| Paraguay | 2014 | non-fiction | 8760 |\n| Paraguay | 2015 | fiction | 76940 |\n| Paraguay | 2015 | non-fiction | 9030 |\n| Senegal | 2014 | fiction | 12234 |\n| Senegal | 2014 | non-fiction | 64980 |\n| Senegal | 2015 | fiction | 15647 |\n| Senegal | 2015 | non-fiction | 78901 |\n+----------+------+-------------+------------+\n\nSELECT country, year, genre, SUM(sales) \n FROM booksales GROUP BY country, year, genre WITH ROLLUP;\n+----------+------+-------------+------------+\n| country | year | genre | SUM(sales) |\n+----------+------+-------------+------------+\n| Paraguay | 2014 | fiction | 87970 |\n| Paraguay | 2014 | non-fiction | 8760 |\n| Paraguay | 2014 | NULL | 96730 |\n| Paraguay | 2015 | fiction | 76940 |\n| Paraguay | 2015 | non-fiction | 9030 |\n| Paraguay | 2015 | NULL | 85970 |\n| Paraguay | NULL | NULL | 182700 |\n| Senegal | 2014 | fiction | 12234 |\n| Senegal | 2014 | non-fiction | 64980 |\n| Senegal | 2014 | NULL | 77214 |\n| Senegal | 2015 | fiction | 15647 |\n| Senegal | 2015 | non-fiction | 78901 |\n| Senegal | 2015 | NULL | 94548 |\n| Senegal | NULL | NULL | 171762 |\n| NULL | NULL | NULL | 354462 |\n+----------+------+-------------+------------+\n\nThe LIMIT clause, applied after WITH ROLLUP:\n\nSELECT country, year, genre, SUM(sales) \n FROM booksales GROUP BY country, year, genre WITH ROLLUP LIMIT 4;\n+----------+------+-------------+------------+\n| country | year | genre | SUM(sales) |\n+----------+------+-------------+------------+\n| Paraguay | 2014 | fiction | 87970 |\n| Paraguay | 2014 | non-fiction | 8760 |\n| Paraguay | 2014 | NULL | 96730 |\n| Paraguay | 2015 | fiction | 76940 |\n+----------+------+-------------+------------+\n\nSorting by year descending:\n\nSELECT country, year, genre, SUM(sales) \n FROM booksales GROUP BY country, year DESC, genre WITH ROLLUP;\n+----------+------+-------------+------------+\n| country | year | genre | SUM(sales) |\n+----------+------+-------------+------------+\n| Paraguay | 2015 | fiction | 76940 |\n| Paraguay | 2015 | non-fiction | 9030 |\n| Paraguay | 2015 | NULL | 85970 |\n| Paraguay | 2014 | fiction | 87970 |\n| Paraguay | 2014 | non-fiction | 8760 |\n| Paraguay | 2014 | NULL | 96730 |\n| Paraguay | NULL | NULL | 182700 |\n| Senegal | 2015 | fiction | 15647 |\n| Senegal | 2015 | non-fiction | 78901 |\n| Senegal | 2015 | NULL | 94548 |\n| Senegal | 2014 | fiction | 12234 |\n| Senegal | 2014 | non-fiction | 64980 |\n| Senegal | 2014 | NULL | 77214 |\n| Senegal | NULL | NULL | 171762 |\n| NULL | NULL | NULL | 354462 |\n+----------+------+-------------+------------+\n\nURL: https://mariadb.com/kb/en/select-with-rollup/','','https://mariadb.com/kb/en/select-with-rollup/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (438,27,'SELECT INTO OUTFILE','Syntax\n------\n\nSELECT ... INTO OUTFILE \'file_name\'\n [CHARACTER SET charset_name]\n [export_options]\n\nexport_options:\n [{FIELDS | COLUMNS}\n [TERMINATED BY \'string\']\n [[OPTIONALLY] ENCLOSED BY \'char\']\n [ESCAPED BY \'char\']\n ]\n [LINES\n [STARTING BY \'string\']\n [TERMINATED BY \'string\']\n ]\n\nDescription\n-----------\n\nSELECT INTO OUTFILE writes the resulting rows to a file, and allows the use of\ncolumn and row terminators to specify a particular output format. The default\nis to terminate fields with tabs (\\t) and lines with newlines (\\n).\n\nThe file must not exist. It cannot be overwritten. A user needs the FILE\nprivilege to run this statement. Also, MariaDB needs permission to write files\nin the specified location. If the secure_file_priv system variable is set to a\nnon-empty directory name, the file can only be written to that directory.\n\nThe LOAD DATA INFILE statement complements SELECT INTO OUTFILE.\n\nCharacter-sets\n--------------\n\nThe CHARACTER SET clause specifies the character set in which the results are\nto be written. Without the clause, no conversion takes place (the binary\ncharacter set). In this case, if there are multiple character sets, the output\nwill contain these too, and may not easily be able to be reloaded.\n\nIn cases where you have two servers using different character-sets, using\nSELECT INTO OUTFILE to transfer data from one to the other can have unexpected\nresults. To ensure that MariaDB correctly interprets the escape sequences, use\nthe CHARACTER SET clause on both the SELECT INTO OUTFILE statement and the\nsubsequent LOAD DATA INFILE statement.\n\nExample\n-------\n\nThe following example produces a file in the CSV format:\n\nSELECT customer_id, firstname, surname INTO OUTFILE \'/exportdata/customers.txt\'\n FIELDS TERMINATED BY \',\' OPTIONALLY ENCLOSED BY \'\"\'\n LINES TERMINATED BY \'\\n\'\n FROM customers;\n\nURL: https://mariadb.com/kb/en/select-into-outfile/','','https://mariadb.com/kb/en/select-into-outfile/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (438,27,'SELECT INTO OUTFILE','Syntax\n------\n\nSELECT ... INTO OUTFILE \'file_name\'\n [CHARACTER SET charset_name]\n [export_options]\n\nexport_options:\n [{FIELDS | COLUMNS}\n [TERMINATED BY \'string\']\n [[OPTIONALLY] ENCLOSED BY \'char\']\n [ESCAPED BY \'char\']\n ]\n [LINES\n [STARTING BY \'string\']\n [TERMINATED BY \'string\']\n ]\n\nDescription\n-----------\n\nSELECT INTO OUTFILE writes the resulting rows to a file, and allows the use of\ncolumn and row terminators to specify a particular output format. The default\nis to terminate fields with tabs (\\t) and lines with newlines (\\n).\n\nThe file must not exist. It cannot be overwritten. A user needs the FILE\nprivilege to run this statement. Also, MariaDB needs permission to write files\nin the specified location. If the secure_file_priv system variable is set to a\nnon-empty directory name, the file can only be written to that directory.\n\nThe LOAD DATA INFILE statement complements SELECT INTO OUTFILE.\n\nCharacter-sets\n--------------\n\nThe CHARACTER SET clause specifies the character set in which the results are\nto be written. Without the clause, no conversion takes place (the binary\ncharacter set). In this case, if there are multiple character sets, the output\nwill contain these too, and may not easily be able to be reloaded.\n\nIn cases where you have two servers using different character-sets, using\nSELECT INTO OUTFILE to transfer data from one to the other can have unexpected\nresults. To ensure that MariaDB correctly interprets the escape sequences, use\nthe CHARACTER SET clause on both the SELECT INTO OUTFILE statement and the\nsubsequent LOAD DATA INFILE statement.\n\nExample\n-------\n\nThe following example produces a file in the CSV format:\n\nSELECT customer_id, firstname, surname from customer\n INTO OUTFILE \'/exportdata/customers.txt\'\n FIELDS TERMINATED BY \',\' OPTIONALLY ENCLOSED BY \'\"\'\n LINES TERMINATED BY \'\\n\';\n\nThe following ANSI syntax is also supported for simple SELECT without UNION\n\nSELECT customer_id, firstname, surname INTO OUTFILE \'/exportdata/customers.txt\'\n FIELDS TERMINATED BY \',\' OPTIONALLY ENCLOSED BY \'\"\'\n LINES TERMINATED BY \'\\n\'\n FROM customers;\n\nIf you want to use the ANSI syntax with UNION or similar construct you have to\nuse the syntax:\n\nSELECT * INTO OUTFILE \"/tmp/skr3\" FROM (SELECT * FROM t1 UNION SELECT * FROM\nt1);\n\nURL: https://mariadb.com/kb/en/select-into-outfile/','','https://mariadb.com/kb/en/select-into-outfile/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (439,27,'SELECT INTO DUMPFILE','Syntax\n------\n\nSELECT ... INTO DUMPFILE \'file_path\'\n\nDescription\n-----------\n\nSELECT ... INTO DUMPFILE is a SELECT clause which writes the resultset into a\nsingle unformatted row, without any separators, in a file. The results will\nnot be returned to the client.\n\nfile_path can be an absolute path, or a relative path starting from the data\ndirectory. It can only be specified as a string literal, not as a variable.\nHowever, the statement can be dynamically composed and executed as a prepared\nstatement to work around this limitation.\n\nThis statement is binary-safe and so is particularly useful for writing BLOB\nvalues to file. It can be used, for example, to copy an image or an audio\ndocument from the database to a file. SELECT ... INTO FILE can be used to save\na text file.\n\nThe file must not exist. It cannot be overwritten. A user needs the FILE\nprivilege to run this statement. Also, MariaDB needs permission to write files\nin the specified location. If the secure_file_priv system variable is set to a\nnon-empty directory name, the file can only be written to that directory.\n\nSince MariaDB 5.1, the character_set_filesystem system variable has controlled\ninterpretation of file names that are given as literal strings.\n\nExample\n-------\n\nSELECT _utf8\'Hello world!\' INTO DUMPFILE \'/tmp/world\';\n\nSELECT LOAD_FILE(\'/tmp/world\') AS world;\n+--------------+\n| world |\n+--------------+\n| Hello world! |\n+--------------+\n\nURL: https://mariadb.com/kb/en/select-into-dumpfile/','','https://mariadb.com/kb/en/select-into-dumpfile/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (440,27,'FOR UPDATE','InnoDB supports row-level locking. Selected rows can be locked using LOCK IN\nSHARE MODE or FOR UPDATE. In both cases, a lock is acquired on the rows read\nby the query, and it will be released when the current transaction is\ncommitted.\n\nThe FOR UPDATE clause of SELECT applies only when autocommit is set to 0 or\nthe SELECT is enclosed in a transaction. A lock is acquired on the rows, and\nother transactions are prevented from writing the rows, acquire locks, and\nfrom reading them (unless their isolation level is READ UNCOMMITTED).\n\nIf autocommit is set to 1, the LOCK IN SHARE MODE and FOR UPDATE clauses have\nno effect.\n\nIf the isolation level is set to SERIALIZABLE, all plain SELECT statements are\nconverted to SELECT ... LOCK IN SHARE MODE.\n\nExample\n-------\n\nSELECT * FROM trans WHERE period=2001 FOR UPDATE;\n\nURL: https://mariadb.com/kb/en/for-update/','','https://mariadb.com/kb/en/for-update/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (441,27,'LOCK IN SHARE MODE','InnoDB supports row-level locking. Selected rows can be locked using LOCK IN\nSHARE MODE or FOR UPDATE. In both cases, a lock is acquired on the rows read\nby the query, and it will be released when the current transaction is\ncommitted.\n\nWhen LOCK IN SHARE MODE is specified in a SELECT statement, MariaDB will wait\nuntil all transactions that have modified the rows are committed. Then, a\nwrite lock is acquired. All transactions can read the rows, but if they want\nto modify them, they have to wait until your transaction is committed.\n\nIf autocommit is set to 1, the LOCK IN SHARE MODE and FOR UPDATE clauses have\nno effect.\n\nURL: https://mariadb.com/kb/en/lock-in-share-mode/','','https://mariadb.com/kb/en/lock-in-share-mode/'); @@ -544,7 +544,7 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (446,27,'INSERT','Syntax\n------\n\nINSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]\n [INTO] tbl_name [PARTITION (partition_list)] [(col,...)]\n {VALUES | VALUE} ({expr | DEFAULT},...),(...),...\n [ ON DUPLICATE KEY UPDATE\n col=expr\n [, col=expr] ... ] [RETURNING select_expr\n [, select_expr ...]]\n\nOr:\n\nINSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]\n [INTO] tbl_name [PARTITION (partition_list)]\n SET col={expr | DEFAULT}, ...\n [ ON DUPLICATE KEY UPDATE\n col=expr\n [, col=expr] ... ] [RETURNING select_expr\n [, select_expr ...]]\n\nOr:\n\nINSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]\n [INTO] tbl_name [PARTITION (partition_list)] [(col,...)]\n SELECT ...\n [ ON DUPLICATE KEY UPDATE\n col=expr\n [, col=expr] ... ] [RETURNING select_expr\n [, select_expr ...]]\n\nThe INSERT statement is used to insert new rows into an existing table. The\nINSERT ... VALUES and INSERT ... SET forms of the statement insert rows based\non explicitly specified values. The INSERT ... SELECT form inserts rows\nselected from another table or tables. INSERT ... SELECT is discussed further\nin the INSERT ... SELECT article.\n\nThe table name can be specified in the form db_name.tbl_name or, if a default\ndatabase is selected, in the form tbl_name (see Identifier Qualifiers). This\nallows to use INSERT ... SELECT to copy rows between different databases.\n\nThe PARTITION clause can be used in both the INSERT and the SELECT part. See\nPartition Pruning and Selection for details.\n\nMariaDB starting with 10.5\n--------------------------\nThe RETURNING clause was introduced in MariaDB 10.5.\n\nThe columns list is optional. It specifies which values are explicitly\ninserted, and in which order. If this clause is not specified, all values must\nbe explicitly specified, in the same order they are listed in the table\ndefinition.\n\nThe list of value follow the VALUES or VALUE keyword (which are\ninterchangeable, regardless how much values you want to insert), and is\nwrapped by parenthesis. The values must be listed in the same order as the\ncolumns list. It is possible to specify more than one list to insert more than\none rows with a single statement. If many rows are inserted, this is a speed\noptimization.\n\nFor one-row statements, the SET clause may be more simple, because you don\'t\nneed to remember the columns order. All values are specified in the form col =\nexpr.\n\nValues can also be specified in the form of a SQL expression or subquery.\nHowever, the subquery cannot access the same table that is named in the INTO\nclause.\n\nIf you use the LOW_PRIORITY keyword, execution of the INSERT is delayed until\nno other clients are reading from the table. If you use the HIGH_PRIORITY\nkeyword, the statement has the same priority as SELECTs. This affects only\nstorage engines that use only table-level locking (MyISAM, MEMORY, MERGE).\nHowever, if one of these keywords is specified, concurrent inserts cannot be\nused. See HIGH_PRIORITY and LOW_PRIORITY clauses for details.\n\nINSERT DELAYED\n--------------\n\nFor more details on the DELAYED option, see INSERT DELAYED.\n\nHIGH PRIORITY and LOW PRIORITY\n------------------------------\n\nSee HIGH_PRIORITY and LOW_PRIORITY.\n\nDefaults and Duplicate Values\n-----------------------------\n\nSee INSERT - Default & Duplicate Values for details..\n\nINSERT IGNORE\n-------------\n\nSee INSERT IGNORE.\n\nINSERT ON DUPLICATE KEY UPDATE\n------------------------------\n\nSee INSERT ON DUPLICATE KEY UPDATE.\n\nExamples\n--------\n\nSpecifying the column names:\n\nINSERT INTO person (first_name, last_name) VALUES (\'John\', \'Doe\');\n\nInserting more than 1 row at a time:\n\nINSERT INTO tbl_name VALUES (1, \"row 1\"), (2, \"row 2\");\n\nUsing the SET clause:\n\nINSERT INTO person SET first_name = \'John\', last_name = \'Doe\';\n\nSELECTing from another table:\n\nINSERT INTO contractor SELECT * FROM person WHERE status = \'c\';\n\nSee INSERT ON DUPLICATE KEY UPDATE and INSERT IGNORE for further examples.\n\nINSERT ... RETURNING\n--------------------\n\nINSERT ... RETURNING returns a resultset of the inserted rows.\n\nThis returns the listed columns for all the rows that are inserted, or\nalternatively, the specified SELECT expression. Any SQL expressions which can\nbe calculated can be used in the select expression for the RETURNING clause,\nincluding virtual columns and aliases, expressions which use various operators\nsuch as bitwise, logical and arithmetic operators, string functions, date-time\nfunctions, numeric functions, control flow functions, secondary functions and\nstored functions. Along with this, statements which have subqueries and\nprepared statements can also be used.\n\nExamples\n--------\n\nSimple INSERT statement\n\nINSERT INTO t2 VALUES (1,\'Dog\'),(2,\'Lion\'),(3,\'Tiger\'),(4,\'Leopard\') \nRETURNING id2,id2+id2,id2&id2,id2||id2;\n+-----+---------+---------+----------+\n| id2 | id2+id2 | id2&id2 | id2||id2 |\n+-----+---------+---------+----------+\n| 1 | 2 | 1 | 1 |\n| 2 | 4 | 2 | 1 |\n| 3 | 6 | 3 | 1 |\n| 4 | 8 | 4 | 1 |\n+-----+---------+---------+----------+\n\nUsing stored functions in RETURNING\n\nDELIMITER |\nCREATE FUNCTION f(arg INT) RETURNS INT\n BEGIN\n RETURN (SELECT arg+arg);\n END|\n\nDELIMITER ;\n\nPREPARE stmt FROM \"INSERT INTO t1 SET id1=1, animal1=\'Bear\' RETURNING f(id1),\nUPPER(animal1)\";\n\nEXECUTE stmt;\n+---------+----------------+\n| f(id1) | UPPER(animal1) |\n+---------+----------------+\n| 2 | BEAR |\n+---------+----------------+\n\nSubqueries in the RETURNING clause that return more than one row or column\ncannot be used.\n\nAggregate functions cannot be used in the RETURNING clause. Since aggregate\nfunctions work on a set of values, and if the purpose is to get the row count,\nROW_COUNT() with SELECT can be used or it can be used in\nINSERT...SELECT...RETURNING if the table in the RETURNING clause is not the\nsame as the INSERT table.\n\nURL: https://mariadb.com/kb/en/insert/','','https://mariadb.com/kb/en/insert/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (447,27,'INSERT DELAYED','Syntax\n------\n\nINSERT DELAYED ...\n\nDescription\n-----------\n\nThe DELAYED option for the INSERT statement is a MariaDB/MySQL extension to\nstandard SQL that is very useful if you have clients that cannot or need not\nwait for the INSERT to complete. This is a common situation when you use\nMariaDB for logging and you also periodically run SELECT and UPDATE statements\nthat take a long time to complete.\n\nWhen a client uses INSERT DELAYED, it gets an okay from the server at once,\nand the row is queued to be inserted when the table is not in use by any other\nthread.\n\nAnother major benefit of using INSERT DELAYED is that inserts from many\nclients are bundled together and written in one block. This is much faster\nthan performing many separate inserts.\n\nNote that INSERT DELAYED is slower than a normal INSERT if the table is not\notherwise in use. There is also the additional overhead for the server to\nhandle a separate thread for each table for which there are delayed rows. This\nmeans that you should use INSERT DELAYED only when you are really sure that\nyou need it.\n\nThe queued rows are held only in memory until they are inserted into the\ntable. This means that if you terminate mysqld forcibly (for example, with\nkill -9) or if mysqld dies unexpectedly, any queued rows that have not been\nwritten to disk are lost.\n\nThe number of concurrent INSERT DELAYED threads is limited by the\nmax_delayed_threads server system variables. If it is set to 0, INSERT DELAYED\nis disabled. The session value can be equal to the global value, or 0 to\ndisable this statement for the current session. If this limit has been\nreached, the DELAYED clause will be silently ignore for subsequent statements\n(no error will be produced).\n\nLimitations\n-----------\n\nThere are some limitations on the use of DELAYED:\n\n* INSERT DELAYED works only with MyISAM, MEMORY, ARCHIVE,\n and BLACKHOLE tables. If you execute INSERT DELAYED with another storage\nengine, you will get an error like this: ERROR 1616 (HY000): DELAYED option\nnot supported for table \'tab_name\'\n* For MyISAM tables, if there are no free blocks in the middle of the data\n file, concurrent SELECT and INSERT statements are supported. Under these\n circumstances, you very seldom need to use INSERT DELAYED\n with MyISAM.\n* INSERT DELAYED should be used only for\n INSERT statements that specify value lists. The server\n ignores DELAYED for INSERT ... SELECT\n or INSERT ... ON DUPLICATE KEY UPDATE statements.\n* Because the INSERT DELAYED statement returns immediately,\n before the rows are inserted, you cannot use\n LAST_INSERT_ID() to get the\n AUTO_INCREMENT value that the statement might generate.\n* DELAYED rows are not visible to SELECT\n statements until they actually have been inserted.\n* After INSERT DELAYED, ROW_COUNT() returns the number of the rows you tried\nto insert, not the number of the successful writes.\n* DELAYED is ignored on slave replication servers, so that \n INSERT DELAYED is treated as a normal\n INSERT on slaves. This is because\n DELAYED could cause the slave to have different data than\n the master. INSERT DELAYED statements are not safe for replication.\n* Pending INSERT DELAYED statements are lost if a table is\n write locked and ALTER TABLE is used to modify the table structure.\n* INSERT DELAYED is not supported for views. If you try, you will get an error\nlike this: ERROR 1347 (HY000): \'view_name\' is not BASE TABLE\n* INSERT DELAYED is not supported for partitioned tables.\n* INSERT DELAYED is not supported within stored programs.\n* INSERT DELAYED does not work with triggers.\n* INSERT DELAYED does not work if there is a check constraint in place.\n* INSERT DELAYED does not work if skip-new mode is active.\n\nURL: https://mariadb.com/kb/en/insert-delayed/','','https://mariadb.com/kb/en/insert-delayed/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (448,27,'INSERT SELECT','Syntax\n------\n\nINSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]\n [INTO] tbl_name [(col_name,...)]\n SELECT ...\n [ ON DUPLICATE KEY UPDATE col_name=expr, ... ]\n\nDescription\n-----------\n\nWith INSERT ... SELECT, you can quickly insert many rows into a table from one\nor more other tables. For example:\n\nINSERT INTO tbl_temp2 (fld_id)\n SELECT tbl_temp1.fld_order_id\n FROM tbl_temp1 WHERE tbl_temp1.fld_order_id > 100;\n\ntbl_name can also be specified in the form db_name.tbl_name (see Identifier\nQualifiers). This allows to copy rows between different databases.\n\nIf the new table has a primary key or UNIQUE indexes, you can use IGNORE to\nhandle duplicate key errors during the query. The newer values will not be\ninserted if an identical value already exists.\n\nREPLACE can be used instead of INSERT to prevent duplicates on UNIQUE indexes\nby deleting old values. In that case, ON DUPLICATE KEY UPDATE cannot be used.\n\nINSERT ... SELECT works for tables which already exist. To create a table for\na given resultset, you can use CREATE TABLE ... SELECT.\n\nURL: https://mariadb.com/kb/en/insert-select/','','https://mariadb.com/kb/en/insert-select/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (449,27,'LOAD DATA INFILE','Syntax\n------\n\nLOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE \'file_name\'\n [REPLACE | IGNORE]\n INTO TABLE tbl_name\n [CHARACTER SET charset_name]\n [{FIELDS | COLUMNS}\n [TERMINATED BY \'string\']\n [[OPTIONALLY] ENCLOSED BY \'char\']\n [ESCAPED BY \'char\']\n ]\n [LINES\n [STARTING BY \'string\']\n [TERMINATED BY \'string\']\n ]\n [IGNORE number LINES]\n [(col_name_or_user_var,...)]\n [SET col_name = expr,...]\n\nDescription\n-----------\n\nLOAD DATA INFILE is unsafe for statement-based replication.\n\nReads rows from a text file into the designated table on the database at a\nvery high speed. The file name must be given as a literal string.\n\nFiles are written to disk using the SELECT INTO OUTFILE statement. You can\nthen read the files back into a table using the LOAD DATA INFILE statement.\nThe FIELDS and LINES clauses are the same in both statements. These clauses\nare optional, but if both are specified then the FIELDS clause must precede\nLINES.\n\nExecuting this statement activates INSERT triggers.\n\nOne must have the FILE privilege to be able to execute LOAD DATA. This is the\nensure the normal users will not attempt to read system files.\n\nNote that MariaDB\'s systemd unit file restricts access to /home, /root, and\n/run/user by default. See Configuring access to home directories.\n\nLOAD DATA LOCAL INFILE\n----------------------\n\nWhen you execute the LOAD DATA INFILE statement, MariaDB Server attempts to\nread the input file from its own file system. In contrast, when you execute\nthe LOAD DATA LOCAL INFILE statement, the client attempts to read the input\nfile from its file system, and it sends the contents of the input file to the\nMariaDB Server. This allows you to load files from the client\'s local file\nsystem into the database.\n\nIn the event that you don\'t want to permit this operation (such as for\nsecurity reasons), you can disable the LOAD DATA LOCAL INFILE statement on\neither the server or the client.\n\n* The LOAD DATA LOCAL INFILE statement can be disabled on the server by\nsetting the local_infile system variable to 0.\n* The LOAD DATA LOCAL INFILE statement can be disabled on the client. If you\nare using MariaDB Connector/C, this can be done by unsetting the\nCLIENT_LOCAL_FILES capability flag with the mysql_real_connect function or by\nunsetting the MYSQL_OPT_LOCAL_INFILE option with mysql_optionsv function. If\nyou are using a different client or client library, then see the documentation\nfor your specific client or client library to determine how it handles the\nLOAD DATA LOCAL INFILE statement.\n\nIf the LOAD DATA LOCAL INFILE statement is disabled by either the server or\nthe client and if the user attempts to execute it, then the server will cause\nthe statement to fail with the following error message:\n\nThe used command is not allowed with this MariaDB version\n\nNote that it is not entirely accurate to say that the MariaDB version does not\nsupport the command. It would be more accurate to say that the MariaDB\nconfiguration does not support the command. See MDEV-20500 for more\ninformation.\n\nFrom MariaDB 10.5.2, the error message is more accurate:\n\nThe used command is not allowed because the MariaDB server or client \n has disabled the local infile capability\n\nREPLACE and IGNORE\n------------------\n\nIn cases where you load data from a file into a table that already contains\ndata and has a primary key, you may encounter issues where the statement\nattempts to insert a row with a primary key that already exists. When this\nhappens, the statement fails with Error 1064, protecting the data already on\nthe table. In cases where you want MariaDB to overwrite duplicates, use the\nREPLACE keyword.\n\nThe REPLACE keyword works like the REPLACE statement. Here, the statement\nattempts to load the data from the file. If the row does not exist, it adds it\nto the table. If the row contains an existing Primary Key, it replaces the\ntable data. That is, in the event of a conflict, it assumes the file contains\nthe desired row.\n\nThis operation can cause a degradation in load speed by a factor of 20 or more\nif the part that has already been loaded is larger than the capacity of the\nInnoDB Buffer Pool. This happens because it causes a lot of turnaround in the\nbuffer pool.\n\nUse the IGNORE keyword when you want to skip any rows that contain a\nconflicting primary key. Here, the statement attempts to load the data from\nthe file. If the row does not exist, it adds it to the table. If the row\ncontains an existing primary key, it ignores the addition request and moves on\nto the next. That is, in the event of a conflict, it assumes the table\ncontains the desired row.\n\nCharacter-sets\n--------------\n\nWhen the statement opens the file, it attempts to read the contents using the\ndefault character-set, as defined by the character_set_database system\nvariable.\n\nIn the cases where the file was written using a character-set other than the\ndefault, you can specify the character-set to use with the CHARACTER SET\nclause in the statement. It ignores character-sets specified by the SET NAMES\nstatement and by the character_set_client system variable. Setting the\nCHARACTER SET clause to a value of binary indicates \"no conversion.\"\n\nThe statement interprets all fields in the file as having the same\ncharacter-set, regardless of the column data type. To properly interpret file\ncontents, you must ensure that it was written with the correct character-set.\nIf you write a data file with mysqldump -T or with the SELECT INTO OUTFILE\nstatement with the mysql client, be sure to use the --default-character-set\noption, so that the output is written with the desired character-set.\n\nWhen using mixed character sets, use the CHARACTER SET clause in both SELECT\nINTO OUTFILE and LOAD DATA INFILE to ensure that MariaDB correctly interprets\nthe escape sequences.\n\nThe character_set_filesystem system variable controls the interpretation of\nthe filename.\n\nIt is currently not possible to load data files that use the ucs2 character\nset.\n\nPreprocessing Inputs\n--------------------\n\ncol_name_or_user_var can be a column name, or a user variable. In the case of\na variable, the SET statement can be used to preprocess the value before\nloading into the table.\n\nPriority and Concurrency\n------------------------\n\nIn storage engines that perform table-level locking (MyISAM, MEMORY and\nMERGE), using the LOW_PRIORITY keyword, MariaDB delays insertions until no\nother clients are reading from the table. Alternatively, when using the MyISAM\nstorage engine, you can use the CONCURRENT keyword to perform concurrent\ninsertion.\n\nThe LOW_PRIORITY and CONCURRENT keywords are mutually exclusive. They cannot\nbe used in the same statement.\n\nProgress Reporting\n------------------\n\nThe LOAD DATA INFILE statement supports progress reporting. You may find this\nuseful when dealing with long-running operations. Using another client you can\nissue a SHOW PROCESSLIST query to check the progress of the data load.\n\nUsing mariadb-import/mysqlimport\n--------------------------------\n\nMariaDB ships with a separate utility for loading data from files:\nmariadb-import (or mysqlimport before MariaDB 10.5). It operates by sending\nLOAD DATA INFILE statements to the server.\n\nUsing mariadb-import/mysqlimport you can compress the file using the\n--compress option, to get better performance over slow networks, providing\nboth the client and server support the compressed protocol. Use the --local\noption to load from the local file system.\n\nIndexing\n--------\n\nIn cases where the storage engine supports ALTER TABLE... DISABLE KEYS\nstatements (MyISAM and Aria), the LOAD DATA INFILE statement automatically\ndisables indexes during the execution.\n\nExamples\n--------\n\nYou have a file with this content (note the the separator is \',\', not tab,\nwhich is the default):\n\n2,2\n3,3\n4,4\n5,5\n6,8\n\nCREATE TABLE t1 (a int, b int, c int, d int);\nLOAD DATA LOCAL INFILE \n \'/tmp/loaddata7.dat\' into table t1 fields terminated by \',\' (a,b) set c=a+b;\nSELECT * FROM t1;\n+------+------+------+\n| a | b | c |\n+------+------+------+\n| 2 | 2 | 4 |\n| 3 | 3 | 6 |\n| 4 | 4 | 8 |\n| 5 | 5 | 10 |\n| 6 | 8 | 14 |\n+------+------+------+\n\nAnother example, given the following data (the separator is a tab):\n\n1 a\n2 b\n\nThe value of the first column is doubled before loading:\n\nLOAD DATA INFILE \'ld.txt\' INTO TABLE ld (@i,v) SET i=@i*2;\n\nSELECT * FROM ld;\n+------+------+\n| i | v |\n+------+------+\n| 2 | a |\n| 4 | b |\n+------+------+\n\nURL: https://mariadb.com/kb/en/load-data-infile/','','https://mariadb.com/kb/en/load-data-infile/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (449,27,'LOAD DATA INFILE','Syntax\n------\n\nLOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE \'file_name\'\n [REPLACE | IGNORE]\n INTO TABLE tbl_name\n [CHARACTER SET charset_name]\n [{FIELDS | COLUMNS}\n [TERMINATED BY \'string\']\n [[OPTIONALLY] ENCLOSED BY \'char\']\n [ESCAPED BY \'char\']\n ]\n [LINES\n [STARTING BY \'string\']\n [TERMINATED BY \'string\']\n ]\n [IGNORE number LINES]\n [(col_name_or_user_var,...)]\n [SET col_name = expr,...]\n\nDescription\n-----------\n\nLOAD DATA INFILE is unsafe for statement-based replication.\n\nReads rows from a text file into the designated table on the database at a\nvery high speed. The file name must be given as a literal string.\n\nFiles are written to disk using the SELECT INTO OUTFILE statement. You can\nthen read the files back into a table using the LOAD DATA INFILE statement.\nThe FIELDS and LINES clauses are the same in both statements. These clauses\nare optional, but if both are specified then the FIELDS clause must precede\nLINES.\n\nExecuting this statement activates INSERT triggers.\n\nOne must have the FILE privilege to be able to execute LOAD DATA INFILE. This\nis to ensure normal users cannot read system files. LOAD DATA LOCAL INFILE\ndoes not have this requirement.\n\nIf the secure_file_priv system variable is set (by default it is not), the\nloaded file must be present in the specified directory.\n\nNote that MariaDB\'s systemd unit file restricts access to /home, /root, and\n/run/user by default. See Configuring access to home directories.\n\nLOAD DATA LOCAL INFILE\n----------------------\n\nWhen you execute the LOAD DATA INFILE statement, MariaDB Server attempts to\nread the input file from its own file system. By contrast, when you execute\nthe LOAD DATA LOCAL INFILE statement, the client attempts to read the input\nfile from its file system, and it sends the contents of the input file to the\nMariaDB Server. This allows you to load files from the client\'s local file\nsystem into the database.\n\nIf you don\'t want to permit this operation (perhaps for security reasons), you\ncan disable the LOAD DATA LOCAL INFILE statement on either the server or the\nclient.\n\n* The LOAD DATA LOCAL INFILE statement can be disabled on the server by\nsetting the local_infile system variable to 0.\n* The LOAD DATA LOCAL INFILE statement can be disabled on the client. If you\nare using MariaDB Connector/C, this can be done by unsetting the\nCLIENT_LOCAL_FILES capability flag with the mysql_real_connect function or by\nunsetting the MYSQL_OPT_LOCAL_INFILE option with mysql_optionsv function. If\nyou are using a different client or client library, then see the documentation\nfor your specific client or client library to determine how it handles the\nLOAD DATA LOCAL INFILE statement.\n\nIf the LOAD DATA LOCAL INFILE statement is disabled by either the server or\nthe client and if the user attempts to execute it, then the server will cause\nthe statement to fail with the following error message:\n\nThe used command is not allowed with this MariaDB version\n\nNote that it is not entirely accurate to say that the MariaDB version does not\nsupport the command. It would be more accurate to say that the MariaDB\nconfiguration does not support the command. See MDEV-20500 for more\ninformation.\n\nFrom MariaDB 10.5.2, the error message is more accurate:\n\nThe used command is not allowed because the MariaDB server or client \n has disabled the local infile capability\n\nREPLACE and IGNORE\n------------------\n\nIf you load data from a file into a table that already contains data and has a\nprimary key, you may encounter issues where the statement attempts to insert a\nrow with a primary key that already exists. When this happens, the statement\nfails with Error 1064, protecting the data already on the table. If you want\nMariaDB to overwrite duplicates, use the REPLACE keyword.\n\nThe REPLACE keyword works like the REPLACE statement. Here, the statement\nattempts to load the data from the file. If the row does not exist, it adds it\nto the table. If the row contains an existing primary key, it replaces the\ntable data. That is, in the event of a conflict, it assumes the file contains\nthe desired row.\n\nThis operation can cause a degradation in load speed by a factor of 20 or more\nif the part that has already been loaded is larger than the capacity of the\nInnoDB Buffer Pool. This happens because it causes a lot of turnaround in the\nbuffer pool.\n\nUse the IGNORE keyword when you want to skip any rows that contain a\nconflicting primary key. Here, the statement attempts to load the data from\nthe file. If the row does not exist, it adds it to the table. If the row\ncontains an existing primary key, it ignores the addition request and moves on\nto the next. That is, in the event of a conflict, it assumes the table\ncontains the desired row.\n\nCharacter-sets\n--------------\n\nWhen the statement opens the file, it attempts to read the contents using the\ndefault character-set, as defined by the character_set_database system\nvariable.\n\nIn the cases where the file was written using a character-set other than the\ndefault, you can specify the character-set to use with the CHARACTER SET\nclause in the statement. It ignores character-sets specified by the SET NAMES\nstatement and by the character_set_client system variable. Setting the\nCHARACTER SET clause to a value of binary indicates \"no conversion.\"\n\nThe statement interprets all fields in the file as having the same\ncharacter-set, regardless of the column data type. To properly interpret file\ncontents, you must ensure that it was written with the correct character-set.\nIf you write a data file with mysqldump -T or with the SELECT INTO OUTFILE\nstatement with the mysql client, be sure to use the --default-character-set\noption, so that the output is written with the desired character-set.\n\nWhen using mixed character sets, use the CHARACTER SET clause in both SELECT\nINTO OUTFILE and LOAD DATA INFILE to ensure that MariaDB correctly interprets\nthe escape sequences.\n\nThe character_set_filesystem system variable controls the interpretation of\nthe filename.\n\nIt is currently not possible to load data files that use the ucs2 character\nset.\n\nPreprocessing Inputs\n--------------------\n\ncol_name_or_user_var can be a column name, or a user variable. In the case of\na variable, the SET statement can be used to preprocess the value before\nloading into the table.\n\nPriority and Concurrency\n------------------------\n\nIn storage engines that perform table-level locking (MyISAM, MEMORY and\nMERGE), using the LOW_PRIORITY keyword, MariaDB delays insertions until no\nother clients are reading from the table. Alternatively, when using the MyISAM\nstorage engine, you can use the CONCURRENT keyword to perform concurrent\ninsertion.\n\nThe LOW_PRIORITY and CONCURRENT keywords are mutually exclusive. They cannot\nbe used in the same statement.\n\nProgress Reporting\n------------------\n\nThe LOAD DATA INFILE statement supports progress reporting. You may find this\nuseful when dealing with long-running operations. Using another client you can\nissue a SHOW PROCESSLIST query to check the progress of the data load.\n\nUsing mariadb-import/mysqlimport\n--------------------------------\n\nMariaDB ships with a separate utility for loading data from files:\nmariadb-import (or mysqlimport before MariaDB 10.5). It operates by sending\nLOAD DATA INFILE statements to the server.\n\nUsing mariadb-import/mysqlimport you can compress the file using the\n--compress option, to get better performance over slow networks, providing\nboth the client and server support the compressed protocol. Use the --local\noption to load from the local file system.\n\nIndexing\n--------\n\nIn cases where the storage engine supports ALTER TABLE... DISABLE KEYS\nstatements (MyISAM and Aria), the LOAD DATA INFILE statement automatically\ndisables indexes during the execution.\n\nExamples\n--------\n\nYou have a file with this content (note the the separator is \',\', not tab,\nwhich is the default):\n\n2,2\n3,3\n4,4\n5,5\n6,8\n\nCREATE TABLE t1 (a int, b int, c int, d int, PRIMARY KEY (a));\nLOAD DATA LOCAL INFILE \n \'/tmp/loaddata7.dat\' INTO TABLE t1 FIELDS TERMINATED BY \',\' (a,b) SET c=a+b;\nSELECT * FROM t1;\n+------+------+------+\n| a | b | c |\n+------+------+------+\n| 2 | 2 | 4 |\n| 3 | 3 | 6 |\n| 4 | 4 | 8 |\n| 5 | 5 | 10 |\n| 6 | 8 | 14 |\n+------+------+------+\n\nAnother example, given the following data (the separator is a tab):\n\n1 a\n2 b\n\nThe value of the first column is doubled before loading:\n\nLOAD DATA INFILE \'ld.txt\' INTO TABLE ld (@i,v) SET i=@i*2;\n\nSELECT * FROM ld;\n+------+------+\n| i | v |\n+------+------+\n| 2 | a |\n| 4 | b |\n+------+------+\n\nURL: https://mariadb.com/kb/en/load-data-infile/','','https://mariadb.com/kb/en/load-data-infile/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (450,27,'LOAD XML','Syntax\n------\n\nLOAD XML [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE \'file_name\'\n [REPLACE | IGNORE]\n INTO TABLE [db_name.]tbl_name\n [CHARACTER SET charset_name]\n [ROWS IDENTIFIED BY \'\']\n [IGNORE number {LINES | ROWS}]\n [(column_or_user_var,...)]\n [SET col_name = expr,...]\n\nDescription\n-----------\n\nThe LOAD XML statement reads data from an XML file into a table. The file_name\nmust be given as a literal string. The tagname in the optional ROWS IDENTIFIED\nBY clause must also be given as a literal string, and must be surrounded by\nangle brackets (< and >).\n\nLOAD XML acts as the complement of running the mysql client in XML output mode\n(that is, starting the client with the --xml option). To write data from a\ntable to an XML file, use a command such as the following one from the system\nshell:\n\nshell> mysql --xml -e \'SELECT * FROM mytable\' > file.xml\n\nTo read the file back into a table, use LOAD XML INFILE. By default, the \nelement is considered to be the equivalent of a database table row; this can\nbe changed using the ROWS IDENTIFIED BY clause.\n\nThis statement supports three different XML formats:\n\n* Column names as attributes and column values as attribute values:\n\n\n\n* Column names as tags and column values as the content of these tags:\n\n\n value1\n value2\n\n\n* Column names are the name attributes of tags, and values are\n the contents of these tags:\n\n\n value1\n value2\n\n\nThis is the format used by other tools, such as mysqldump.\n\nAll 3 formats can be used in the same XML file; the import routine\nautomatically detects the format for each row and interprets it correctly.\nTags are matched based on the tag or attribute name and the column name.\n\nThe following clauses work essentially the same way for LOAD XML as they do\nfor LOAD DATA:\n\n* LOW_PRIORITY or CONCURRENT\n* LOCAL\n* REPLACE or IGNORE\n* CHARACTER SET\n* (column_or_user_var,...)\n* SET\n\nSee LOAD DATA for more information about these clauses.\n\nThe IGNORE number LINES or IGNORE number ROWS clause causes the first number\nrows in the XML file to be skipped. It is analogous to the LOAD DATA\nstatement\'s IGNORE ... LINES clause.\n\nIf the LOW_PRIORITY keyword is used, insertions are delayed until no other\nclients are reading from the table. The CONCURRENT keyword allowes the use of\nconcurrent inserts. These clauses cannot be specified together.\n\nThis statement activates INSERT triggers.\n\nURL: https://mariadb.com/kb/en/load-xml/','','https://mariadb.com/kb/en/load-xml/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (451,27,'Concurrent Inserts','The MyISAM storage engine supports concurrent inserts. This feature allows\nSELECT statements to be executed during INSERT operations, reducing contention.\n\nWhether concurrent inserts can be used or not depends on the value of the\nconcurrent_insert server system variable:\n\n* NEVER (0) disables concurrent inserts.\n* AUTO (1) allows concurrent inserts only when the target table has no free\nblocks (no data in the middle of the table has been deleted after the last\nOPTIMIZE TABLE). This is the default.\n* ALWAYS (2) always enables concurrent inserts, in which case new rows are\nadded at the end of a table if the table is being used by another thread.\n\nIf the binary log is used, CREATE TABLE ... SELECT and INSERT ... SELECT\nstatements cannot use concurrent inserts. These statements acquire a read lock\non the table, so concurrent inserts will need to wait. This way the log can be\nsafely used to restore data.\n\nConcurrent inserts are not used by replicas with the row based replication\n(see binary log formats).\n\nIf an INSERT statement contain the HIGH_PRIORITY clause, concurrent inserts\ncannot be used. INSERT ... DELAYED is usually unneeded if concurrent inserts\nare enabled.\n\nLOAD DATA INFILE uses concurrent inserts if the CONCURRENT keyword is\nspecified and concurrent_insert is not NEVER. This makes the statement slower\n(even if no other sessions access the table) but reduces contention.\n\nLOCK TABLES allows non-conflicting concurrent inserts if a READ LOCAL lock is\nused. Concurrent inserts are not allowed if the LOCAL keyword is omitted.\n\nNotes\n-----\n\nThe decision to enable concurrent insert for a table is done when the table is\nopened. If you change the value of concurrent_insert it will only affect new\nopened tables. If you want it to work for also for tables in use or cached,\nyou should do FLUSH TABLES after setting the variable.\n\nURL: https://mariadb.com/kb/en/concurrent-inserts/','','https://mariadb.com/kb/en/concurrent-inserts/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (452,27,'HIGH_PRIORITY and LOW_PRIORITY','The InnoDB storage engine uses row-level locking to ensure data integrity.\nHowever some storage engines (such as MEMORY, MyISAM, Aria and MERGE) lock the\nwhole table to prevent conflicts. These storage engines use two separate\nqueues to remember pending statements; one is for SELECTs and the other one is\nfor write statements (INSERT, DELETE, UPDATE). By default, the latter has a\nhigher priority.\n\nTo give write operations a lower priority, the low_priority_updates server\nsystem variable can be set to ON. The option is available on both the global\nand session levels, and it can be set at startup or via the SET statement.\n\nWhen too many table locks have been set by write statements, some pending\nSELECTs are executed. The maximum number of write locks that can be acquired\nbefore this happens is determined by the max_write_lock_count server system\nvariable, which is dynamic.\n\nIf write statements have a higher priority (default), the priority of\nindividual write statements (INSERT, REPLACE, UPDATE, DELETE) can be changed\nvia the LOW_PRIORITY attribute, and the priority of a SELECT statement can be\nraised via the HIGH_PRIORITY attribute. Also, LOCK TABLES supports a\nLOW_PRIORITY attribute for WRITE locks.\n\nIf read statements have a higher priority, the priority of an INSERT can be\nchanged via the HIGH_PRIORITY attribute. However, the priority of other write\nstatements cannot be raised individually.\n\nThe use of LOW_PRIORITY or HIGH_PRIORITY for an INSERT prevents Concurrent\nInserts from being used.\n\nURL: https://mariadb.com/kb/en/high_priority-and-low_priority/','','https://mariadb.com/kb/en/high_priority-and-low_priority/'); @@ -642,13 +642,13 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (538,31,'MINUTE','Syntax\n------\n\nMINUTE(time)\n\nDescription\n-----------\n\nReturns the minute for time, in the range 0 to 59.\n\nExamples\n--------\n\nSELECT MINUTE(\'2013-08-03 11:04:03\');\n+-------------------------------+\n| MINUTE(\'2013-08-03 11:04:03\') |\n+-------------------------------+\n| 4 |\n+-------------------------------+\n\nSELECT MINUTE (\'23:12:50\');\n+---------------------+\n| MINUTE (\'23:12:50\') |\n+---------------------+\n| 12 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/minute/','','https://mariadb.com/kb/en/minute/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (539,31,'MONTH','Syntax\n------\n\nMONTH(date)\n\nDescription\n-----------\n\nReturns the month for date in the range 1 to 12 for January to December, or 0\nfor dates such as \'0000-00-00\' or \'2008-00-00\' that have a zero month part.\n\nExamples\n--------\n\nSELECT MONTH(\'2019-01-03\');\n+---------------------+\n| MONTH(\'2019-01-03\') |\n+---------------------+\n| 1 |\n+---------------------+\n\nSELECT MONTH(\'2019-00-03\');\n+---------------------+\n| MONTH(\'2019-00-03\') |\n+---------------------+\n| 0 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/month/','','https://mariadb.com/kb/en/month/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (540,31,'MONTHNAME','Syntax\n------\n\nMONTHNAME(date)\n\nDescription\n-----------\n\nReturns the full name of the month for date. The language used for the name is\ncontrolled by the value of the lc_time_names system variable. See server\nlocale for more on the supported locales.\n\nExamples\n--------\n\nSELECT MONTHNAME(\'2019-02-03\');\n+-------------------------+\n| MONTHNAME(\'2019-02-03\') |\n+-------------------------+\n| February |\n+-------------------------+\n\nChanging the locale:\n\nSET lc_time_names = \'fr_CA\';\n\nSELECT MONTHNAME(\'2019-05-21\');\n+-------------------------+\n| MONTHNAME(\'2019-05-21\') |\n+-------------------------+\n| mai |\n+-------------------------+\n\nURL: https://mariadb.com/kb/en/monthname/','','https://mariadb.com/kb/en/monthname/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (541,31,'NOW','Syntax\n------\n\nNOW([precision])\nCURRENT_TIMESTAMP\nCURRENT_TIMESTAMP([precision])\nLOCALTIME, LOCALTIME([precision])\nLOCALTIMESTAMP\nLOCALTIMESTAMP([precision])\n\nDescription\n-----------\n\nReturns the current date and time as a value in \'YYYY-MM-DD HH:MM:SS\' or\nYYYYMMDDHHMMSS.uuuuuu format, depending on whether the function is used in a\nstring or numeric context. The value is expressed in the current time zone.\n\nThe optional precision determines the microsecond precision. See Microseconds\nin MariaDB.\n\nNOW() (or its synonyms) can be used as the default value for TIMESTAMP columns\nas well as, since MariaDB 10.0.1, DATETIME columns. Before MariaDB 10.0.1, it\nwas only possible for a single TIMESTAMP column per table to contain the\nCURRENT_TIMESTAMP as its default.\n\nWhen displayed in the INFORMATION_SCHEMA.COLUMNS table, a default CURRENT\nTIMESTAMP is displayed as CURRENT_TIMESTAMP up until MariaDB 10.2.2, and as\ncurrent_timestamp() from MariaDB 10.2.3, due to to MariaDB 10.2 accepting\nexpressions in the DEFAULT clause.\n\nExamples\n--------\n\nSELECT NOW();\n+---------------------+\n| NOW() |\n+---------------------+\n| 2010-03-27 13:13:25 |\n+---------------------+\n\nSELECT NOW() + 0;\n+-----------------------+\n| NOW() + 0 |\n+-----------------------+\n| 20100327131329.000000 |\n+-----------------------+\n\nWith precision:\n\nSELECT CURRENT_TIMESTAMP(2);\n+------------------------+\n| CURRENT_TIMESTAMP(2) |\n+------------------------+\n| 2018-07-10 09:47:26.24 |\n+------------------------+\n\nUsed as a default TIMESTAMP:\n\nCREATE TABLE t (createdTS TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP);\n\nFrom MariaDB 10.2.2:\n\nSELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=\'test\'\n AND COLUMN_NAME LIKE \'%ts%\'\\G\n*************************** 1. row ***************************\n TABLE_CATALOG: def\n TABLE_SCHEMA: test\n TABLE_NAME: t\n COLUMN_NAME: ts\n ORDINAL_POSITION: 1\n COLUMN_DEFAULT: current_timestamp()\n...\n\n<= MariaDB 10.2.1\n\nSELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=\'test\'\n AND COLUMN_NAME LIKE \'%ts%\'\\G\n*************************** 1. row ***************************\n TABLE_CATALOG: def\n TABLE_SCHEMA: test\n TABLE_NAME: t\n COLUMN_NAME: createdTS\n ORDINAL_POSITION: 1\n COLUMN_DEFAULT: CURRENT_TIMESTAMP\n...\n\nURL: https://mariadb.com/kb/en/now/','','https://mariadb.com/kb/en/now/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (541,31,'NOW','Syntax\n------\n\nNOW([precision])\nCURRENT_TIMESTAMP\nCURRENT_TIMESTAMP([precision])\nLOCALTIME, LOCALTIME([precision])\nLOCALTIMESTAMP\nLOCALTIMESTAMP([precision])\n\nDescription\n-----------\n\nReturns the current date and time as a value in \'YYYY-MM-DD HH:MM:SS\' or\nYYYYMMDDHHMMSS.uuuuuu format, depending on whether the function is used in a\nstring or numeric context. The value is expressed in the current time zone.\n\nThe optional precision determines the microsecond precision. See Microseconds\nin MariaDB.\n\nNOW() (or its synonyms) can be used as the default value for TIMESTAMP columns\nas well as, since MariaDB 10.0.1, DATETIME columns. Before MariaDB 10.0.1, it\nwas only possible for a single TIMESTAMP column per table to contain the\nCURRENT_TIMESTAMP as its default.\n\nWhen displayed in the INFORMATION_SCHEMA.COLUMNS table, a default CURRENT\nTIMESTAMP is displayed as CURRENT_TIMESTAMP up until MariaDB 10.2.2, and as\ncurrent_timestamp() from MariaDB 10.2.3, due to to MariaDB 10.2 accepting\nexpressions in the DEFAULT clause.\n\nChanging the timestamp system variable with a SET timestamp statement affects\nthe value returned by NOW(), but not by SYSDATE().\n\nExamples\n--------\n\nSELECT NOW();\n+---------------------+\n| NOW() |\n+---------------------+\n| 2010-03-27 13:13:25 |\n+---------------------+\n\nSELECT NOW() + 0;\n+-----------------------+\n| NOW() + 0 |\n+-----------------------+\n| 20100327131329.000000 |\n+-----------------------+\n\nWith precision:\n\nSELECT CURRENT_TIMESTAMP(2);\n+------------------------+\n| CURRENT_TIMESTAMP(2) |\n+------------------------+\n| 2018-07-10 09:47:26.24 |\n+------------------------+\n\nUsed as a default TIMESTAMP:\n\nCREATE TABLE t (createdTS TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP);\n\nFrom MariaDB 10.2.2:\n\nSELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=\'test\'\n AND COLUMN_NAME LIKE \'%ts%\'\\G\n*************************** 1. row ***************************\n TABLE_CATALOG: def\n TABLE_SCHEMA: test\n TABLE_NAME: t\n COLUMN_NAME: ts\n ORDINAL_POSITION: 1\n COLUMN_DEFAULT: current_timestamp()\n...\n\n<= MariaDB 10.2.1\n\nSELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=\'test\'\n AND COLUMN_NAME LIKE \'%ts%\'\\G\n*************************** 1. row ***************************\n TABLE_CATALOG: def\n TABLE_SCHEMA: test\n TABLE_NAME: t\n COLUMN_NAME: createdTS\n ORDINAL_POSITION: 1\n COLUMN_DEFAULT: CURRENT_TIMESTAMP\n...\n\nURL: https://mariadb.com/kb/en/now/','','https://mariadb.com/kb/en/now/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (542,31,'PERIOD_ADD','Syntax\n------\n\nPERIOD_ADD(P,N)\n\nDescription\n-----------\n\nAdds N months to period P. P is in the format YYMM or YYYYMM, and is not a\ndate value. If P contains a two-digit year, values from 00 to 69 are converted\nto from 2000 to 2069, while values from 70 are converted to 1970 upwards.\n\nReturns a value in the format YYYYMM.\n\nExamples\n--------\n\nSELECT PERIOD_ADD(200801,2);\n+----------------------+\n| PERIOD_ADD(200801,2) |\n+----------------------+\n| 200803 |\n+----------------------+\n\nSELECT PERIOD_ADD(6910,2);\n+--------------------+\n| PERIOD_ADD(6910,2) |\n+--------------------+\n| 206912 |\n+--------------------+\n\nSELECT PERIOD_ADD(7010,2);\n+--------------------+\n| PERIOD_ADD(7010,2) |\n+--------------------+\n| 197012 |\n+--------------------+\n\nURL: https://mariadb.com/kb/en/period_add/','','https://mariadb.com/kb/en/period_add/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (543,31,'PERIOD_DIFF','Syntax\n------\n\nPERIOD_DIFF(P1,P2)\n\nDescription\n-----------\n\nReturns the number of months between periods P1 and P2. P1 and P2 can be in\nthe format YYMM or YYYYMM, and are not date values.\n\nIf P1 or P2 contains a two-digit year, values from 00 to 69 are converted to\nfrom 2000 to 2069, while values from 70 are converted to 1970 upwards.\n\nExamples\n--------\n\nSELECT PERIOD_DIFF(200802,200703);\n+----------------------------+\n| PERIOD_DIFF(200802,200703) |\n+----------------------------+\n| 11 |\n+----------------------------+\n\nSELECT PERIOD_DIFF(6902,6803);\n+------------------------+\n| PERIOD_DIFF(6902,6803) |\n+------------------------+\n| 11 |\n+------------------------+\n\nSELECT PERIOD_DIFF(7002,6803);\n+------------------------+\n| PERIOD_DIFF(7002,6803) |\n+------------------------+\n| -1177 |\n+------------------------+\n\nURL: https://mariadb.com/kb/en/period_diff/','','https://mariadb.com/kb/en/period_diff/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (544,31,'QUARTER','Syntax\n------\n\nQUARTER(date)\n\nDescription\n-----------\n\nReturns the quarter of the year for date, in the range 1 to 4. Returns 0 if\nmonth contains a zero value, or NULL if the given value is not otherwise a\nvalid date (zero values are accepted).\n\nExamples\n--------\n\nSELECT QUARTER(\'2008-04-01\');\n+-----------------------+\n| QUARTER(\'2008-04-01\') |\n+-----------------------+\n| 2 |\n+-----------------------+\n\nSELECT QUARTER(\'2019-00-01\');\n+-----------------------+\n| QUARTER(\'2019-00-01\') |\n+-----------------------+\n| 0 |\n+-----------------------+\n\nURL: https://mariadb.com/kb/en/quarter/','','https://mariadb.com/kb/en/quarter/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (545,31,'SECOND','Syntax\n------\n\nSECOND(time)\n\nDescription\n-----------\n\nReturns the second for a given time (which can include microseconds), in the\nrange 0 to 59, or NULL if not given a valid time value.\n\nExamples\n--------\n\nSELECT SECOND(\'10:05:03\');\n+--------------------+\n| SECOND(\'10:05:03\') |\n+--------------------+\n| 3 |\n+--------------------+\n\nSELECT SECOND(\'10:05:01.999999\');\n+---------------------------+\n| SECOND(\'10:05:01.999999\') |\n+---------------------------+\n| 1 |\n+---------------------------+\n\nURL: https://mariadb.com/kb/en/second/','','https://mariadb.com/kb/en/second/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (546,31,'SEC_TO_TIME','Syntax\n------\n\nSEC_TO_TIME(seconds)\n\nDescription\n-----------\n\nReturns the seconds argument, converted to hours, minutes, and seconds, as a\nTIME value. The range of the result is constrained to that of the TIME data\ntype. A warning occurs if the argument corresponds to a value outside that\nrange.\n\nThe time will be returned in the format hh:mm:ss, or hhmmss if used in a\nnumeric calculation.\n\nExamples\n--------\n\nSELECT SEC_TO_TIME(12414);\n+--------------------+\n| SEC_TO_TIME(12414) |\n+--------------------+\n| 03:26:54 |\n+--------------------+\n\nSELECT SEC_TO_TIME(12414)+0;\n+----------------------+\n| SEC_TO_TIME(12414)+0 |\n+----------------------+\n| 32654 |\n+----------------------+\n\nSELECT SEC_TO_TIME(9999999);\n+----------------------+\n| SEC_TO_TIME(9999999) |\n+----------------------+\n| 838:59:59 |\n+----------------------+\n1 row in set, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+---------+------+-------------------------------------------+\n| Level | Code | Message |\n+---------+------+-------------------------------------------+\n| Warning | 1292 | Truncated incorrect time value: \'9999999\' |\n+---------+------+-------------------------------------------+\n\nURL: https://mariadb.com/kb/en/sec_to_time/','','https://mariadb.com/kb/en/sec_to_time/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (547,31,'STR_TO_DATE','Syntax\n------\n\nSTR_TO_DATE(str,format)\n\nDescription\n-----------\n\nThis is the inverse of the DATE_FORMAT() function. It takes a string str and a\nformat string format. STR_TO_DATE() returns a DATETIME value if the format\nstring contains both date and time parts, or a DATE or TIME value if the\nstring contains only date or time parts.\n\nThe date, time, or datetime values contained in str should be given in the\nformat indicated by format. If str contains an illegal date, time, or datetime\nvalue, STR_TO_DATE() returns NULL. An illegal value also produces a warning.\n\nThe options that can be used by STR_TO_DATE(), as well as its inverse\nDATE_FORMAT() and the FROM_UNIXTIME() function, are:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| %a | Short weekday name in current locale |\n| | (Variable lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %b | Short form month name in current locale. For |\n| | locale en_US this is one of: |\n| | Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov |\n| | or Dec. |\n+---------------------------+------------------------------------------------+\n| %c | Month with 1 or 2 digits. |\n+---------------------------+------------------------------------------------+\n| %D | Day with English suffix \'th\', \'nd\', \'st\' or |\n| | \'rd\'\'. (1st, 2nd, 3rd...). |\n+---------------------------+------------------------------------------------+\n| %d | Day with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %e | Day with 1 or 2 digits. |\n+---------------------------+------------------------------------------------+\n| %f | Microseconds 6 digits. |\n+---------------------------+------------------------------------------------+\n| %H | Hour with 2 digits between 00-23. |\n+---------------------------+------------------------------------------------+\n| %h | Hour with 2 digits between 01-12. |\n+---------------------------+------------------------------------------------+\n| %I | Hour with 2 digits between 01-12. |\n+---------------------------+------------------------------------------------+\n| %i | Minute with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %j | Day of the year (001-366) |\n+---------------------------+------------------------------------------------+\n| %k | Hour with 1 digits between 0-23. |\n+---------------------------+------------------------------------------------+\n| %l | Hour with 1 digits between 1-12. |\n+---------------------------+------------------------------------------------+\n| %M | Full month name in current locale (Variable |\n| | lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %m | Month with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %p | AM/PM according to current locale (Variable |\n| | lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %r | Time in 12 hour format, followed by AM/PM. |\n| | Short for \'%I:%i:%S %p\'. |\n+---------------------------+------------------------------------------------+\n| %S | Seconds with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %s | Seconds with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %T | Time in 24 hour format. Short for \'%H:%i:%S\'. |\n+---------------------------+------------------------------------------------+\n| %U | Week number (00-53), when first day of the |\n| | week is Sunday. |\n+---------------------------+------------------------------------------------+\n| %u | Week number (00-53), when first day of the |\n| | week is Monday. |\n+---------------------------+------------------------------------------------+\n| %V | Week number (01-53), when first day of the |\n| | week is Sunday. Used with %X. |\n+---------------------------+------------------------------------------------+\n| %v | Week number (01-53), when first day of the |\n| | week is Monday. Used with %x. |\n+---------------------------+------------------------------------------------+\n| %W | Full weekday name in current locale (Variable |\n| | lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %w | Day of the week. 0 = Sunday, 6 = Saturday. |\n+---------------------------+------------------------------------------------+\n| %X | Year with 4 digits when first day of the week |\n| | is Sunday. Used with %V. |\n+---------------------------+------------------------------------------------+\n| %x | Year with 4 digits when first day of the week |\n| | is Monday. Used with %v. |\n+---------------------------+------------------------------------------------+\n| %Y | Year with 4 digits. |\n+---------------------------+------------------------------------------------+\n| %y | Year with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %# | For str_to_date(), skip all numbers. |\n+---------------------------+------------------------------------------------+\n| %. | For str_to_date(), skip all punctation |\n| | characters. |\n+---------------------------+------------------------------------------------+\n| %@ | For str_to_date(), skip all alpha characters. |\n+---------------------------+------------------------------------------------+\n| %% | A literal % character. |\n+---------------------------+------------------------------------------------+\n\nExamples\n--------\n\nSELECT STR_TO_DATE(\'Wednesday, June 2, 2014\', \'%W, %M %e, %Y\');\n+---------------------------------------------------------+\n| STR_TO_DATE(\'Wednesday, June 2, 2014\', \'%W, %M %e, %Y\') |\n+---------------------------------------------------------+\n| 2014-06-02 |\n+---------------------------------------------------------+\n\nSELECT STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W, %M %e, %Y\');\n+--------------------------------------------------------------+\n| STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W, %M %e, %Y\') |\n+--------------------------------------------------------------+\n| NULL |\n+--------------------------------------------------------------+\n1 row in set, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+---------+------+-------------------------------------------------------------\n---------------------+\n| Level | Code | Message \n |\n+---------+------+-------------------------------------------------------------\n---------------------+\n| Warning | 1411 | Incorrect datetime value: \'Wednesday23423, June 2, 2014\'\nfor function str_to_date |\n+---------+------+-------------------------------------------------------------\n---------------------+\n\nSELECT STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W%#, %M %e, %Y\');\n+----------------------------------------------------------------+\n| STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W%#, %M %e, %Y\') |\n+----------------------------------------------------------------+\n| 2014-06-02 |\n+----------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/str_to_date/','','https://mariadb.com/kb/en/str_to_date/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (547,31,'STR_TO_DATE','Syntax\n------\n\nSTR_TO_DATE(str,format)\n\nDescription\n-----------\n\nThis is the inverse of the DATE_FORMAT() function. It takes a string str and a\nformat string format. STR_TO_DATE() returns a DATETIME value if the format\nstring contains both date and time parts, or a DATE or TIME value if the\nstring contains only date or time parts.\n\nThe date, time, or datetime values contained in str should be given in the\nformat indicated by format. If str contains an illegal date, time, or datetime\nvalue, STR_TO_DATE() returns NULL. An illegal value also produces a warning.\n\nUnder specific SQL_MODE settings an error may also be generated if the str\nisn\'t a valid date:\n\n* ALLOW_INVALID_DATES\n* NO_ZERO_DATE\n* NO_ZERO_IN_DATE\n\nThe options that can be used by STR_TO_DATE(), as well as its inverse\nDATE_FORMAT() and the FROM_UNIXTIME() function, are:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| %a | Short weekday name in current locale |\n| | (Variable lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %b | Short form month name in current locale. For |\n| | locale en_US this is one of: |\n| | Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov |\n| | or Dec. |\n+---------------------------+------------------------------------------------+\n| %c | Month with 1 or 2 digits. |\n+---------------------------+------------------------------------------------+\n| %D | Day with English suffix \'th\', \'nd\', \'st\' or |\n| | \'rd\'\'. (1st, 2nd, 3rd...). |\n+---------------------------+------------------------------------------------+\n| %d | Day with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %e | Day with 1 or 2 digits. |\n+---------------------------+------------------------------------------------+\n| %f | Microseconds 6 digits. |\n+---------------------------+------------------------------------------------+\n| %H | Hour with 2 digits between 00-23. |\n+---------------------------+------------------------------------------------+\n| %h | Hour with 2 digits between 01-12. |\n+---------------------------+------------------------------------------------+\n| %I | Hour with 2 digits between 01-12. |\n+---------------------------+------------------------------------------------+\n| %i | Minute with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %j | Day of the year (001-366) |\n+---------------------------+------------------------------------------------+\n| %k | Hour with 1 digits between 0-23. |\n+---------------------------+------------------------------------------------+\n| %l | Hour with 1 digits between 1-12. |\n+---------------------------+------------------------------------------------+\n| %M | Full month name in current locale (Variable |\n| | lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %m | Month with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %p | AM/PM according to current locale (Variable |\n| | lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %r | Time in 12 hour format, followed by AM/PM. |\n| | Short for \'%I:%i:%S %p\'. |\n+---------------------------+------------------------------------------------+\n| %S | Seconds with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %s | Seconds with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %T | Time in 24 hour format. Short for \'%H:%i:%S\'. |\n+---------------------------+------------------------------------------------+\n| %U | Week number (00-53), when first day of the |\n| | week is Sunday. |\n+---------------------------+------------------------------------------------+\n| %u | Week number (00-53), when first day of the |\n| | week is Monday. |\n+---------------------------+------------------------------------------------+\n| %V | Week number (01-53), when first day of the |\n| | week is Sunday. Used with %X. |\n+---------------------------+------------------------------------------------+\n| %v | Week number (01-53), when first day of the |\n| | week is Monday. Used with %x. |\n+---------------------------+------------------------------------------------+\n| %W | Full weekday name in current locale (Variable |\n| | lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %w | Day of the week. 0 = Sunday, 6 = Saturday. |\n+---------------------------+------------------------------------------------+\n| %X | Year with 4 digits when first day of the week |\n| | is Sunday. Used with %V. |\n+---------------------------+------------------------------------------------+\n| %x | Year with 4 digits when first day of the week |\n| | is Monday. Used with %v. |\n+---------------------------+------------------------------------------------+\n| %Y | Year with 4 digits. |\n+---------------------------+------------------------------------------------+\n| %y | Year with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %# | For str_to_date(), skip all numbers. |\n+---------------------------+------------------------------------------------+\n| %. | For str_to_date(), skip all punctation |\n| | characters. |\n+---------------------------+------------------------------------------------+\n| %@ | For str_to_date(), skip all alpha characters. |\n+---------------------------+------------------------------------------------+\n| %% | A literal % character. |\n+---------------------------+------------------------------------------------+\n\nExamples\n--------\n\nSELECT STR_TO_DATE(\'Wednesday, June 2, 2014\', \'%W, %M %e, %Y\');\n+---------------------------------------------------------+\n| STR_TO_DATE(\'Wednesday, June 2, 2014\', \'%W, %M %e, %Y\') |\n+---------------------------------------------------------+\n| 2014-06-02 |\n+---------------------------------------------------------+\n\nSELECT STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W, %M %e, %Y\');\n+--------------------------------------------------------------+\n| STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W, %M %e, %Y\') |\n+--------------------------------------------------------------+\n| NULL |\n+--------------------------------------------------------------+\n1 row in set, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+---------+------+-------------------------------------------------------------\n---------------------+\n| Level | Code | Message \n |\n+---------+------+-------------------------------------------------------------\n---------------------+\n| Warning | 1411 | Incorrect datetime value: \'Wednesday23423, June 2, 2014\'\nfor function str_to_date |\n+---------+------+-------------------------------------------------------------\n---------------------+\n\nSELECT STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W%#, %M %e, %Y\');\n+----------------------------------------------------------------+\n| STR_TO_DATE(\'Wednesday23423, June 2, 2014\', \'%W%#, %M %e, %Y\') |\n+----------------------------------------------------------------+\n| 2014-06-02 |\n+----------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/str_to_date/','','https://mariadb.com/kb/en/str_to_date/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (548,31,'SUBDATE','Syntax\n------\n\nSUBDATE(date,INTERVAL expr unit), SUBDATE(expr,days)\n\nDescription\n-----------\n\nWhen invoked with the INTERVAL form of the second argument, SUBDATE() is a\nsynonym for DATE_SUB(). See Date and Time Units for a complete list of\npermitted units.\n\nThe second form allows the use of an integer value for days. In such cases, it\nis interpreted as the number of days to be subtracted from the date or\ndatetime expression expr.\n\nExamples\n--------\n\nSELECT DATE_SUB(\'2008-01-02\', INTERVAL 31 DAY);\n+-----------------------------------------+\n| DATE_SUB(\'2008-01-02\', INTERVAL 31 DAY) |\n+-----------------------------------------+\n| 2007-12-02 |\n+-----------------------------------------+\n\nSELECT SUBDATE(\'2008-01-02\', INTERVAL 31 DAY);\n+----------------------------------------+\n| SUBDATE(\'2008-01-02\', INTERVAL 31 DAY) |\n+----------------------------------------+\n| 2007-12-02 |\n+----------------------------------------+\n\nSELECT SUBDATE(\'2008-01-02 12:00:00\', 31);\n+------------------------------------+\n| SUBDATE(\'2008-01-02 12:00:00\', 31) |\n+------------------------------------+\n| 2007-12-02 12:00:00 |\n+------------------------------------+\n\nCREATE TABLE t1 (d DATETIME);\nINSERT INTO t1 VALUES\n (\"2007-01-30 21:31:07\"),\n (\"1983-10-15 06:42:51\"),\n (\"2011-04-21 12:34:56\"),\n (\"2011-10-30 06:31:41\"),\n (\"2011-01-30 14:03:25\"),\n (\"2004-10-07 11:19:34\");\n\nSELECT d, SUBDATE(d, 10) from t1;\n+---------------------+---------------------+\n| d | SUBDATE(d, 10) |\n+---------------------+---------------------+\n| 2007-01-30 21:31:07 | 2007-01-20 21:31:07 |\n| 1983-10-15 06:42:51 | 1983-10-05 06:42:51 |\n| 2011-04-21 12:34:56 | 2011-04-11 12:34:56 |\n| 2011-10-30 06:31:41 | 2011-10-20 06:31:41 |\n| 2011-01-30 14:03:25 | 2011-01-20 14:03:25 |\n| 2004-10-07 11:19:34 | 2004-09-27 11:19:34 |\n+---------------------+---------------------+\n\nSELECT d, SUBDATE(d, INTERVAL 10 MINUTE) from t1;\n+---------------------+--------------------------------+\n| d | SUBDATE(d, INTERVAL 10 MINUTE) |\n+---------------------+--------------------------------+\n| 2007-01-30 21:31:07 | 2007-01-30 21:21:07 |\n| 1983-10-15 06:42:51 | 1983-10-15 06:32:51 |\n| 2011-04-21 12:34:56 | 2011-04-21 12:24:56 |\n| 2011-10-30 06:31:41 | 2011-10-30 06:21:41 |\n| 2011-01-30 14:03:25 | 2011-01-30 13:53:25 |\n| 2004-10-07 11:19:34 | 2004-10-07 11:09:34 |\n+---------------------+--------------------------------+\n\nURL: https://mariadb.com/kb/en/subdate/','','https://mariadb.com/kb/en/subdate/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (549,31,'SUBTIME','Syntax\n------\n\nSUBTIME(expr1,expr2)\n\nDescription\n-----------\n\nSUBTIME() returns expr1 - expr2 expressed as a value in the same format as\nexpr1. expr1 is a time or datetime expression, and expr2 is a time expression.\n\nExamples\n--------\n\nSELECT SUBTIME(\'2007-12-31 23:59:59.999999\',\'1 1:1:1.000002\');\n+--------------------------------------------------------+\n| SUBTIME(\'2007-12-31 23:59:59.999999\',\'1 1:1:1.000002\') |\n+--------------------------------------------------------+\n| 2007-12-30 22:58:58.999997 |\n+--------------------------------------------------------+\n\nSELECT SUBTIME(\'01:00:00.999999\', \'02:00:00.999998\');\n+-----------------------------------------------+\n| SUBTIME(\'01:00:00.999999\', \'02:00:00.999998\') |\n+-----------------------------------------------+\n| -00:59:59.999999 |\n+-----------------------------------------------+\n\nURL: https://mariadb.com/kb/en/subtime/','','https://mariadb.com/kb/en/subtime/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (550,31,'SYSDATE','Syntax\n------\n\nSYSDATE([precision])\n\nDescription\n-----------\n\nReturns the current date and time as a value in \'YYYY-MM-DD HH:MM:SS\' or\nYYYYMMDDHHMMSS.uuuuuu format, depending on whether the function is used in a\nstring or numeric context.\n\nThe optional precision determines the microsecond precision. See Microseconds\nin MariaDB.\n\nSYSDATE() returns the time at which it executes. This differs from the\nbehavior for NOW(), which returns a constant time that indicates the time at\nwhich the statement began to execute. (Within a stored routine or trigger,\nNOW() returns the time at which the routine or triggering statement began to\nexecute.)\n\nIn addition, changing the timestamp system variable with a SET timestamp\nstatement affects the value returned by NOW() but not by SYSDATE(). This means\nthat timestamp settings in the binary log have no effect on invocations of\nSYSDATE().\n\nBecause SYSDATE() can return different values even within the same statement,\nand is not affected by SET TIMESTAMP, it is non-deterministic and therefore\nunsafe for replication if statement-based binary logging is used. If that is a\nproblem, you can use row-based logging, or start the server with the mysqld\noption --sysdate-is-now to cause SYSDATE() to be an alias for NOW(). The\nnon-deterministic nature of SYSDATE() also means that indexes cannot be used\nfor evaluating expressions that refer to it, and that statements using the\nSYSDATE() function are unsafe for statement-based replication.\n\nExamples\n--------\n\nDifference between NOW() and SYSDATE():\n\nSELECT NOW(), SLEEP(2), NOW();\n+---------------------+----------+---------------------+\n| NOW() | SLEEP(2) | NOW() |\n+---------------------+----------+---------------------+\n| 2010-03-27 13:23:40 | 0 | 2010-03-27 13:23:40 |\n+---------------------+----------+---------------------+\n\nSELECT SYSDATE(), SLEEP(2), SYSDATE();\n+---------------------+----------+---------------------+\n| SYSDATE() | SLEEP(2) | SYSDATE() |\n+---------------------+----------+---------------------+\n| 2010-03-27 13:23:52 | 0 | 2010-03-27 13:23:54 |\n+---------------------+----------+---------------------+\n\nWith precision:\n\nSELECT SYSDATE(4);\n+--------------------------+\n| SYSDATE(4) |\n+--------------------------+\n| 2018-07-10 10:17:13.1689 |\n+--------------------------+\n\nURL: https://mariadb.com/kb/en/sysdate/','','https://mariadb.com/kb/en/sysdate/'); @@ -796,8 +796,8 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (692,36,'Type Conversion','Implicit type conversion takes place when MariaDB is using operands or\ndifferent types, in order to make the operands compatible.\n\nIt is best practice not to rely upon implicit conversion; rather use CAST to\nexplicitly convert types.\n\nRules for Conversion on Comparison\n----------------------------------\n\n* If either argument is NULL, the result of the comparison is NULL unless the\nNULL-safe <=> equality comparison operator is used.\n* If both arguments are integers, they are compared as integers.\n* If both arguments are strings, they are compared as strings.\n* If one argument is decimal and the other argument is decimal or integer,\nthey are compared as decimals.\n* If one argument is decimal and the other argument is a floating point, they\nare compared as floating point values.\n* If one argument is string and the other argument is integer, they are\ncompared as decimals. This conversion was added in MariaDB 10.3.36. Prior to\n10.3.36, this combination was compared as floating point values, which did not\nalways work well for huge 64-bit integers because of a possible precision loss\non conversion to double.\n* If a hexadecimal argument is not compared to a number, it is treated as a\nbinary string.\n* If a constant is compared to a TIMESTAMP or DATETIME, the constant is\nconverted to a timestamp, unless used as an argument to the IN function.\n* In other cases, arguments are compared as floating point, or real, numbers.\n\nNote that if a string column is being compared with a numeric value, MariaDB\nwill not use the index on the column, as there are numerous alternatives that\nmay evaluate as equal (see examples below).\n\nComparison Examples\n-------------------\n\nConverting a string to a number:\n\nSELECT 15+\'15\';\n+---------+\n| 15+\'15\' |\n+---------+\n| 30 |\n+---------+\n\nConverting a number to a string:\n\nSELECT CONCAT(15,\'15\');\n+-----------------+\n| CONCAT(15,\'15\') |\n+-----------------+\n| 1515 |\n+-----------------+\n\nFloating point number errors:\n\nSELECT \'9746718491924563214\' = 9746718491924563213;\n+---------------------------------------------+\n| \'9746718491924563214\' = 9746718491924563213 |\n+---------------------------------------------+\n| 1 |\n+---------------------------------------------+\n\nNumeric equivalence with strings:\n\nSELECT \'5\' = 5;\n+---------+\n| \'5\' = 5 |\n+---------+\n| 1 |\n+---------+\n\nSELECT \' 5\' = 5;\n+------------+\n| \' 5\' = 5 |\n+------------+\n| 1 |\n+------------+\n\nSELECT \' 5 \' = 5;\n+--------------+\n| \' 5 \' = 5 |\n+--------------+\n| 1 |\n+--------------+\n1 row in set, 1 warning (0.000 sec)\n\nSHOW WARNINGS;\n+-------+------+--------------------------------------------+\n| Level | Code | Message |\n+-------+------+--------------------------------------------+\n| Note | 1292 | Truncated incorrect DOUBLE value: \' 5 \' |\n+-------+------+--------------------------------------------+\n\nAs a result of the above, MariaDB cannot use the index when comparing a string\nwith a numeric value in the example below:\n\nCREATE TABLE t (a VARCHAR(10), b VARCHAR(10), INDEX idx_a (a));\n\nINSERT INTO t VALUES \n (\'1\', \'1\'), (\'2\', \'2\'), (\'3\', \'3\'),\n (\'4\', \'4\'), (\'5\', \'5\'), (\'1\', \'5\');\n\nEXPLAIN SELECT * FROM t WHERE a = \'3\' \\G\n*************************** 1. row ***************************\n id: 1\n select_type: SIMPLE\n table: t\n type: ref\npossible_keys: idx_a\n key: idx_a\n key_len: 13\n ref: const\n rows: 1\n Extra: Using index condition\n\nEXPLAIN SELECT * FROM t WHERE a = 3 \\G\n*************************** 1. row ***************************\n id: 1\n select_type: SIMPLE\n table: t\n type: ALL\npossible_keys: idx_a\n key: NULL\n key_len: NULL\n ref: NULL\n rows: 6\n Extra: Using where\n\nRules for Conversion on Dyadic Arithmetic Operations\n----------------------------------------------------\n\nImplicit type conversion also takes place on dyadic arithmetic operations\n(+,-,*,/). MariaDB chooses the minimum data type that is guaranteed to fit the\nresult and converts both arguments to the result data type.\n\nFor addition (+), subtraction (-) and multiplication (*), the result data type\nis chosen as follows:\n\n* If either of the arguments is an approximate number (float, double), the\nresult is double.\n* If either of the arguments is a string (char, varchar, text), the result is\ndouble.\n* If either of the arguments is a decimal number, the result is decimal.\n* If either of the arguments is of a temporal type with a non-zero fractional\nsecond precision (time(N), datetime(N), timestamp(N)), the result is decimal.\n* If either of the arguments is of a temporal type with a zero fractional\nsecond precision (time(0), date, datetime(0), timestamp(0)), the result may\nvary between int, int unsigned, bigint or bigint unsigned, depending on the\nexact data type combination.\n* If both arguments are integer numbers (tinyint, smallint, mediumint,\nbigint), the result may vary between int, int unsigned, bigint or bigint\nunsigned, depending of the exact data types and their signs.\n\nFor division (/), the result data type is chosen as follows:\n\n* If either of the arguments is an approximate number (float, double), the\nresult is double.\n* If either of the arguments is a string (char, varchar, text), the result is\ndouble.\n* Otherwise, the result is decimal.\n\nArithmetic Examples\n-------------------\n\nNote, the above rules mean that when an argument of a temporal data type\nappears in addition or subtraction, it\'s treated as a number by default.\n\nSELECT TIME\'10:20:30\' + 1;\n+--------------------+\n| TIME\'10:20:30\' + 1 |\n+--------------------+\n| 102031 |\n+--------------------+\n\nIn order to do temporal addition or subtraction instead, use the DATE_ADD() or\nDATE_SUB() functions, or an INTERVAL expression as the second argument:\n\nSELECT TIME\'10:20:30\' + INTERVAL 1 SECOND;\n+------------------------------------+\n| TIME\'10:20:30\' + INTERVAL 1 SECOND |\n+------------------------------------+\n| 10:20:31 |\n+------------------------------------+\n\nSELECT \"2.2\" + 3;\n+-----------+\n| \"2.2\" + 3 |\n+-----------+\n| 5.2 |\n+-----------+\n\nSELECT 2.2 + 3;\n+---------+\n| 2.2 + 3 |\n+---------+\n| 5.2 |\n+---------+\n\nSELECT 2.2 / 3;\n+---------+\n| 2.2 / 3 |\n+---------+\n| 0.73333 |\n+---------+\n\nSELECT \"2.2\" / 3;\n+--------------------+\n| \"2.2\" / 3 |\n+--------------------+\n| 0.7333333333333334 |\n+--------------------+\n\nURL: https://mariadb.com/kb/en/type-conversion/','','https://mariadb.com/kb/en/type-conversion/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (693,37,'_rowid','Syntax\n------\n\n_rowid\n\nDescription\n-----------\n\nThe _rowid pseudo column is mapped to the primary key in the related table.\nThis can be used as a replacement of the rowid pseudo column in other\ndatabases. Another usage is to simplify sql queries as one doesn\'t have to\nknow the name of the primary key.\n\nExamples\n--------\n\ncreate table t1 (a int primary key, b varchar(80));\ninsert into t1 values (1,\"one\"),(2,\"two\");\nselect * from t1 where _rowid=1;\n\n+---+------+\n| a | b |\n+---+------+\n| 1 | one |\n+---+------+\n\nupdate t1 set b=\"three\" where _rowid=2;\nselect * from t1 where _rowid>=1 and _rowid<=10;\n\n+---+-------+\n| a | b |\n+---+-------+\n| 1 | one |\n| 2 | three |\n+---+-------+\n\nURL: https://mariadb.com/kb/en/_rowid/','','https://mariadb.com/kb/en/_rowid/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (694,38,'ALTER TABLE','Syntax\n------\n\nALTER [ONLINE] [IGNORE] TABLE [IF EXISTS] tbl_name\n [WAIT n | NOWAIT]\n alter_specification [, alter_specification] ...\nalter_specification:\n table_option ...\n | ADD [COLUMN] [IF NOT EXISTS] col_name column_definition\n [FIRST | AFTER col_name ]\n | ADD [COLUMN] [IF NOT EXISTS] (col_name column_definition,...)\n | ADD {INDEX|KEY} [IF NOT EXISTS] [index_name]\n [index_type] (index_col_name,...) [index_option] ...\n | ADD [CONSTRAINT [symbol]] PRIMARY KEY\n [index_type] (index_col_name,...) [index_option] ...\n | ADD [CONSTRAINT [symbol]]\n UNIQUE [INDEX|KEY] [index_name]\n [index_type] (index_col_name,...) [index_option] ...\n | ADD FULLTEXT [INDEX|KEY] [index_name]\n (index_col_name,...) [index_option] ...\n | ADD SPATIAL [INDEX|KEY] [index_name]\n (index_col_name,...) [index_option] ...\n | ADD [CONSTRAINT [symbol]]\n FOREIGN KEY [IF NOT EXISTS] [index_name] (index_col_name,...)\n reference_definition\n | ADD PERIOD FOR SYSTEM_TIME (start_column_name, end_column_name)\n | ALTER [COLUMN] col_name SET DEFAULT literal | (expression)\n | ALTER [COLUMN] col_name DROP DEFAULT\n | ALTER {INDEX|KEY} index_name [NOT] INVISIBLE\n | CHANGE [COLUMN] [IF EXISTS] old_col_name new_col_name column_definition\n [FIRST|AFTER col_name]\n | MODIFY [COLUMN] [IF EXISTS] col_name column_definition\n [FIRST | AFTER col_name]\n | DROP [COLUMN] [IF EXISTS] col_name [RESTRICT|CASCADE]\n | DROP PRIMARY KEY\n | DROP {INDEX|KEY} [IF EXISTS] index_name\n | DROP FOREIGN KEY [IF EXISTS] fk_symbol\n | DROP CONSTRAINT [IF EXISTS] constraint_name\n | DISABLE KEYS\n | ENABLE KEYS\n | RENAME [TO] new_tbl_name\n | ORDER BY col_name [, col_name] ...\n | RENAME COLUMN old_col_name TO new_col_name\n | RENAME {INDEX|KEY} old_index_name TO new_index_name\n | CONVERT TO CHARACTER SET charset_name [COLLATE collation_name]\n | [DEFAULT] CHARACTER SET [=] charset_name\n | [DEFAULT] COLLATE [=] collation_name\n | DISCARD TABLESPACE\n | IMPORT TABLESPACE\n | ALGORITHM [=] {DEFAULT|INPLACE|COPY|NOCOPY|INSTANT}\n | LOCK [=] {DEFAULT|NONE|SHARED|EXCLUSIVE}\n | FORCE\n | partition_options\n | ADD PARTITION [IF NOT EXISTS] (partition_definition)\n | DROP PARTITION [IF EXISTS] partition_names\n | COALESCE PARTITION number\n | REORGANIZE PARTITION [partition_names INTO (partition_definitions)]\n | ANALYZE PARTITION partition_names\n | CHECK PARTITION partition_names\n | OPTIMIZE PARTITION partition_names\n | REBUILD PARTITION partition_names\n | REPAIR PARTITION partition_names\n | EXCHANGE PARTITION partition_name WITH TABLE tbl_name\n | REMOVE PARTITIONING\n | ADD SYSTEM VERSIONING\n | DROP SYSTEM VERSIONING\nindex_col_name:\n col_name [(length)] [ASC | DESC]\nindex_type:\n USING {BTREE | HASH | RTREE}\nindex_option:\n [ KEY_BLOCK_SIZE [=] value\n | index_type\n | WITH PARSER parser_name\n | COMMENT \'string\'\n | CLUSTERING={YES| NO} ]\n [ IGNORED | NOT IGNORED ]\ntable_options:\n table_option [[,] table_option] ...\n\nDescription\n-----------\n\nALTER TABLE enables you to change the structure of an existing table. For\nexample, you can add or delete columns, create or destroy indexes, change the\ntype of existing columns, or rename columns or the table itself. You can also\nchange the comment for the table and the storage engine of the table.\n\nIf another connection is using the table, a metadata lock is active, and this\nstatement will wait until the lock is released. This is also true for\nnon-transactional tables.\n\nWhen adding a UNIQUE index on a column (or a set of columns) which have\nduplicated values, an error will be produced and the statement will be\nstopped. To suppress the error and force the creation of UNIQUE indexes,\ndiscarding duplicates, the IGNORE option can be specified. This can be useful\nif a column (or a set of columns) should be UNIQUE but it contains duplicate\nvalues; however, this technique provides no control on which rows are\npreserved and which are deleted. Also, note that IGNORE is accepted but\nignored in ALTER TABLE ... EXCHANGE PARTITION statements.\n\nThis statement can also be used to rename a table. For details see RENAME\nTABLE.\n\nWhen an index is created, the storage engine may use a configurable buffer in\nthe process. Incrementing the buffer speeds up the index creation. Aria and\nMyISAM allocate a buffer whose size is defined by aria_sort_buffer_size or\nmyisam_sort_buffer_size, also used for REPAIR TABLE. InnoDB allocates three\nbuffers whose size is defined by innodb_sort_buffer_size.\n\nPrivileges\n----------\n\nExecuting the ALTER TABLE statement generally requires at least the ALTER\nprivilege for the table or the database..\n\nIf you are renaming a table, then it also requires the DROP, CREATE and INSERT\nprivileges for the table or the database as well.\n\nOnline DDL\n----------\n\nOnline DDL is supported with the ALGORITHM and LOCK clauses.\n\nSee InnoDB Online DDL Overview for more information on online DDL with InnoDB.\n\nALTER ONLINE TABLE\n------------------\n\nALTER ONLINE TABLE also works for partitioned tables.\n\nOnline ALTER TABLE is available by executing the following:\n\nALTER ONLINE TABLE ...;\n\nThis statement has the following semantics:\n\nThis statement is equivalent to the following:\n\nALTER TABLE ... LOCK=NONE;\n\nSee the LOCK alter specification for more information.\n\nThis statement is equivalent to the following:\n\nALTER TABLE ... ALGORITHM=INPLACE;\n\nSee the ALGORITHM alter specification for more information.\n\nWAIT/NOWAIT\n-----------\n\nMariaDB starting with 10.3.0\n----------------------------\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nIF EXISTS\n---------\n\nThe IF EXISTS and IF NOT EXISTS clauses are available for the following:\n\nADD COLUMN [IF NOT EXISTS]\nADD INDEX [IF NOT EXISTS]\nADD FOREIGN KEY [IF NOT EXISTS]\nADD PARTITION [IF NOT EXISTS]\nCREATE INDEX [IF NOT EXISTS]\nDROP COLUMN [IF EXISTS]\nDROP INDEX [IF EXISTS]\nDROP FOREIGN KEY [IF EXISTS]\nDROP PARTITION [IF EXISTS]\nCHANGE COLUMN [IF EXISTS]\nMODIFY COLUMN [IF EXISTS]\nDROP INDEX [IF EXISTS]\nWhen IF EXISTS and IF NOT EXISTS are used in clauses, queries will not report\nerrors when the condition is triggered for that clause. A warning with the\nsame message text will be issued and the ALTER will move on to the next clause\nin the statement (or end if finished).\n\nMariaDB starting with 10.5.2\n----------------------------\nIf this is directive is used after ALTER ... TABLE, one will not get an error\nif the table doesn\'t exist.\n\nColumn Definitions\n------------------\n\nSee CREATE TABLE: Column Definitions for information about column definitions.\n\nIndex Definitions\n-----------------\n\nSee CREATE TABLE: Index Definitions for information about index definitions.\n\nThe CREATE INDEX and DROP INDEX statements can also be used to add or remove\nan index.\n\nCharacter Sets and Collations\n-----------------------------\n\nCONVERT TO CHARACTER SET charset_name [COLLATE collation_name]\n[DEFAULT] CHARACTER SET [=] charset_name\n[DEFAULT] COLLATE [=] collation_name\nSee Setting Character Sets and Collations for details on setting the character\nsets and collations.\n\nAlter Specifications\n--------------------\n\nTable Options\n-------------\n\nSee CREATE TABLE: Table Options for information about table options.\n\nADD COLUMN\n----------\n\n... ADD COLUMN [IF NOT EXISTS] (col_name column_definition,...)\nAdds a column to the table. The syntax is the same as in CREATE TABLE. If you\nare using IF NOT_EXISTS the column will not be added if it was not there\nalready. This is very useful when doing scripts to modify tables.\n\nThe FIRST and AFTER clauses affect the physical order of columns in the\ndatafile. Use FIRST to add a column in the first (leftmost) position, or AFTER\nfollowed by a column name to add the new column in any other position. Note\nthat, nowadays, the physical position of a column is usually irrelevant.\n\nSee also Instant ADD COLUMN for InnoDB.\n\nDROP COLUMN\n-----------\n\n... DROP COLUMN [IF EXISTS] col_name [CASCADE|RESTRICT]\nDrops the column from the table. If you are using IF EXISTS you will not get\nan error if the column didn\'t exist. If the column is part of any index, the\ncolumn will be dropped from them, except if you add a new column with\nidentical name at the same time. The index will be dropped if all columns from\nthe index were dropped. If the column was used in a view or trigger, you will\nget an error next time the view or trigger is accessed.\n\nMariaDB starting with 10.2.8\n----------------------------\nDropping a column that is part of a multi-column UNIQUE constraint is not\npermitted. For example:\n\nCREATE TABLE a (\n a int,\n b int,\n primary key (a,b)\n);\n\nALTER TABLE x DROP COLUMN a;\n[42000][1072] Key column \'A\' doesn\'t exist in table\n\nThe reason is that dropping column a would result in the new constraint that\nall values in column b be unique. In order to drop the column, an explicit\nDROP PRIMARY KEY and ADD PRIMARY KEY would be required. Up until MariaDB\n10.2.7, the column was dropped and the additional constraint applied,\nresulting in the following structure:\n\nALTER TABLE x DROP COLUMN a;\nQuery OK, 0 rows affected (0.46 sec)\n\nDESC x;\n+-------+---------+------+-----+---------+-------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+---------+------+-----+---------+-------+\n| b | int(11) | NO | PRI | NULL | |\n+-------+---------+------+-----+---------+-------+\n\nMariaDB starting with 10.4.0\n----------------------------\nMariaDB 10.4.0 supports instant DROP COLUMN. DROP COLUMN of an indexed column\nwould imply DROP INDEX (and in the case of a non-UNIQUE multi-column index,\npossibly ADD INDEX). These will not be allowed with ALGORITHM=INSTANT, but\nunlike before, they can be allowed with ALGORITHM=NOCOPY\n\nRESTRICT and CASCADE are allowed to make porting from other database systems\neasier. In MariaDB, they do nothing.\n\nMODIFY COLUMN\n-------------\n\nAllows you to modify the type of a column. The column will be at the same\nplace as the original column and all indexes on the column will be kept. Note\nthat when modifying column, you should specify all attributes for the new\ncolumn.\n\nCREATE TABLE t1 (a INT UNSIGNED AUTO_INCREMENT, PRIMARY KEY((a));\nALTER TABLE t1 MODIFY a BIGINT UNSIGNED AUTO_INCREMENT;\n\nCHANGE COLUMN\n-------------\n\nWorks like MODIFY COLUMN except that you can also change the name of the\ncolumn. The column will be at the same place as the original column and all\nindex on the column will be kept.\n\nCREATE TABLE t1 (a INT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(a));\nALTER TABLE t1 CHANGE a b BIGINT UNSIGNED AUTO_INCREMENT;\n\nALTER COLUMN\n------------\n\nThis lets you change column options.\n\nCREATE TABLE t1 (a INT UNSIGNED AUTO_INCREMENT, b varchar(50), PRIMARY KEY(a));\nALTER TABLE t1 ALTER b SET DEFAULT \'hello\';\n\nRENAME INDEX/KEY\n----------------\n\nMariaDB starting with 10.5.2\n----------------------------\nFrom MariaDB 10.5.2, it is possible to rename an index using the RENAME INDEX\n(or RENAME KEY) syntax, for example:\n\nALTER TABLE t1 RENAME INDEX i_old TO i_new;\n\nRENAME COLUMN\n-------------\n\nMariaDB starting with 10.5.2\n----------------------------\nFrom MariaDB 10.5.2, it is possible to rename a column using the RENAME COLUMN\nsyntax, for example:\n\nALTER TABLE t1 RENAME COLUMN c_old TO c_new;\n\nADD PRIMARY KEY\n---------------\n\nAdd a primary key.\n\nFor PRIMARY KEY indexes, you can specify a name for the index, but it is\nsilently ignored, and the name of the index is always PRIMARY.\n\nSee Getting Started with Indexes: Primary Key for more information.\n\nDROP PRIMARY KEY\n----------------\n\nDrop a primary key.\n\nFor PRIMARY KEY indexes, you can specify a name for the index, but it is\nsilently ignored, and the name of the index is always PRIMARY.\n\nSee Getting Started with Indexes: Primary Key for more information.\n\nADD FOREIGN KEY\n---------------\n\nAdd a foreign key.\n\nFor FOREIGN KEY indexes, a reference definition must be provided.\n\nFor FOREIGN KEY indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nFirst, you have to specify the name of the target (parent) table and a column\nor a column list which must be indexed and whose values must match to the\nforeign key\'s values. The MATCH clause is accepted to improve the\ncompatibility with other DBMS\'s, but has no meaning in MariaDB. The ON DELETE\nand ON UPDATE clauses specify what must be done when a DELETE (or a REPLACE)\nstatements attempts to delete a referenced row from the parent table, and when\nan UPDATE statement attempts to modify the referenced foreign key columns in a\nparent table row, respectively. The following options are allowed:\n\n* RESTRICT: The delete/update operation is not performed. The statement\nterminates with a 1451 error (SQLSTATE \'2300\').\n* NO ACTION: Synonym for RESTRICT.\n* CASCADE: The delete/update operation is performed in both tables.\n* SET NULL: The update or delete goes ahead in the parent table, and the\ncorresponding foreign key fields in the child table are set to NULL. (They\nmust not be defined as NOT NULL for this to succeed).\n* SET DEFAULT: This option is implemented only for the legacy PBXT storage\nengine, which is disabled by default and no longer maintained. It sets the\nchild table\'s foreign key fields to their DEFAULT values when the referenced\nparent table key entries are updated or deleted.\n\nIf either clause is omitted, the default behavior for the omitted clause is\nRESTRICT.\n\nSee Foreign Keys for more information.\n\nDROP FOREIGN KEY\n----------------\n\nDrop a foreign key.\n\nSee Foreign Keys for more information.\n\nADD INDEX\n---------\n\nAdd a plain index.\n\nPlain indexes are regular indexes that are not unique, and are not acting as a\nprimary key or a foreign key. They are also not the \"specialized\" FULLTEXT or\nSPATIAL indexes.\n\nSee Getting Started with Indexes: Plain Indexes for more information.\n\nDROP INDEX\n----------\n\nDrop a plain index.\n\nPlain indexes are regular indexes that are not unique, and are not acting as a\nprimary key or a foreign key. They are also not the \"specialized\" FULLTEXT or\nSPATIAL indexes.\n\nSee Getting Started with Indexes: Plain Indexes for more information.\n\nADD UNIQUE INDEX\n----------------\n\nAdd a unique index.\n\nThe UNIQUE keyword means that the index will not accept duplicated values,','','https://mariadb.com/kb/en/alter-table/'); -update help_topic set description = CONCAT(description, '\nexcept for NULLs. An error will raise if you try to insert duplicate values in\na UNIQUE index.\n\nFor UNIQUE indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nSee Getting Started with Indexes: Unique Index for more information.\n\nDROP UNIQUE INDEX\n-----------------\n\nDrop a unique index.\n\nThe UNIQUE keyword means that the index will not accept duplicated values,\nexcept for NULLs. An error will raise if you try to insert duplicate values in\na UNIQUE index.\n\nFor UNIQUE indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nSee Getting Started with Indexes: Unique Index for more information.\n\nADD FULLTEXT INDEX\n------------------\n\nAdd a FULLTEXT index.\n\nSee Full-Text Indexes for more information.\n\nDROP FULLTEXT INDEX\n-------------------\n\nDrop a FULLTEXT index.\n\nSee Full-Text Indexes for more information.\n\nADD SPATIAL INDEX\n-----------------\n\nAdd a SPATIAL index.\n\nSee SPATIAL INDEX for more information.\n\nDROP SPATIAL INDEX\n------------------\n\nDrop a SPATIAL index.\n\nSee SPATIAL INDEX for more information.\n\nENABLE/ DISABLE KEYS\n--------------------\n\nDISABLE KEYS will disable all non unique keys for the table for storage\nengines that support this (at least MyISAM and Aria). This can be used to\nspeed up inserts into empty tables.\n\nENABLE KEYS will enable all disabled keys.\n\nRENAME TO\n---------\n\nRenames the table. See also RENAME TABLE.\n\nADD CONSTRAINT\n--------------\n\nModifies the table adding a constraint on a particular column or columns.\n\nMariaDB starting with 10.2.1\n----------------------------\nMariaDB 10.2.1 introduced new ways to define a constraint.\n\nNote: Before MariaDB 10.2.1, constraint expressions were accepted in syntax,\nbut ignored.\n\nALTER TABLE table_name \nADD CONSTRAINT [constraint_name] CHECK(expression);\nBefore a row is inserted or updated, all constraints are evaluated in the\norder they are defined. If any constraint fails, then the row will not be\nupdated. One can use most deterministic functions in a constraint, including\nUDF\'s.\n\nCREATE TABLE account_ledger (\n id INT PRIMARY KEY AUTO_INCREMENT,\n transaction_name VARCHAR(100),\n credit_account VARCHAR(100),\n credit_amount INT,\n debit_account VARCHAR(100),\n debit_amount INT);\n\nALTER TABLE account_ledger \nADD CONSTRAINT is_balanced \n CHECK((debit_amount + credit_amount) = 0);\n\nThe constraint_name is optional. If you don\'t provide one in the ALTER TABLE\nstatement, MariaDB auto-generates a name for you. This is done so that you can\nremove it later using DROP CONSTRAINT clause.\n\nYou can disable all constraint expression checks by setting the variable\ncheck_constraint_checks to OFF. You may find this useful when loading a table\nthat violates some constraints that you want to later find and fix in SQL.\n\nTo view constraints on a table, query information_schema.TABLE_CONSTRAINTS:\n\nSELECT CONSTRAINT_NAME, TABLE_NAME, CONSTRAINT_TYPE \nFROM information_schema.TABLE_CONSTRAINTS\nWHERE TABLE_NAME = \'account_ledger\';\n\n+-----------------+----------------+-----------------+\n| CONSTRAINT_NAME | TABLE_NAME | CONSTRAINT_TYPE |\n+-----------------+----------------+-----------------+\n| is_balanced | account_ledger | CHECK |\n+-----------------+----------------+-----------------+\n\nDROP CONSTRAINT\n---------------\n\nMariaDB starting with 10.2.22\n-----------------------------\nDROP CONSTRAINT for UNIQUE and FOREIGN KEY constraints was introduced in\nMariaDB 10.2.22 and MariaDB 10.3.13.\n\nMariaDB starting with 10.2.1\n----------------------------\nDROP CONSTRAINT for CHECK constraints was introduced in MariaDB 10.2.1\n\nModifies the table, removing the given constraint.\n\nALTER TABLE table_name\nDROP CONSTRAINT constraint_name;\n\nWhen you add a constraint to a table, whether through a CREATE TABLE or ALTER\nTABLE...ADD CONSTRAINT statement, you can either set a constraint_name\nyourself, or allow MariaDB to auto-generate one for you. To view constraints\non a table, query information_schema.TABLE_CONSTRAINTS. For instance,\n\nCREATE TABLE t (\n a INT,\n b INT,\n c INT,\n CONSTRAINT CHECK(a > b),\n CONSTRAINT check_equals CHECK(a = c));\n\nSELECT CONSTRAINT_NAME, TABLE_NAME, CONSTRAINT_TYPE \nFROM information_schema.TABLE_CONSTRAINTS\nWHERE TABLE_NAME = \'t\';\n\n+-----------------+----------------+-----------------+\n| CONSTRAINT_NAME | TABLE_NAME | CONSTRAINT_TYPE |\n+-----------------+----------------+-----------------+\n| check_equals | t | CHECK |\n| CONSTRAINT_1 | t | CHECK |\n+-----------------+----------------+-----------------+\n\nTo remove a constraint from the table, issue an ALTER TABLE...DROP CONSTRAINT\nstatement. For example,\n\nALTER TABLE t DROP CONSTRAINT is_unique;\n\nADD SYSTEM VERSIONING\n---------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nSystem-versioned tables was added in MariaDB 10.3.4.\n\nAdd system versioning.\n\nDROP SYSTEM VERSIONING\n----------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nSystem-versioned tables was added in MariaDB 10.3.4.\n\nDrop system versioning.\n\nADD PERIOD FOR SYSTEM_TIME\n--------------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nSystem-versioned tables was added in MariaDB 10.3.4.\n\nFORCE\n-----\n\nALTER TABLE ... FORCE can force MariaDB to re-build the table.\n\nIn MariaDB 5.5 and before, this could only be done by setting the ENGINE table\noption to its old value. For example, for an InnoDB table, one could execute\nthe following:\n\nALTER TABLE tab_name ENGINE = InnoDB;\n\nThe FORCE option can be used instead. For example, :\n\nALTER TABLE tab_name FORCE;\n\nWith InnoDB, the table rebuild will only reclaim unused space (i.e. the space\npreviously used for deleted rows) if the innodb_file_per_table system variable\nis set to ON. If the system variable is OFF, then the space will not be\nreclaimed, but it will be-re-used for new data that\'s later added.\n\nEXCHANGE PARTITION\n------------------\n\nThis is used to exchange the tablespace files between a partition and another\ntable.\n\nSee copying InnoDB\'s transportable tablespaces for more information.\n\nDISCARD TABLESPACE\n------------------\n\nThis is used to discard an InnoDB table\'s tablespace.\n\nSee copying InnoDB\'s transportable tablespaces for more information.\n\nIMPORT TABLESPACE\n-----------------\n\nThis is used to import an InnoDB table\'s tablespace. The tablespace should\nhave been copied from its original server after executing FLUSH TABLES FOR\nEXPORT.\n\nSee copying InnoDB\'s transportable tablespaces for more information.\n\nALTER TABLE ... IMPORT only applies to InnoDB tables. Most other popular\nstorage engines, such as Aria and MyISAM, will recognize their data files as\nsoon as they\'ve been placed in the proper directory under the datadir, and no\nspecial DDL is required to import them.\n\nALGORITHM\n---------\n\nThe ALTER TABLE statement supports the ALGORITHM clause. This clause is one of\nthe clauses that is used to implement online DDL. ALTER TABLE supports several\ndifferent algorithms. An algorithm can be explicitly chosen for an ALTER TABLE\noperation by setting the ALGORITHM clause. The supported values are:\n\n* ALGORITHM=DEFAULT - This implies the default behavior for the specific\nstatement, such as if no ALGORITHM clause is specified.\n* ALGORITHM=COPY\n* ALGORITHM=INPLACE\n* ALGORITHM=NOCOPY - This was added in MariaDB 10.3.7.\n* ALGORITHM=INSTANT - This was added in MariaDB 10.3.7.\n\nSee InnoDB Online DDL Overview: ALGORITHM for information on how the ALGORITHM\nclause affects InnoDB.\n\nALGORITHM=DEFAULT\n-----------------\n\nThe default behavior, which occurs if ALGORITHM=DEFAULT is specified, or if\nALGORITHM is not specified at all, usually only makes a copy if the operation\ndoesn\'t support being done in-place at all. In this case, the most efficient\navailable algorithm will usually be used.\n\nHowever, in MariaDB 10.3.6 and before, if the value of the old_alter_table\nsystem variable is set to ON, then the default behavior is to perform ALTER\nTABLE operations by making a copy of the table using the old algorithm.\n\nIn MariaDB 10.3.7 and later, the old_alter_table system variable is\ndeprecated. Instead, the alter_algorithm system variable defines the default\nalgorithm for ALTER TABLE operations.\n\nALGORITHM=COPY\n--------------\n\nALGORITHM=COPY is the name for the original ALTER TABLE algorithm from early\nMariaDB versions.\n\nWhen ALGORITHM=COPY is set, MariaDB essentially does the following operations:\n\n-- Create a temporary table with the new definition\nCREATE TEMPORARY TABLE tmp_tab (\n...\n);\n\n-- Copy the data from the original table\nINSERT INTO tmp_tab\n SELECT * FROM original_tab;\n\n-- Drop the original table\nDROP TABLE original_tab;\n\n-- Rename the temporary table, so that it replaces the original one\nRENAME TABLE tmp_tab TO original_tab;\n\nThis algorithm is very inefficient, but it is generic, so it works for all\nstorage engines.\n\nIf ALGORITHM=COPY is specified, then the copy algorithm will be used even if\nit is not necessary. This can result in a lengthy table copy. If multiple\nALTER TABLE operations are required that each require the table to be rebuilt,\nthen it is best to specify all operations in a single ALTER TABLE statement,\nso that the table is only rebuilt once.\n\nALGORITHM=INPLACE\n-----------------\n\nALGORITHM=COPY can be incredibly slow, because the whole table has to be\ncopied and rebuilt. ALGORITHM=INPLACE was introduced as a way to avoid this by\nperforming operations in-place and avoiding the table copy and rebuild, when\npossible.\n\nWhen ALGORITHM=INPLACE is set, the underlying storage engine uses\noptimizations to perform the operation while avoiding the table copy and\nrebuild. However, INPLACE is a bit of a misnomer, since some operations may\nstill require the table to be rebuilt for some storage engines. Regardless,\nseveral operations can be performed without a full copy of the table for some\nstorage engines.\n\nA more accurate name would have been ALGORITHM=ENGINE, where ENGINE refers to\nan \"engine-specific\" algorithm.\n\nIf an ALTER TABLE operation supports ALGORITHM=INPLACE, then it can be\nperformed using optimizations by the underlying storage engine, but it may\nrebuilt.\n\nSee InnoDB Online DDL Operations with ALGORITHM=INPLACE for more.\n\nALGORITHM=NOCOPY\n----------------\n\nALGORITHM=NOCOPY was introduced in MariaDB 10.3.7.\n\nALGORITHM=INPLACE can sometimes be surprisingly slow in instances where it has\nto rebuild the clustered index, because when the clustered index has to be\nrebuilt, the whole table has to be rebuilt. ALGORITHM=NOCOPY was introduced as\na way to avoid this.\n\nIf an ALTER TABLE operation supports ALGORITHM=NOCOPY, then it can be\nperformed without rebuilding the clustered index.\n\nIf ALGORITHM=NOCOPY is specified for an ALTER TABLE operation that does not\nsupport ALGORITHM=NOCOPY, then an error will be raised. In this case, raising\nan error is preferable, if the alternative is for the operation to rebuild the\nclustered index, and perform unexpectedly slowly.\n\nSee InnoDB Online DDL Operations with ALGORITHM=NOCOPY for more.\n\nALGORITHM=INSTANT\n-----------------\n\nALGORITHM=INSTANT was introduced in MariaDB 10.3.7.\n\nALGORITHM=INPLACE can sometimes be surprisingly slow in instances where it has\nto modify data files. ALGORITHM=INSTANT was introduced as a way to avoid this.\n\nIf an ALTER TABLE operation supports ALGORITHM=INSTANT, then it can be\nperformed without modifying any data files.\n\nIf ALGORITHM=INSTANT is specified for an ALTER TABLE operation that does not\nsupport ALGORITHM=INSTANT, then an error will be raised. In this case, raising\nan error is preferable, if the alternative is for the operation to modify data\nfiles, and perform unexpectedly slowly.\n\nSee InnoDB Online DDL Operations with ALGORITHM=INSTANT for more.\n\nLOCK\n----\n\nThe ALTER TABLE statement supports the LOCK clause. This clause is one of the\nclauses that is used to implement online DDL. ALTER TABLE supports several\ndifferent locking strategies. A locking strategy can be explicitly chosen for\nan ALTER TABLE operation by setting the LOCK clause. The supported values are:\n\n* DEFAULT: Acquire the least restrictive lock on the table that is supported\nfor the specific operation. Permit the maximum amount of concurrency that is\nsupported for the specific operation.\n* NONE: Acquire no lock on the table. Permit all concurrent DML. If this\nlocking strategy is not permitted for an operation, then an error is raised.\n* SHARED: Acquire a read lock on the table. Permit read-only concurrent DML.\nIf this locking strategy is not permitted for an operation, then an error is\nraised.\n* EXCLUSIVE: Acquire a write lock on the table. Do not permit concurrent DML.\n\nDifferent storage engines support different locking strategies for different\noperations. If a specific locking strategy is chosen for an ALTER TABLE\noperation, and that table\'s storage engine does not support that locking\nstrategy for that specific operation, then an error will be raised.\n\nIf the LOCK clause is not explicitly set, then the operation uses LOCK=DEFAULT.\n\nALTER ONLINE TABLE is equivalent to LOCK=NONE. Therefore, the ALTER ONLINE\nTABLE statement can be used to ensure that your ALTER TABLE operation allows\nall concurrent DML.\n\nSee InnoDB Online DDL Overview: LOCK for information on how the LOCK clause\naffects InnoDB.\n\nProgress Reporting\n------------------\n\nMariaDB provides progress reporting for ALTER TABLE statement for clients that\nsupport the new progress reporting protocol. For example, if you were using\nthe mysql client, then the progress report might look like this::\n\nALTER TABLE test ENGINE=Aria;\nStage: 1 of 2 \'copy to tmp table\' 46% of stage\n\nThe progress report is also shown in the output of the SHOW PROCESSLIST\nstatement and in the contents of the information_schema.PROCESSLIST table.\n\nSee Progress Reporting for more information.\n\nAborting ALTER TABLE Operations\n-------------------------------\n\nIf an ALTER TABLE operation is being performed and the connection is killed,\nthe changes will be rolled back in a controlled manner. The rollback can be a') WHERE help_topic_id = 694; -update help_topic set description = CONCAT(description, '\nslow operation as the time it takes is relative to how far the operation has\nprogressed.\n\nMariaDB starting with 10.2.13\n-----------------------------\nAborting ALTER TABLE ... ALGORITHM=COPY was made faster by removing excessive\nundo logging (MDEV-11415). This significantly shortens the time it takes to\nabort a running ALTER TABLE operation.\n\nAtomic ALTER TABLE\n------------------\n\nMariaDB starting with 10.6.1\n----------------------------\nFrom MariaDB 10.6, ALTER TABLE is atomic for most engines, including InnoDB,\nMyRocks, MyISAM and Aria (MDEV-25180). This means that if there is a crash\n(server down or power outage) during an ALTER TABLE operation, after recovery,\neither the old table and associated triggers and status will be intact, or the\nnew table will be active.\n\nIn older MariaDB versions one could get leftover #sql-alter..\',\n\'#sql-backup..\' or \'table_name.frm˝\' files if the system crashed during the\nALTER TABLE operation.\n\nSee Atomic DDL for more information.\n\nReplication\n-----------\n\nMariaDB starting with 10.8.0\n----------------------------\nBefore MariaDB 10.8.0, ALTER TABLE got fully executed on the primary first,\nand only then was it replicated and started executing on replicas. From\nMariaDB 10.8.0, ALTER TABLE gets replicated and starts executing on replicas\nwhen it starts executing on the primary, not when it finishes. This way the\nreplication lag caused by a heavy ALTER TABLE can be completely eliminated\n(MDEV-11675).\n\nExamples\n--------\n\nAdding a new column:\n\nALTER TABLE t1 ADD x INT;\n\nDropping a column:\n\nALTER TABLE t1 DROP x;\n\nModifying the type of a column:\n\nALTER TABLE t1 MODIFY x bigint unsigned;\n\nChanging the name and type of a column:\n\nALTER TABLE t1 CHANGE a b bigint unsigned auto_increment;\n\nCombining multiple clauses in a single ALTER TABLE statement, separated by\ncommas:\n\nALTER TABLE t1 DROP x, ADD x2 INT, CHANGE y y2 INT;\n\nChanging the storage engine and adding a comment:\n\nALTER TABLE t1 \n ENGINE = InnoDB\n COMMENT = \'First of three tables containing usage info\';\n\nRebuilding the table (the previous example will also rebuild the table if it\nwas already InnoDB):\n\nALTER TABLE t1 FORCE;\n\nDropping an index:\n\nALTER TABLE rooms DROP INDEX u;\n\nAdding a unique index:\n\nALTER TABLE rooms ADD UNIQUE INDEX u(room_number);\n\nFrom MariaDB 10.5.3, adding a primary key for an application-time period table\nwith a WITHOUT OVERLAPS constraint:\n\nALTER TABLE rooms ADD PRIMARY KEY(room_number, p WITHOUT OVERLAPS);\n\nURL: https://mariadb.com/kb/en/alter-table/') WHERE help_topic_id = 694; +update help_topic set description = CONCAT(description, '\nexcept for NULLs. An error will raise if you try to insert duplicate values in\na UNIQUE index.\n\nFor UNIQUE indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nSee Getting Started with Indexes: Unique Index for more information.\n\nDROP UNIQUE INDEX\n-----------------\n\nDrop a unique index.\n\nThe UNIQUE keyword means that the index will not accept duplicated values,\nexcept for NULLs. An error will raise if you try to insert duplicate values in\na UNIQUE index.\n\nFor UNIQUE indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nSee Getting Started with Indexes: Unique Index for more information.\n\nADD FULLTEXT INDEX\n------------------\n\nAdd a FULLTEXT index.\n\nSee Full-Text Indexes for more information.\n\nDROP FULLTEXT INDEX\n-------------------\n\nDrop a FULLTEXT index.\n\nSee Full-Text Indexes for more information.\n\nADD SPATIAL INDEX\n-----------------\n\nAdd a SPATIAL index.\n\nSee SPATIAL INDEX for more information.\n\nDROP SPATIAL INDEX\n------------------\n\nDrop a SPATIAL index.\n\nSee SPATIAL INDEX for more information.\n\nENABLE/ DISABLE KEYS\n--------------------\n\nDISABLE KEYS will disable all non unique keys for the table for storage\nengines that support this (at least MyISAM and Aria). This can be used to\nspeed up inserts into empty tables.\n\nENABLE KEYS will enable all disabled keys.\n\nRENAME TO\n---------\n\nRenames the table. See also RENAME TABLE.\n\nADD CONSTRAINT\n--------------\n\nModifies the table adding a constraint on a particular column or columns.\n\nMariaDB starting with 10.2.1\n----------------------------\nMariaDB 10.2.1 introduced new ways to define a constraint.\n\nNote: Before MariaDB 10.2.1, constraint expressions were accepted in syntax,\nbut ignored.\n\nALTER TABLE table_name \nADD CONSTRAINT [constraint_name] CHECK(expression);\nBefore a row is inserted or updated, all constraints are evaluated in the\norder they are defined. If any constraint fails, then the row will not be\nupdated. One can use most deterministic functions in a constraint, including\nUDF\'s.\n\nCREATE TABLE account_ledger (\n id INT PRIMARY KEY AUTO_INCREMENT,\n transaction_name VARCHAR(100),\n credit_account VARCHAR(100),\n credit_amount INT,\n debit_account VARCHAR(100),\n debit_amount INT);\n\nALTER TABLE account_ledger \nADD CONSTRAINT is_balanced \n CHECK((debit_amount + credit_amount) = 0);\n\nThe constraint_name is optional. If you don\'t provide one in the ALTER TABLE\nstatement, MariaDB auto-generates a name for you. This is done so that you can\nremove it later using DROP CONSTRAINT clause.\n\nYou can disable all constraint expression checks by setting the variable\ncheck_constraint_checks to OFF. You may find this useful when loading a table\nthat violates some constraints that you want to later find and fix in SQL.\n\nTo view constraints on a table, query information_schema.TABLE_CONSTRAINTS:\n\nSELECT CONSTRAINT_NAME, TABLE_NAME, CONSTRAINT_TYPE \nFROM information_schema.TABLE_CONSTRAINTS\nWHERE TABLE_NAME = \'account_ledger\';\n\n+-----------------+----------------+-----------------+\n| CONSTRAINT_NAME | TABLE_NAME | CONSTRAINT_TYPE |\n+-----------------+----------------+-----------------+\n| is_balanced | account_ledger | CHECK |\n+-----------------+----------------+-----------------+\n\nDROP CONSTRAINT\n---------------\n\nMariaDB starting with 10.2.22\n-----------------------------\nDROP CONSTRAINT for UNIQUE and FOREIGN KEY constraints was introduced in\nMariaDB 10.2.22 and MariaDB 10.3.13.\n\nMariaDB starting with 10.2.1\n----------------------------\nDROP CONSTRAINT for CHECK constraints was introduced in MariaDB 10.2.1\n\nModifies the table, removing the given constraint.\n\nALTER TABLE table_name\nDROP CONSTRAINT constraint_name;\n\nWhen you add a constraint to a table, whether through a CREATE TABLE or ALTER\nTABLE...ADD CONSTRAINT statement, you can either set a constraint_name\nyourself, or allow MariaDB to auto-generate one for you. To view constraints\non a table, query information_schema.TABLE_CONSTRAINTS. For instance,\n\nCREATE TABLE t (\n a INT,\n b INT,\n c INT,\n CONSTRAINT CHECK(a > b),\n CONSTRAINT check_equals CHECK(a = c));\n\nSELECT CONSTRAINT_NAME, TABLE_NAME, CONSTRAINT_TYPE \nFROM information_schema.TABLE_CONSTRAINTS\nWHERE TABLE_NAME = \'t\';\n\n+-----------------+----------------+-----------------+\n| CONSTRAINT_NAME | TABLE_NAME | CONSTRAINT_TYPE |\n+-----------------+----------------+-----------------+\n| check_equals | t | CHECK |\n| CONSTRAINT_1 | t | CHECK |\n+-----------------+----------------+-----------------+\n\nTo remove a constraint from the table, issue an ALTER TABLE...DROP CONSTRAINT\nstatement. For example,\n\nALTER TABLE t DROP CONSTRAINT is_unique;\n\nADD SYSTEM VERSIONING\n---------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nSystem-versioned tables was added in MariaDB 10.3.4.\n\nAdd system versioning.\n\nDROP SYSTEM VERSIONING\n----------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nSystem-versioned tables was added in MariaDB 10.3.4.\n\nDrop system versioning.\n\nADD PERIOD FOR SYSTEM_TIME\n--------------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nSystem-versioned tables was added in MariaDB 10.3.4.\n\nFORCE\n-----\n\nALTER TABLE ... FORCE can force MariaDB to re-build the table.\n\nIn MariaDB 5.5 and before, this could only be done by setting the ENGINE table\noption to its old value. For example, for an InnoDB table, one could execute\nthe following:\n\nALTER TABLE tab_name ENGINE = InnoDB;\n\nThe FORCE option can be used instead. For example, :\n\nALTER TABLE tab_name FORCE;\n\nWith InnoDB, the table rebuild will only reclaim unused space (i.e. the space\npreviously used for deleted rows) if the innodb_file_per_table system variable\nis set to ON (the default). If the system variable is OFF, then the space will\nnot be reclaimed, but it will be-re-used for new data that\'s later added.\n\nEXCHANGE PARTITION\n------------------\n\nThis is used to exchange the contents of a partition with another table.\n\nThis is performed by swapping the tablespaces of the partition with the other\ntable.\n\nSee copying InnoDB\'s transportable tablespaces for more information.\n\nDISCARD TABLESPACE\n------------------\n\nThis is used to discard an InnoDB table\'s tablespace.\n\nSee copying InnoDB\'s transportable tablespaces for more information.\n\nIMPORT TABLESPACE\n-----------------\n\nThis is used to import an InnoDB table\'s tablespace. The tablespace should\nhave been copied from its original server after executing FLUSH TABLES FOR\nEXPORT.\n\nSee copying InnoDB\'s transportable tablespaces for more information.\n\nALTER TABLE ... IMPORT only applies to InnoDB tables. Most other popular\nstorage engines, such as Aria and MyISAM, will recognize their data files as\nsoon as they\'ve been placed in the proper directory under the datadir, and no\nspecial DDL is required to import them.\n\nALGORITHM\n---------\n\nThe ALTER TABLE statement supports the ALGORITHM clause. This clause is one of\nthe clauses that is used to implement online DDL. ALTER TABLE supports several\ndifferent algorithms. An algorithm can be explicitly chosen for an ALTER TABLE\noperation by setting the ALGORITHM clause. The supported values are:\n\n* ALGORITHM=DEFAULT - This implies the default behavior for the specific\nstatement, such as if no ALGORITHM clause is specified.\n* ALGORITHM=COPY\n* ALGORITHM=INPLACE\n* ALGORITHM=NOCOPY - This was added in MariaDB 10.3.7.\n* ALGORITHM=INSTANT - This was added in MariaDB 10.3.7.\n\nSee InnoDB Online DDL Overview: ALGORITHM for information on how the ALGORITHM\nclause affects InnoDB.\n\nALGORITHM=DEFAULT\n-----------------\n\nThe default behavior, which occurs if ALGORITHM=DEFAULT is specified, or if\nALGORITHM is not specified at all, usually only makes a copy if the operation\ndoesn\'t support being done in-place at all. In this case, the most efficient\navailable algorithm will usually be used.\n\nHowever, in MariaDB 10.3.6 and before, if the value of the old_alter_table\nsystem variable is set to ON, then the default behavior is to perform ALTER\nTABLE operations by making a copy of the table using the old algorithm.\n\nIn MariaDB 10.3.7 and later, the old_alter_table system variable is\ndeprecated. Instead, the alter_algorithm system variable defines the default\nalgorithm for ALTER TABLE operations.\n\nALGORITHM=COPY\n--------------\n\nALGORITHM=COPY is the name for the original ALTER TABLE algorithm from early\nMariaDB versions.\n\nWhen ALGORITHM=COPY is set, MariaDB essentially does the following operations:\n\n-- Create a temporary table with the new definition\nCREATE TEMPORARY TABLE tmp_tab (\n...\n);\n\n-- Copy the data from the original table\nINSERT INTO tmp_tab\n SELECT * FROM original_tab;\n\n-- Drop the original table\nDROP TABLE original_tab;\n\n-- Rename the temporary table, so that it replaces the original one\nRENAME TABLE tmp_tab TO original_tab;\n\nThis algorithm is very inefficient, but it is generic, so it works for all\nstorage engines.\n\nIf ALGORITHM=COPY is specified, then the copy algorithm will be used even if\nit is not necessary. This can result in a lengthy table copy. If multiple\nALTER TABLE operations are required that each require the table to be rebuilt,\nthen it is best to specify all operations in a single ALTER TABLE statement,\nso that the table is only rebuilt once.\n\nALGORITHM=INPLACE\n-----------------\n\nALGORITHM=COPY can be incredibly slow, because the whole table has to be\ncopied and rebuilt. ALGORITHM=INPLACE was introduced as a way to avoid this by\nperforming operations in-place and avoiding the table copy and rebuild, when\npossible.\n\nWhen ALGORITHM=INPLACE is set, the underlying storage engine uses\noptimizations to perform the operation while avoiding the table copy and\nrebuild. However, INPLACE is a bit of a misnomer, since some operations may\nstill require the table to be rebuilt for some storage engines. Regardless,\nseveral operations can be performed without a full copy of the table for some\nstorage engines.\n\nA more accurate name would have been ALGORITHM=ENGINE, where ENGINE refers to\nan \"engine-specific\" algorithm.\n\nIf an ALTER TABLE operation supports ALGORITHM=INPLACE, then it can be\nperformed using optimizations by the underlying storage engine, but it may\nrebuilt.\n\nSee InnoDB Online DDL Operations with ALGORITHM=INPLACE for more.\n\nALGORITHM=NOCOPY\n----------------\n\nALGORITHM=NOCOPY was introduced in MariaDB 10.3.7.\n\nALGORITHM=INPLACE can sometimes be surprisingly slow in instances where it has\nto rebuild the clustered index, because when the clustered index has to be\nrebuilt, the whole table has to be rebuilt. ALGORITHM=NOCOPY was introduced as\na way to avoid this.\n\nIf an ALTER TABLE operation supports ALGORITHM=NOCOPY, then it can be\nperformed without rebuilding the clustered index.\n\nIf ALGORITHM=NOCOPY is specified for an ALTER TABLE operation that does not\nsupport ALGORITHM=NOCOPY, then an error will be raised. In this case, raising\nan error is preferable, if the alternative is for the operation to rebuild the\nclustered index, and perform unexpectedly slowly.\n\nSee InnoDB Online DDL Operations with ALGORITHM=NOCOPY for more.\n\nALGORITHM=INSTANT\n-----------------\n\nALGORITHM=INSTANT was introduced in MariaDB 10.3.7.\n\nALGORITHM=INPLACE can sometimes be surprisingly slow in instances where it has\nto modify data files. ALGORITHM=INSTANT was introduced as a way to avoid this.\n\nIf an ALTER TABLE operation supports ALGORITHM=INSTANT, then it can be\nperformed without modifying any data files.\n\nIf ALGORITHM=INSTANT is specified for an ALTER TABLE operation that does not\nsupport ALGORITHM=INSTANT, then an error will be raised. In this case, raising\nan error is preferable, if the alternative is for the operation to modify data\nfiles, and perform unexpectedly slowly.\n\nSee InnoDB Online DDL Operations with ALGORITHM=INSTANT for more.\n\nLOCK\n----\n\nThe ALTER TABLE statement supports the LOCK clause. This clause is one of the\nclauses that is used to implement online DDL. ALTER TABLE supports several\ndifferent locking strategies. A locking strategy can be explicitly chosen for\nan ALTER TABLE operation by setting the LOCK clause. The supported values are:\n\n* DEFAULT: Acquire the least restrictive lock on the table that is supported\nfor the specific operation. Permit the maximum amount of concurrency that is\nsupported for the specific operation.\n* NONE: Acquire no lock on the table. Permit all concurrent DML. If this\nlocking strategy is not permitted for an operation, then an error is raised.\n* SHARED: Acquire a read lock on the table. Permit read-only concurrent DML.\nIf this locking strategy is not permitted for an operation, then an error is\nraised.\n* EXCLUSIVE: Acquire a write lock on the table. Do not permit concurrent DML.\n\nDifferent storage engines support different locking strategies for different\noperations. If a specific locking strategy is chosen for an ALTER TABLE\noperation, and that table\'s storage engine does not support that locking\nstrategy for that specific operation, then an error will be raised.\n\nIf the LOCK clause is not explicitly set, then the operation uses LOCK=DEFAULT.\n\nALTER ONLINE TABLE is equivalent to LOCK=NONE. Therefore, the ALTER ONLINE\nTABLE statement can be used to ensure that your ALTER TABLE operation allows\nall concurrent DML.\n\nSee InnoDB Online DDL Overview: LOCK for information on how the LOCK clause\naffects InnoDB.\n\nProgress Reporting\n------------------\n\nMariaDB provides progress reporting for ALTER TABLE statement for clients that\nsupport the new progress reporting protocol. For example, if you were using\nthe mysql client, then the progress report might look like this::\n\nALTER TABLE test ENGINE=Aria;\nStage: 1 of 2 \'copy to tmp table\' 46% of stage\n\nThe progress report is also shown in the output of the SHOW PROCESSLIST\nstatement and in the contents of the information_schema.PROCESSLIST table.\n\nSee Progress Reporting for more information.\n\nAborting ALTER TABLE Operations\n-------------------------------\n\nIf an ALTER TABLE operation is being performed and the connection is killed,') WHERE help_topic_id = 694; +update help_topic set description = CONCAT(description, '\nthe changes will be rolled back in a controlled manner. The rollback can be a\nslow operation as the time it takes is relative to how far the operation has\nprogressed.\n\nMariaDB starting with 10.2.13\n-----------------------------\nAborting ALTER TABLE ... ALGORITHM=COPY was made faster by removing excessive\nundo logging (MDEV-11415). This significantly shortens the time it takes to\nabort a running ALTER TABLE operation.\n\nAtomic ALTER TABLE\n------------------\n\nMariaDB starting with 10.6.1\n----------------------------\nFrom MariaDB 10.6, ALTER TABLE is atomic for most engines, including InnoDB,\nMyRocks, MyISAM and Aria (MDEV-25180). This means that if there is a crash\n(server down or power outage) during an ALTER TABLE operation, after recovery,\neither the old table and associated triggers and status will be intact, or the\nnew table will be active.\n\nIn older MariaDB versions one could get leftover #sql-alter..\',\n\'#sql-backup..\' or \'table_name.frm˝\' files if the system crashed during the\nALTER TABLE operation.\n\nSee Atomic DDL for more information.\n\nReplication\n-----------\n\nMariaDB starting with 10.8.1\n----------------------------\nBefore MariaDB 10.8.1, ALTER TABLE got fully executed on the primary first,\nand only then was it replicated and started executing on replicas. From\nMariaDB 10.8.1, ALTER TABLE gains an option to replicate sooner and begin\nexecuting on replicas when it merely starts executing on the primary, not when\nit finishes. This way the replication lag caused by a heavy ALTER TABLE can be\ncompletely eliminated (MDEV-11675).\n\nExamples\n--------\n\nAdding a new column:\n\nALTER TABLE t1 ADD x INT;\n\nDropping a column:\n\nALTER TABLE t1 DROP x;\n\nModifying the type of a column:\n\nALTER TABLE t1 MODIFY x bigint unsigned;\n\nChanging the name and type of a column:\n\nALTER TABLE t1 CHANGE a b bigint unsigned auto_increment;\n\nCombining multiple clauses in a single ALTER TABLE statement, separated by\ncommas:\n\nALTER TABLE t1 DROP x, ADD x2 INT, CHANGE y y2 INT;\n\nChanging the storage engine and adding a comment:\n\nALTER TABLE t1 \n ENGINE = InnoDB\n COMMENT = \'First of three tables containing usage info\';\n\nRebuilding the table (the previous example will also rebuild the table if it\nwas already InnoDB):\n\nALTER TABLE t1 FORCE;\n\nDropping an index:\n\nALTER TABLE rooms DROP INDEX u;\n\nAdding a unique index:\n\nALTER TABLE rooms ADD UNIQUE INDEX u(room_number);\n\nFrom MariaDB 10.5.3, adding a primary key for an application-time period table\nwith a WITHOUT OVERLAPS constraint:\n\nALTER TABLE rooms ADD PRIMARY KEY(room_number, p WITHOUT OVERLAPS);\n\nFrom MariaDB 10.8.1, ALTER query can be replicated faster with the setting of\n\nSET @@SESSION.binlog_alter_two_phase = true;\n\nprior the ALTER query. Binlog would contain two event groups\n\n| master-bin.000001 | 495 | Gtid | 1 | 537 | GTID\n0-1-2 START ALTER |\n| master-bin.000001 | 537 | Query | 1 | 655 | use\n`test`; alter table t add column b int, algorithm=inplace |\n| master-bin.000001 | 655 | Gtid | 1 | 700 | GTID\n0-1-3 COMMIT ALTER id=2 |\n| master-bin.000001 | 700 | Query | 1 | 835 | use\n`test`; alter table t add column b int, algorithm=inplace |\n\nof which the first one gets delivered to replicas before ALTER is taken to\nactual execution on the primary.\n\nURL: https://mariadb.com/kb/en/alter-table/') WHERE help_topic_id = 694; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (695,38,'ALTER DATABASE','Modifies a database, changing its overall characteristics.\n\nSyntax\n------\n\nALTER {DATABASE | SCHEMA} [db_name]\n alter_specification ...\nALTER {DATABASE | SCHEMA} db_name\n UPGRADE DATA DIRECTORY NAME\n\nalter_specification:\n [DEFAULT] CHARACTER SET [=] charset_name\n | [DEFAULT] COLLATE [=] collation_name\n | COMMENT [=] \'comment\'\n\nDescription\n-----------\n\nALTER DATABASE enables you to change the overall characteristics of a\ndatabase. These characteristics are stored in the db.opt file in the database\ndirectory. To use ALTER DATABASE, you need the ALTER privilege on the\ndatabase. ALTER SCHEMA is a synonym for ALTER DATABASE.\n\nThe CHARACTER SET clause changes the default database character set. The\nCOLLATE clause changes the default database collation. See Character Sets and\nCollations for more.\n\nYou can see what character sets and collations are available using,\nrespectively, the SHOW CHARACTER SET and SHOW COLLATION statements.\n\nChanging the default character set/collation of a database does not change the\ncharacter set/collation of any stored procedures or stored functions that were\npreviously created, and relied on the defaults. These need to be dropped and\nrecreated in order to apply the character set/collation changes.\n\nThe database name can be omitted from the first syntax, in which case the\nstatement applies to the default database.\n\nThe syntax that includes the UPGRADE DATA DIRECTORY NAME clause was added in\nMySQL 5.1.23. It updates the name of the directory associated with the\ndatabase to use the encoding implemented in MySQL 5.1 for mapping database\nnames to database directory names (see Identifier to File Name Mapping). This\nclause is for use under these conditions:\n\n* It is intended when upgrading MySQL to 5.1 or later from older versions.\n* It is intended to update a database directory name to the current encoding\nformat if the name contains special characters that need encoding.\n* The statement is used by mysqlcheck (as invoked by mysql_upgrade).\n\nFor example,if a database in MySQL 5.0 has a name of a-b-c, the name contains\ninstance of the `-\' character. In 5.0, the database directory is also named\na-b-c, which is not necessarily safe for all file systems. In MySQL 5.1 and\nup, the same database name is encoded as a@002db@002dc to produce a file\nsystem-neutral directory name.\n\nWhen a MySQL installation is upgraded to MySQL 5.1 or later from an older\nversion,the server displays a name such as a-b-c (which is in the old format)\nas #mysql50#a-b-c, and you must refer to the name using the #mysql50# prefix.\nUse UPGRADE DATA DIRECTORY NAME in this case to explicitly tell the server to\nre-encode the database directory name to the current encoding format:\n\nALTER DATABASE `#mysql50#a-b-c` UPGRADE DATA DIRECTORY NAME;\n\nAfter executing this statement, you can refer to the database as a-b-c without\nthe special #mysql50# prefix.\n\nCOMMENT\n-------\n\nMariaDB starting with 10.5.0\n----------------------------\nFrom MariaDB 10.5.0, it is possible to add a comment of a maximum of 1024\nbytes. If the comment length exceeds this length, a error/warning code 4144 is\nthrown. The database comment is also added to the db.opt file, as well as to\nthe information_schema.schemata table.\n\nExamples\n--------\n\nALTER DATABASE test CHARACTER SET=\'utf8\' COLLATE=\'utf8_bin\';\n\nFrom MariaDB 10.5.0:\n\nALTER DATABASE p COMMENT=\'Presentations\';\n\nURL: https://mariadb.com/kb/en/alter-database/','','https://mariadb.com/kb/en/alter-database/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (696,38,'ALTER EVENT','Modifies one or more characteristics of an existing event.\n\nSyntax\n------\n\nALTER\n [DEFINER = { user | CURRENT_USER }]\n EVENT event_name\n [ON SCHEDULE schedule]\n [ON COMPLETION [NOT] PRESERVE]\n [RENAME TO new_event_name]\n [ENABLE | DISABLE | DISABLE ON SLAVE]\n [COMMENT \'comment\']\n [DO sql_statement]\n\nDescription\n-----------\n\nThe ALTER EVENT statement is used to change one or more of the characteristics\nof an existing event without the need to drop and recreate it. The syntax for\neach of the DEFINER, ON SCHEDULE, ON COMPLETION, COMMENT, ENABLE / DISABLE,\nand DO clauses is exactly the same as when used with CREATE EVENT.\n\nThis statement requires the EVENT privilege. When a user executes a successful\nALTER EVENT statement, that user becomes the definer for the affected event.\n\n(In MySQL 5.1.11 and earlier, an event could be altered only by its definer,\nor by a user having the SUPER privilege.)\n\nALTER EVENT works only with an existing event:\n\nALTER EVENT no_such_event ON SCHEDULE EVERY \'2:3\' DAY_HOUR;\nERROR 1539 (HY000): Unknown event \'no_such_event\'\n\nExamples\n--------\n\nALTER EVENT myevent \n ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 2 HOUR\n DO\n UPDATE myschema.mytable SET mycol = mycol + 1;\n\nURL: https://mariadb.com/kb/en/alter-event/','','https://mariadb.com/kb/en/alter-event/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (697,38,'ALTER FUNCTION','Syntax\n------\n\nALTER FUNCTION func_name [characteristic ...]\n\ncharacteristic:\n { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }\n | SQL SECURITY { DEFINER | INVOKER }\n | COMMENT \'string\'\n\nDescription\n-----------\n\nThis statement can be used to change the characteristics of a stored function.\nMore than one change may be specified in an ALTER FUNCTION statement. However,\nyou cannot change the parameters or body of a stored function using this\nstatement; to make such changes, you must drop and re-create the function\nusing DROP FUNCTION and CREATE FUNCTION.\n\nYou must have the ALTER ROUTINE privilege for the function. (That privilege is\ngranted automatically to the function creator.) If binary logging is enabled,\nthe ALTER FUNCTION statement might also require the SUPER privilege, as\ndescribed in Binary Logging of Stored Routines.\n\nExample\n-------\n\nALTER FUNCTION hello SQL SECURITY INVOKER;\n\nURL: https://mariadb.com/kb/en/alter-function/','','https://mariadb.com/kb/en/alter-function/'); @@ -806,16 +806,16 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (700,38,'ALTER SERVER','Syntax\n------\n\nALTER SERVER server_name\n OPTIONS (option [, option] ...)\n\nDescription\n-----------\n\nAlters the server information for server_name, adjusting the specified options\nas per the CREATE SERVER command. The corresponding fields in the\nmysql.servers table are updated accordingly. This statement requires the SUPER\nprivilege or, from MariaDB 10.5.2, the FEDERATED ADMIN privilege.\n\nALTER SERVER is not written to the binary log, irrespective of the binary log\nformat being used. From MariaDB 10.1.13, Galera replicates the CREATE SERVER,\nALTER SERVER and DROP SERVER statements.\n\nExamples\n--------\n\nALTER SERVER s OPTIONS (USER \'sally\');\n\nURL: https://mariadb.com/kb/en/alter-server/','','https://mariadb.com/kb/en/alter-server/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (701,38,'ALTER TABLESPACE','The ALTER TABLESPACE statement is not supported by MariaDB. It was originally\ninherited from MySQL NDB Cluster. In MySQL 5.7 and later, the statement is\nalso supported for InnoDB. However, MariaDB has chosen not to include that\nspecific feature. See MDEV-19294 for more information.\n\nURL: https://mariadb.com/kb/en/alter-tablespace/','','https://mariadb.com/kb/en/alter-tablespace/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (702,38,'ALTER VIEW','Syntax\n------\n\nALTER\n [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]\n [DEFINER = { user | CURRENT_USER }]\n [SQL SECURITY { DEFINER | INVOKER }]\n VIEW view_name [(column_list)]\n AS select_statement\n [WITH [CASCADED | LOCAL] CHECK OPTION]\n\nDescription\n-----------\n\nThis statement changes the definition of a view, which must exist. The syntax\nis similar to that for CREATE VIEW and the effect is the same as for CREATE OR\nREPLACE VIEW if the view exists. This statement requires the CREATE VIEW and\nDROP privileges for the view, and some privilege for each column referred to\nin the SELECT statement. ALTER VIEW is allowed only to the definer or users\nwith the SUPER privilege.\n\nExample\n-------\n\nALTER VIEW v AS SELECT a, a*3 AS a2 FROM t;\n\nURL: https://mariadb.com/kb/en/alter-view/','','https://mariadb.com/kb/en/alter-view/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (703,38,'CREATE TABLE','Syntax\n------\n\nCREATE [OR REPLACE] [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name\n (create_definition,...) [table_options ]... [partition_options]\nCREATE [OR REPLACE] [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name\n [(create_definition,...)] [table_options ]... [partition_options]\n select_statement\nCREATE [OR REPLACE] [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name\n { LIKE old_table_name | (LIKE old_table_name) }\nselect_statement:\n [IGNORE | REPLACE] [AS] SELECT ... (Some legal select statement)\n\nDescription\n-----------\n\nUse the CREATE TABLE statement to create a table with the given name.\n\nIn its most basic form, the CREATE TABLE statement provides a table name\nfollowed by a list of columns, indexes, and constraints. By default, the table\nis created in the default database. Specify a database with db_name.tbl_name.\nIf you quote the table name, you must quote the database name and table name\nseparately as `db_name`.`tbl_name`. This is particularly useful for CREATE\nTABLE ... SELECT, because it allows to create a table into a database, which\ncontains data from other databases. See Identifier Qualifiers.\n\nIf a table with the same name exists, error 1050 results. Use IF NOT EXISTS to\nsuppress this error and issue a note instead. Use SHOW WARNINGS to see notes.\n\nThe CREATE TABLE statement automatically commits the current transaction,\nexcept when using the TEMPORARY keyword.\n\nFor valid identifiers to use as table names, see Identifier Names.\n\nNote: if the default_storage_engine is set to ColumnStore then it needs\nsetting on all UMs. Otherwise when the tables using the default engine are\nreplicated across UMs they will use the wrong engine. You should therefore not\nuse this option as a session variable with ColumnStore.\n\nMicrosecond precision can be between 0-6. If no precision is specified it is\nassumed to be 0, for backward compatibility reasons.\n\nPrivileges\n----------\n\nExecuting the CREATE TABLE statement requires the CREATE privilege for the\ntable or the database.\n\nCREATE OR REPLACE\n-----------------\n\nIf the OR REPLACE clause is used and the table already exists, then instead of\nreturning an error, the server will drop the existing table and replace it\nwith the newly defined table.\n\nThis syntax was originally added to make replication more robust if it has to\nrollback and repeat statements such as CREATE ... SELECT on replicas.\n\nCREATE OR REPLACE TABLE table_name (a int);\n\nis basically the same as:\n\nDROP TABLE IF EXISTS table_name;\nCREATE TABLE table_name (a int);\n\nwith the following exceptions:\n\n* If table_name was locked with LOCK TABLES it will continue to be locked\nafter the statement.\n* Temporary tables are only dropped if the TEMPORARY keyword was used. (With\nDROP TABLE, temporary tables are preferred to be dropped before normal\ntables).\n\nThings to be Aware of With CREATE OR REPLACE\n--------------------------------------------\n\n* The table is dropped first (if it existed), after that the CREATE is done.\nBecause of this, if the CREATE fails, then the table will not exist anymore\nafter the statement. If the table was used with LOCK TABLES it will be\nunlocked.\n* One can\'t use OR REPLACE together with IF EXISTS.\n* Slaves in replication will by default use CREATE OR REPLACE when replicating\nCREATE statements that don\'\'t use IF EXISTS. This can be changed by setting\nthe variable slave-ddl-exec-mode to STRICT.\n\nCREATE TABLE IF NOT EXISTS\n--------------------------\n\nIf the IF NOT EXISTS clause is used, then the table will only be created if a\ntable with the same name does not already exist. If the table already exists,\nthen a warning will be triggered by default.\n\nCREATE TEMPORARY TABLE\n----------------------\n\nUse the TEMPORARY keyword to create a temporary table that is only available\nto the current session. Temporary tables are dropped when the session ends.\nTemporary table names are specific to the session. They will not conflict with\nother temporary tables from other sessions even if they share the same name.\nThey will shadow names of non-temporary tables or views, if they are\nidentical. A temporary table can have the same name as a non-temporary table\nwhich is located in the same database. In that case, their name will reference\nthe temporary table when used in SQL statements. You must have the CREATE\nTEMPORARY TABLES privilege on the database to create temporary tables. If no\nstorage engine is specified, the default_tmp_storage_engine setting will\ndetermine the engine.\n\nROCKSDB temporary tables cannot be created by setting the\ndefault_tmp_storage_engine system variable, or using CREATE TEMPORARY TABLE\nLIKE. Before MariaDB 10.7, they could be specified, but would silently fail,\nand a MyISAM table would be created instead. From MariaDB 10.7 an error is\nreturned. Explicitly creating a temporary table with ENGINE=ROCKSDB has never\nbeen permitted.\n\nCREATE TABLE ... LIKE\n---------------------\n\nUse the LIKE clause instead of a full table definition to create a table with\nthe same definition as another table, including columns, indexes, and table\noptions. Foreign key definitions, as well as any DATA DIRECTORY or INDEX\nDIRECTORY table options specified on the original table, will not be created.\n\nCREATE TABLE ... SELECT\n-----------------------\n\nYou can create a table containing data from other tables using the CREATE ...\nSELECT statement. Columns will be created in the table for each field returned\nby the SELECT query.\n\nYou can also define some columns normally and add other columns from a SELECT.\nYou can also create columns in the normal way and assign them some values\nusing the query, this is done to force a certain type or other field\ncharacteristics. The columns that are not named in the query will be placed\nbefore the others. For example:\n\nCREATE TABLE test (a INT NOT NULL, b CHAR(10)) ENGINE=MyISAM\n SELECT 5 AS b, c, d FROM another_table;\n\nRemember that the query just returns data. If you want to use the same\nindexes, or the same columns attributes ([NOT] NULL, DEFAULT, AUTO_INCREMENT)\nin the new table, you need to specify them manually. Types and sizes are not\nautomatically preserved if no data returned by the SELECT requires the full\nsize, and VARCHAR could be converted into CHAR. The CAST() function can be\nused to forcee the new table to use certain types.\n\nAliases (AS) are taken into account, and they should always be used when you\nSELECT an expression (function, arithmetical operation, etc).\n\nIf an error occurs during the query, the table will not be created at all.\n\nIf the new table has a primary key or UNIQUE indexes, you can use the IGNORE\nor REPLACE keywords to handle duplicate key errors during the query. IGNORE\nmeans that the newer values must not be inserted an identical value exists in\nthe index. REPLACE means that older values must be overwritten.\n\nIf the columns in the new table are more than the rows returned by the query,\nthe columns populated by the query will be placed after other columns. Note\nthat if the strict SQL_MODE is on, and the columns that are not names in the\nquery do not have a DEFAULT value, an error will raise and no rows will be\ncopied.\n\nConcurrent inserts are not used during the execution of a CREATE ... SELECT.\n\nIf the table already exists, an error similar to the following will be\nreturned:\n\nERROR 1050 (42S01): Table \'t\' already exists\n\nIf the IF NOT EXISTS clause is used and the table exists, a note will be\nproduced instead of an error.\n\nTo insert rows from a query into an existing table, INSERT ... SELECT can be\nused.\n\nColumn Definitions\n------------------\n\ncreate_definition:\n { col_name column_definition | index_definition | period_definition | CHECK\n(expr) }\ncolumn_definition:\n data_type\n [NOT NULL | NULL] [DEFAULT default_value | (expression)]\n [ON UPDATE [NOW | CURRENT_TIMESTAMP] [(precision)]]\n [AUTO_INCREMENT] [ZEROFILL] [UNIQUE [KEY] | [PRIMARY] KEY]\n [INVISIBLE] [{WITH|WITHOUT} SYSTEM VERSIONING]\n [COMMENT \'string\'] [REF_SYSTEM_ID = value]\n [reference_definition]\n | data_type [GENERATED ALWAYS]\n AS { { ROW {START|END} } | { (expression) [VIRTUAL | PERSISTENT | STORED] } }\n [UNIQUE [KEY]] [COMMENT \'string\']\nconstraint_definition:\n CONSTRAINT [constraint_name] CHECK (expression)\nNote: Until MariaDB 10.4, MariaDB accepts the shortcut format with a\nREFERENCES clause only in ALTER TABLE and CREATE TABLE statements, but that\nsyntax does nothing. For example:\n\nCREATE TABLE b(for_key INT REFERENCES a(not_key));\n\nMariaDB simply parses it without returning any error or warning, for\ncompatibility with other DBMS\'s. Before MariaDB 10.2.1 this was also true for\nCHECK constraints. However, only the syntax described below creates foreign\nkeys.\n\nFrom MariaDB 10.5, MariaDB will attempt to apply the constraint. See Foreign\nKeys examples.\n\nEach definition either creates a column in the table or specifies and index or\nconstraint on one or more columns. See Indexes below for details on creating\nindexes.\n\nCreate a column by specifying a column name and a data type, optionally\nfollowed by column options. See Data Types for a full list of data types\nallowed in MariaDB.\n\nNULL and NOT NULL\n-----------------\n\nUse the NULL or NOT NULL options to specify that values in the column may or\nmay not be NULL, respectively. By default, values may be NULL. See also NULL\nValues in MariaDB.\n\nDEFAULT Column Option\n---------------------\n\nMariaDB starting with 10.2.1\n----------------------------\nThe DEFAULT clause was enhanced in MariaDB 10.2.1. Some enhancements include\n\n* BLOB and TEXT columns now support DEFAULT.\n* The DEFAULT clause can now be used with an expression or function.\n\nSpecify a default value using the DEFAULT clause. If you don\'t specify DEFAULT\nthen the following rules apply:\n\n* If the column is not defined with NOT NULL, AUTO_INCREMENT or TIMESTAMP, an\nexplicit DEFAULT NULL will be added.\nNote that in MySQL and in MariaDB before 10.1.6, you may get an explicit\nDEFAULT for primary key parts, if not specified with NOT NULL.\n\nThe default value will be used if you INSERT a row without specifying a value\nfor that column, or if you specify DEFAULT for that column. Before MariaDB\n10.2.1 you couldn\'t usually provide an expression or function to evaluate at\ninsertion time. You had to provide a constant default value instead. The one\nexception is that you may use CURRENT_TIMESTAMP as the default value for a\nTIMESTAMP column to use the current timestamp at insertion time.\n\nCURRENT_TIMESTAMP may also be used as the default value for a DATETIME\n\nFrom MariaDB 10.2.1 you can use most functions in DEFAULT. Expressions should\nhave parentheses around them. If you use a non deterministic function in\nDEFAULT then all inserts to the table will be replicated in row mode. You can\neven refer to earlier columns in the DEFAULT expression (excluding\nAUTO_INCREMENT columns):\n\nCREATE TABLE t1 (a int DEFAULT (1+1), b int DEFAULT (a+1));\nCREATE TABLE t2 (a bigint primary key DEFAULT UUID_SHORT());\n\nThe DEFAULT clause cannot contain any stored functions or subqueries, and a\ncolumn used in the clause must already have been defined earlier in the\nstatement.\n\nSince MariaDB 10.2.1, it is possible to assign BLOB or TEXT columns a DEFAULT\nvalue. In earlier versions, assigning a default to these columns was not\npossible.\n\nMariaDB starting with 10.3.3\n----------------------------\nStarting from 10.3.3 you can also use DEFAULT (NEXT VALUE FOR sequence)\n\nAUTO_INCREMENT Column Option\n----------------------------\n\nUse AUTO_INCREMENT to create a column whose value can can be set automatically\nfrom a simple counter. You can only use AUTO_INCREMENT on a column with an\ninteger type. The column must be a key, and there can only be one\nAUTO_INCREMENT column in a table. If you insert a row without specifying a\nvalue for that column (or if you specify 0, NULL, or DEFAULT as the value),\nthe actual value will be taken from the counter, with each insertion\nincrementing the counter by one. You can still insert a value explicitly. If\nyou insert a value that is greater than the current counter value, the counter\nis set based on the new value. An AUTO_INCREMENT column is implicitly NOT\nNULL. Use LAST_INSERT_ID to get the AUTO_INCREMENT value most recently used by\nan INSERT statement.\n\nZEROFILL Column Option\n----------------------\n\nIf the ZEROFILL column option is specified for a column using a numeric data\ntype, then the column will be set to UNSIGNED and the spaces used by default\nto pad the field are replaced with zeros. ZEROFILL is ignored in expressions\nor as part of a UNION. ZEROFILL is a non-standard MySQL and MariaDB\nenhancement.\n\nPRIMARY KEY Column Option\n-------------------------\n\nUse PRIMARY KEY to make a column a primary key. A primary key is a special\ntype of a unique key. There can be at most one primary key per table, and it\nis implicitly NOT NULL.\n\nSpecifying a column as a unique key creates a unique index on that column. See\nthe Index Definitions section below for more information.\n\nUNIQUE KEY Column Option\n------------------------\n\nUse UNIQUE KEY (or just UNIQUE) to specify that all values in the column must\nbe distinct from each other. Unless the column is NOT NULL, there may be\nmultiple rows with NULL in the column.\n\nSpecifying a column as a unique key creates a unique index on that column. See\nthe Index Definitions section below for more information.\n\nCOMMENT Column Option\n---------------------\n\nYou can provide a comment for each column using the COMMENT clause. The\nmaximum length is 1024 characters. Use the SHOW FULL COLUMNS statement to see\ncolumn comments.\n\nREF_SYSTEM_ID\n-------------\n\nREF_SYSTEM_ID can be used to specify Spatial Reference System IDs for spatial\ndata type columns.\n\nGenerated Columns\n-----------------\n\nA generated column is a column in a table that cannot explicitly be set to a\nspecific value in a DML query. Instead, its value is automatically generated\nbased on an expression. This expression might generate the value based on the\nvalues of other columns in the table, or it might generate the value by\ncalling built-in functions or user-defined functions (UDFs).\n\nThere are two types of generated columns:\n\n* PERSISTENT or STORED: This type\'s value is actually stored in the table.\n* VIRTUAL: This type\'s value is not stored at all. Instead, the value is','','https://mariadb.com/kb/en/create-table/'); -update help_topic set description = CONCAT(description, '\ngenerated dynamically when the table is queried. This type is the default.\n\nGenerated columns are also sometimes called computed columns or virtual\ncolumns.\n\nFor a complete description about generated columns and their limitations, see\nGenerated (Virtual and Persistent/Stored) Columns.\n\nCOMPRESSED\n----------\n\nMariaDB starting with 10.3.3\n----------------------------\nCertain columns may be compressed. See Storage-Engine Independent Column\nCompression.\n\nINVISIBLE\n---------\n\nMariaDB starting with 10.3.3\n----------------------------\nColumns may be made invisible, and hidden in certain contexts. See Invisible\nColumns.\n\nWITH SYSTEM VERSIONING Column Option\n------------------------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nColumns may be explicitly marked as included from system versioning. See\nSystem-versioned tables for details.\n\nWITHOUT SYSTEM VERSIONING Column Option\n---------------------------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nColumns may be explicitly marked as excluded from system versioning. See\nSystem-versioned tables for details.\n\nIndex Definitions\n-----------------\n\nindex_definition:\n {INDEX|KEY} [index_name] [index_type] (index_col_name,...) [index_option]\n...\n {{{|}}} {FULLTEXT|SPATIAL} [INDEX|KEY] [index_name] (index_col_name,...)\n[index_option] ...\n {{{|}}} [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (index_col_name,...)\n[index_option] ...\n {{{|}}} [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY] [index_name] [index_type]\n(index_col_name,...) [index_option] ...\n {{{|}}} [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,...)\nreference_definition\n\nindex_col_name:\n col_name [(length)] [ASC | DESC]\n\nindex_type:\n USING {BTREE | HASH | RTREE}\n\nindex_option:\n [ KEY_BLOCK_SIZE [=] value\n {{{|}}} index_type\n {{{|}}} WITH PARSER parser_name\n {{{|}}} COMMENT \'string\'\n {{{|}}} CLUSTERING={YES| NO} ]\n [ IGNORED | NOT IGNORED ]\n\nreference_definition:\n REFERENCES tbl_name (index_col_name,...)\n [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]\n [ON DELETE reference_option]\n [ON UPDATE reference_option]\n\nreference_option:\n RESTRICT | CASCADE | SET NULL | NO ACTION\n\nINDEX and KEY are synonyms.\n\nIndex names are optional, if not specified an automatic name will be assigned.\nIndex name are needed to drop indexes and appear in error messages when a\nconstraint is violated.\n\nIndex Categories\n----------------\n\nPlain Indexes\n-------------\n\nPlain indexes are regular indexes that are not unique, and are not acting as a\nprimary key or a foreign key. They are also not the \"specialized\" FULLTEXT or\nSPATIAL indexes.\n\nSee Getting Started with Indexes: Plain Indexes for more information.\n\nPRIMARY KEY\n-----------\n\nFor PRIMARY KEY indexes, you can specify a name for the index, but it is\nignored, and the name of the index is always PRIMARY. From MariaDB 10.3.18 and\nMariaDB 10.4.8, a warning is explicitly issued if a name is specified. Before\nthen, the name was silently ignored.\n\nSee Getting Started with Indexes: Primary Key for more information.\n\nUNIQUE\n------\n\nThe UNIQUE keyword means that the index will not accept duplicated values,\nexcept for NULLs. An error will raise if you try to insert duplicate values in\na UNIQUE index.\n\nFor UNIQUE indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nSee Getting Started with Indexes: Unique Index for more information.\n\nFOREIGN KEY\n-----------\n\nFor FOREIGN KEY indexes, a reference definition must be provided.\n\nFor FOREIGN KEY indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nFirst, you have to specify the name of the target (parent) table and a column\nor a column list which must be indexed and whose values must match to the\nforeign key\'s values. The MATCH clause is accepted to improve the\ncompatibility with other DBMS\'s, but has no meaning in MariaDB. The ON DELETE\nand ON UPDATE clauses specify what must be done when a DELETE (or a REPLACE)\nstatements attempts to delete a referenced row from the parent table, and when\nan UPDATE statement attempts to modify the referenced foreign key columns in a\nparent table row, respectively. The following options are allowed:\n\n* RESTRICT: The delete/update operation is not performed. The statement\nterminates with a 1451 error (SQLSTATE \'2300\').\n* NO ACTION: Synonym for RESTRICT.\n* CASCADE: The delete/update operation is performed in both tables.\n* SET NULL: The update or delete goes ahead in the parent table, and the\ncorresponding foreign key fields in the child table are set to NULL. (They\nmust not be defined as NOT NULL for this to succeed).\n* SET DEFAULT: This option is currently implemented only for the PBXT storage\nengine, which is disabled by default and no longer maintained. It sets the\nchild table\'s foreign key fields to their DEFAULT values when the referenced\nparent table key entries are updated or deleted.\n\nIf either clause is omitted, the default behavior for the omitted clause is\nRESTRICT.\n\nSee Foreign Keys for more information.\n\nFULLTEXT\n--------\n\nUse the FULLTEXT keyword to create full-text indexes.\n\nSee Full-Text Indexes for more information.\n\nSPATIAL\n-------\n\nUse the SPATIAL keyword to create geometric indexes.\n\nSee SPATIAL INDEX for more information.\n\nIndex Options\n-------------\n\nKEY_BLOCK_SIZE Index Option\n---------------------------\n\nThe KEY_BLOCK_SIZE index option is similar to the KEY_BLOCK_SIZE table option.\n\nWith the InnoDB storage engine, if you specify a non-zero value for the\nKEY_BLOCK_SIZE table option for the whole table, then the table will\nimplicitly be created with the ROW_FORMAT table option set to COMPRESSED.\nHowever, this does not happen if you just set the KEY_BLOCK_SIZE index option\nfor one or more indexes in the table. The InnoDB storage engine ignores the\nKEY_BLOCK_SIZE index option. However, the SHOW CREATE TABLE statement may\nstill report it for the index.\n\nFor information about the KEY_BLOCK_SIZE index option, see the KEY_BLOCK_SIZE\ntable option below.\n\nIndex Types\n-----------\n\nEach storage engine supports some or all index types. See Storage Engine Index\nTypes for details on permitted index types for each storage engine.\n\nDifferent index types are optimized for different kind of operations:\n\n* BTREE is the default type, and normally is the best choice. It is supported\nby all storage engines. It can be used to compare a column\'s value with a\nvalue using the =, >, >=, <, <=, BETWEEN, and LIKE operators. BTREE can also\nbe used to find NULL values. Searches against an index prefix are possible.\n* HASH is only supported by the MEMORY storage engine. HASH indexes can only\nbe used for =, <=, and >= comparisons. It can not be used for the ORDER BY\nclause. Searches against an index prefix are not possible.\n* RTREE is the default for SPATIAL indexes, but if the storage engine does not\nsupport it BTREE can be used.\n\nIndex columns names are listed between parenthesis. After each column, a\nprefix length can be specified. If no length is specified, the whole column\nwill be indexed. ASC and DESC can be specified for compatibility with are\nDBMS\'s, but have no meaning in MariaDB.\n\nWITH PARSER Index Option\n------------------------\n\nThe WITH PARSER index option only applies to FULLTEXT indexes and contains the\nfulltext parser name. The fulltext parser must be an installed plugin.\n\nCOMMENT Index Option\n--------------------\n\nA comment of up to 1024 characters is permitted with the COMMENT index option.\n\nThe COMMENT index option allows you to specify a comment with user-readable\ntext describing what the index is for. This information is not used by the\nserver itself.\n\nCLUSTERING Index Option\n-----------------------\n\nThe CLUSTERING index option is only valid for tables using the TokuDB storage\nengine.\n\nIGNORED / NOT IGNORED\n---------------------\n\nMariaDB starting with 10.6.0\n----------------------------\nFrom MariaDB 10.6.0, indexes can be specified to be ignored by the optimizer.\nSee Ignored Indexes.\n\nPeriods\n-------\n\nMariaDB starting with 10.3.4\n----------------------------\n\nperiod_definition:\n PERIOD FOR SYSTEM_TIME (start_column_name, end_column_name)\nMariaDB supports a subset of the standard syntax for periods. At the moment\nit\'s only used for creating System-versioned tables. Both columns must be\ncreated, must be either of a TIMESTAMP(6) or BIGINT UNSIGNED type, and be\ngenerated as ROW START and ROW END accordingly. See System-versioned tables\nfor details.\n\nThe table must also have the WITH SYSTEM VERSIONING clause.\n\nConstraint Expressions\n----------------------\n\nMariaDB starting with 10.2.1\n----------------------------\nMariaDB 10.2.1 introduced new ways to define a constraint.\n\nNote: Before MariaDB 10.2.1, constraint expressions were accepted in the\nsyntax but ignored.\n\nMariaDB 10.2.1 introduced two ways to define a constraint:\n\n* CHECK(expression) given as part of a column definition.\n* CONSTRAINT [constraint_name] CHECK (expression)\n\nBefore a row is inserted or updated, all constraints are evaluated in the\norder they are defined. If any constraints fails, then the row will not be\nupdated. One can use most deterministic functions in a constraint, including\nUDFs.\n\ncreate table t1 (a int check(a>0) ,b int check (b> 0), constraint abc check\n(a>b));\n\nIf you use the second format and you don\'t give a name to the constraint, then\nthe constraint will get a auto generated name. This is done so that you can\nlater delete the constraint with ALTER TABLE DROP constraint_name.\n\nOne can disable all constraint expression checks by setting the variable\ncheck_constraint_checks to OFF. This is useful for example when loading a\ntable that violates some constraints that you want to later find and fix in\nSQL.\n\nSee CONSTRAINT for more information.\n\nTable Options\n-------------\n\nFor each individual table you create (or alter), you can set some table\noptions. The general syntax for setting options is:\n\n = , [ = ...]\n\nThe equal sign is optional.\n\nSome options are supported by the server and can be used for all tables, no\nmatter what storage engine they use; other options can be specified for all\nstorage engines, but have a meaning only for some engines. Also, engines can\nextend CREATE TABLE with new options.\n\nIf the IGNORE_BAD_TABLE_OPTIONS SQL_MODE is enabled, wrong table options\ngenerate a warning; otherwise, they generate an error.\n\ntable_option: \n [STORAGE] ENGINE [=] engine_name\n | AUTO_INCREMENT [=] value\n | AVG_ROW_LENGTH [=] value\n | [DEFAULT] CHARACTER SET [=] charset_name\n | CHECKSUM [=] {0 | 1}\n | [DEFAULT] COLLATE [=] collation_name\n | COMMENT [=] \'string\'\n | CONNECTION [=] \'connect_string\'\n | DATA DIRECTORY [=] \'absolute path to directory\'\n | DELAY_KEY_WRITE [=] {0 | 1}\n | ENCRYPTED [=] {YES | NO}\n | ENCRYPTION_KEY_ID [=] value\n | IETF_QUOTES [=] {YES | NO}\n | INDEX DIRECTORY [=] \'absolute path to directory\'\n | INSERT_METHOD [=] { NO | FIRST | LAST }\n | KEY_BLOCK_SIZE [=] value\n | MAX_ROWS [=] value\n | MIN_ROWS [=] value\n | PACK_KEYS [=] {0 | 1 | DEFAULT}\n | PAGE_CHECKSUM [=] {0 | 1}\n | PAGE_COMPRESSED [=] {0 | 1}\n | PAGE_COMPRESSION_LEVEL [=] {0 .. 9}\n | PASSWORD [=] \'string\'\n | ROW_FORMAT [=] {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT|PAGE}\n | SEQUENCE [=] {0|1}\n | STATS_AUTO_RECALC [=] {DEFAULT|0|1}\n | STATS_PERSISTENT [=] {DEFAULT|0|1}\n | STATS_SAMPLE_PAGES [=] {DEFAULT|value}\n | TABLESPACE tablespace_name\n | TRANSACTIONAL [=] {0 | 1}\n | UNION [=] (tbl_name[,tbl_name]...)\n | WITH SYSTEM VERSIONING\n\n[STORAGE] ENGINE\n----------------\n\n[STORAGE] ENGINE specifies a storage engine for the table. If this option is\nnot used, the default storage engine is used instead. That is, the\ndefault_storage_engine session option value if it is set, or the value\nspecified for the --default-storage-engine mysqld startup option, or the\ndefault storage engine, InnoDB. If the specified storage engine is not\ninstalled and active, the default value will be used, unless the\nNO_ENGINE_SUBSTITUTION SQL MODE is set (default). This is only true for CREATE\nTABLE, not for ALTER TABLE. For a list of storage engines that are present in\nyour server, issue a SHOW ENGINES.\n\nAUTO_INCREMENT\n--------------\n\nAUTO_INCREMENT specifies the initial value for the AUTO_INCREMENT primary key.\nThis works for MyISAM, Aria, InnoDB, MEMORY, and ARCHIVE tables. You can\nchange this option with ALTER TABLE, but in that case the new value must be\nhigher than the highest value which is present in the AUTO_INCREMENT column.\nIf the storage engine does not support this option, you can insert (and then\ndelete) a row having the wanted value - 1 in the AUTO_INCREMENT column.\n\nAVG_ROW_LENGTH\n--------------\n\nAVG_ROW_LENGTH is the average rows size. It only applies to tables using\nMyISAM and Aria storage engines that have the ROW_FORMAT table option set to\nFIXED format.\n\nMyISAM uses MAX_ROWS and AVG_ROW_LENGTH to decide the maximum size of a table\n(default: 256TB, or the maximum file size allowed by the system).\n\n[DEFAULT] CHARACTER SET/CHARSET\n-------------------------------\n\n[DEFAULT] CHARACTER SET (or [DEFAULT] CHARSET) is used to set a default\ncharacter set for the table. This is the character set used for all columns\nwhere an explicit character set is not specified. If this option is omitted or\nDEFAULT is specified, database\'s default character set will be used. See\nSetting Character Sets and Collations for details on setting the character\nsets.\n\nCHECKSUM/TABLE_CHECKSUM\n-----------------------\n\nCHECKSUM (or TABLE_CHECKSUM) can be set to 1 to maintain a live checksum for\nall table\'s rows. This makes write operations slower, but CHECKSUM TABLE will\nbe very fast. This option is only supported for MyISAM and Aria tables.\n\n[DEFAULT] COLLATE\n-----------------\n\n[DEFAULT] COLLATE is used to set a default collation for the table. This is\nthe collation used for all columns where an explicit character set is not\nspecified. If this option is omitted or DEFAULT is specified, database\'s\ndefault option will be used. See Setting Character Sets and Collations for\ndetails on setting the collations\n\nCOMMENT\n-------\n') WHERE help_topic_id = 703; -update help_topic set description = CONCAT(description, '\nCOMMENT is a comment for the table. The maximum length is 2048 characters.\nAlso used to define table parameters when creating a Spider table.\n\nCONNECTION\n----------\n\nCONNECTION is used to specify a server name or a connection string for a\nSpider, CONNECT, Federated or FederatedX table.\n\nDATA DIRECTORY/INDEX DIRECTORY\n------------------------------\n\nDATA DIRECTORY and INDEX DIRECTORY are supported for MyISAM and Aria, and DATA\nDIRECTORY is also supported by InnoDB if the innodb_file_per_table server\nsystem variable is enabled, but only in CREATE TABLE, not in ALTER TABLE. So,\ncarefully choose a path for InnoDB tables at creation time, because it cannot\nbe changed without dropping and re-creating the table. These options specify\nthe paths for data files and index files, respectively. If these options are\nomitted, the database\'s directory will be used to store data files and index\nfiles. Note that these table options do not work for partitioned tables (use\nthe partition options instead), or if the server has been invoked with the\n--skip-symbolic-links startup option. To avoid the overwriting of old files\nwith the same name that could be present in the directories, you can use the\n--keep_files_on_create option (an error will be issued if files already\nexist). These options are ignored if the NO_DIR_IN_CREATE SQL_MODE is enabled\n(useful for replication slaves). Also note that symbolic links cannot be used\nfor InnoDB tables.\n\nDATA DIRECTORY works by creating symlinks from where the table would normally\nhave been (inside the datadir) to where the option specifies. For security\nreasons, to avoid bypassing the privilege system, the server does not permit\nsymlinks inside the datadir. Therefore, DATA DIRECTORY cannot be used to\nspecify a location inside the datadir. An attempt to do so will result in an\nerror 1210 (HY000) Incorrect arguments to DATA DIRECTORY.\n\nDELAY_KEY_WRITE\n---------------\n\nDELAY_KEY_WRITE is supported by MyISAM and Aria, and can be set to 1 to speed\nup write operations. In that case, when data are modified, the indexes are not\nupdated until the table is closed. Writing the changes to the index file\naltogether can be much faster. However, note that this option is applied only\nif the delay_key_write server variable is set to \'ON\'. If it is \'OFF\' the\ndelayed index writes are always disabled, and if it is \'ALL\' the delayed index\nwrites are always used, disregarding the value of DELAY_KEY_WRITE.\n\nENCRYPTED\n---------\n\nThe ENCRYPTED table option can be used to manually set the encryption status\nof an InnoDB table. See InnoDB Encryption for more information.\n\nAria does not support the ENCRYPTED table option. See MDEV-18049.\n\nSee Data-at-Rest Encryption for more information.\n\nENCRYPTION_KEY_ID\n-----------------\n\nThe ENCRYPTION_KEY_ID table option can be used to manually set the encryption\nkey of an InnoDB table. See InnoDB Encryption for more information.\n\nAria does not support the ENCRYPTION_KEY_ID table option. See MDEV-18049.\n\nSee Data-at-Rest Encryption for more information.\n\nIETF_QUOTES\n-----------\n\nFor the CSV storage engine, the IETF_QUOTES option, when set to YES, enables\nIETF-compatible parsing of embedded quote and comma characters. Enabling this\noption for a table improves compatibility with other tools that use CSV, but\nis not compatible with MySQL CSV tables, or MariaDB CSV tables created without\nthis option. Disabled by default.\n\nINSERT_METHOD\n-------------\n\nINSERT_METHOD is only used with MERGE tables. This option determines in which\nunderlying table the new rows should be inserted. If you set it to \'NO\' (which\nis the default) no new rows can be added to the table (but you will still be\nable to perform INSERTs directly against the underlying tables). FIRST means\nthat the rows are inserted into the first table, and LAST means that thet are\ninserted into the last table.\n\nKEY_BLOCK_SIZE\n--------------\n\nKEY_BLOCK_SIZE is used to determine the size of key blocks, in bytes or\nkilobytes. However, this value is just a hint, and the storage engine could\nmodify or ignore it. If KEY_BLOCK_SIZE is set to 0, the storage engine\'s\ndefault value will be used.\n\nWith the InnoDB storage engine, if you specify a non-zero value for the\nKEY_BLOCK_SIZE table option for the whole table, then the table will\nimplicitly be created with the ROW_FORMAT table option set to COMPRESSED.\n\nMIN_ROWS/MAX_ROWS\n-----------------\n\nMIN_ROWS and MAX_ROWS let the storage engine know how many rows you are\nplanning to store as a minimum and as a maximum. These values will not be used\nas real limits, but they help the storage engine to optimize the table.\nMIN_ROWS is only used by MEMORY storage engine to decide the minimum memory\nthat is always allocated. MAX_ROWS is used to decide the minimum size for\nindexes.\n\nPACK_KEYS\n---------\n\nPACK_KEYS can be used to determine whether the indexes will be compressed. Set\nit to 1 to compress all keys. With a value of 0, compression will not be used.\nWith the DEFAULT value, only long strings will be compressed. Uncompressed\nkeys are faster.\n\nPAGE_CHECKSUM\n-------------\n\nPAGE_CHECKSUM is only applicable to Aria tables, and determines whether\nindexes and data should use page checksums for extra safety.\n\nPAGE_COMPRESSED\n---------------\n\nPAGE_COMPRESSED is used to enable InnoDB page compression for InnoDB tables.\n\nPAGE_COMPRESSION_LEVEL\n----------------------\n\nPAGE_COMPRESSION_LEVEL is used to set the compression level for InnoDB page\ncompression for InnoDB tables. The table must also have the PAGE_COMPRESSED\ntable option set to 1.\n\nValid values for PAGE_COMPRESSION_LEVEL are 1 (the best speed) through 9 (the\nbest compression), .\n\nPASSWORD\n--------\n\nPASSWORD is unused.\n\nRAID_TYPE\n---------\n\nRAID_TYPE is an obsolete option, as the raid support has been disabled since\nMySQL 5.0.\n\nROW_FORMAT\n----------\n\nThe ROW_FORMAT table option specifies the row format for the data file.\nPossible values are engine-dependent.\n\nSupported MyISAM Row Formats\n----------------------------\n\nFor MyISAM, the supported row formats are:\n\n* FIXED\n* DYNAMIC\n* COMPRESSED\n\nThe COMPRESSED row format can only be set by the myisampack command line tool.\n\nSee MyISAM Storage Formats for more information.\n\nSupported Aria Row Formats\n--------------------------\n\nFor Aria, the supported row formats are:\n\n* PAGE\n* FIXED\n* DYNAMIC.\n\nSee Aria Storage Formats for more information.\n\nSupported InnoDB Row Formats\n----------------------------\n\nFor InnoDB, the supported row formats are:\n\n* COMPACT\n* REDUNDANT\n* COMPRESSED\n* DYNAMIC.\n\nIf the ROW_FORMAT table option is set to FIXED for an InnoDB table, then the\nserver will either return an error or a warning depending on the value of the\ninnodb_strict_mode system variable. If the innodb_strict_mode system variable\nis set to OFF, then a warning is issued, and MariaDB will create the table\nusing the default row format for the specific MariaDB server version. If the\ninnodb_strict_mode system variable is set to ON, then an error will be raised.\n\nSee InnoDB Storage Formats for more information.\n\nOther Storage Engines and ROW_FORMAT\n------------------------------------\n\nOther storage engines do not support the ROW_FORMAT table option.\n\nSEQUENCE\n--------\n\nMariaDB starting with 10.3\n--------------------------\nIf the table is a sequence, then it will have the SEQUENCE set to 1.\n\nSTATS_AUTO_RECALC\n-----------------\n\nSTATS_AUTO_RECALC indicates whether to automatically recalculate persistent\nstatistics (see STATS_PERSISTENT, below) for an InnoDB table. If set to 1,\nstatistics will be recalculated when more than 10% of the data has changed.\nWhen set to 0, stats will be recalculated only when an ANALYZE TABLE is run.\nIf set to DEFAULT, or left out, the value set by the innodb_stats_auto_recalc\nsystem variable applies. See InnoDB Persistent Statistics.\n\nSTATS_PERSISTENT\n----------------\n\nSTATS_PERSISTENT indicates whether the InnoDB statistics created by ANALYZE\nTABLE will remain on disk or not. It can be set to 1 (on disk), 0 (not on\ndisk, the pre-MariaDB 10 behavior), or DEFAULT (the same as leaving out the\noption), in which case the value set by the innodb_stats_persistent system\nvariable will apply. Persistent statistics stored on disk allow the statistics\nto survive server restarts, and provide better query plan stability. See\nInnoDB Persistent Statistics.\n\nSTATS_SAMPLE_PAGES\n------------------\n\nSTATS_SAMPLE_PAGES indicates how many pages are used to sample index\nstatistics. If 0 or DEFAULT, the default value, the innodb_stats_sample_pages\nvalue is used. See InnoDB Persistent Statistics.\n\nTRANSACTIONAL\n-------------\n\nTRANSACTIONAL is only applicable for Aria tables. In future Aria tables\ncreated with this option will be fully transactional, but currently this\nprovides a form of crash protection. See Aria Storage Engine for more details.\n\nUNION\n-----\n\nUNION must be specified when you create a MERGE table. This option contains a\ncomma-separated list of MyISAM tables which are accessed by the new table. The\nlist is enclosed between parenthesis. Example: UNION = (t1,t2)\n\nWITH SYSTEM VERSIONING\n----------------------\n\nWITH SYSTEM VERSIONING is used for creating System-versioned tables.\n\nPartitions\n----------\n\npartition_options:\n PARTITION BY\n { [LINEAR] HASH(expr)\n | [LINEAR] KEY(column_list)\n | RANGE(expr)\n | LIST(expr)\n | SYSTEM_TIME [INTERVAL time_quantity time_unit] [LIMIT num] }\n [PARTITIONS num]\n [SUBPARTITION BY\n { [LINEAR] HASH(expr)\n | [LINEAR] KEY(column_list) }\n [SUBPARTITIONS num]\n ]\n [(partition_definition [, partition_definition] ...)]\npartition_definition:\n PARTITION partition_name\n [VALUES {LESS THAN {(expr) | MAXVALUE} | IN (value_list)}]\n [[STORAGE] ENGINE [=] engine_name]\n [COMMENT [=] \'comment_text\' ]\n [DATA DIRECTORY [=] \'data_dir\']\n [INDEX DIRECTORY [=] \'index_dir\']\n [MAX_ROWS [=] max_number_of_rows]\n [MIN_ROWS [=] min_number_of_rows]\n [TABLESPACE [=] tablespace_name]\n [NODEGROUP [=] node_group_id]\n [(subpartition_definition [, subpartition_definition] ...)]\nsubpartition_definition:\n SUBPARTITION logical_name\n [[STORAGE] ENGINE [=] engine_name]\n [COMMENT [=] \'comment_text\' ]\n [DATA DIRECTORY [=] \'data_dir\']\n [INDEX DIRECTORY [=] \'index_dir\']\n [MAX_ROWS [=] max_number_of_rows]\n [MIN_ROWS [=] min_number_of_rows]\n [TABLESPACE [=] tablespace_name]\n [NODEGROUP [=] node_group_id]\nIf the PARTITION BY clause is used, the table will be partitioned. A partition\nmethod must be explicitly indicated for partitions and subpartitions.\nPartition methods are:\n\n* [LINEAR] HASH creates a hash key which will be used to read and write rows.\nThe partition function can be any valid SQL expression which returns an\nINTEGER number. Thus, it is possible to use the HASH method on an integer\ncolumn, or on functions which accept integer columns as an argument. However,\nVALUES LESS THAN and VALUES IN clauses can not be used with HASH. An example:\n\nCREATE TABLE t1 (a INT, b CHAR(5), c DATETIME)\n PARTITION BY HASH ( YEAR(c) );\n\n[LINEAR] HASH can be used for subpartitions, too.\n\n* [LINEAR] KEY is similar to HASH, but the index has an even distribution of\ndata. Also, the expression can only be a column or a list of columns. VALUES\nLESS THAN and VALUES IN clauses can not be used with KEY.\n* RANGE partitions the rows using on a range of values, using the VALUES LESS\nTHAN operator. VALUES IN is not allowed with RANGE. The partition function can\nbe any valid SQL expression which returns a single value.\n* LIST assigns partitions based on a table\'s column with a restricted set of\npossible values. It is similar to RANGE, but VALUES IN must be used for at\nleast 1 columns, and VALUES LESS THAN is disallowed.\n* SYSTEM_TIME partitioning is used for System-versioned tables to store\nhistorical data separately from current data.\n\nOnly HASH and KEY can be used for subpartitions, and they can be [LINEAR].\n\nIt is possible to define up to 1024 partitions and subpartitions.\n\nThe number of defined partitions can be optionally specified as PARTITION\ncount. This can be done to avoid specifying all partitions individually. But\nyou can also declare each individual partition and, additionally, specify a\nPARTITIONS count clause; in the case, the number of PARTITIONs must equal\ncount.\n\nAlso see Partitioning Types Overview.\n\nSequences\n---------\n\nMariaDB starting with 10.3\n--------------------------\nCREATE TABLE can also be used to create a SEQUENCE. See CREATE SEQUENCE and\nSequence Overview.\n\nAtomic DDL\n----------\n\nMariaDB starting with 10.6.1\n----------------------------\nMariaDB 10.6.1 supports Atomic DDL. CREATE TABLE is atomic, except for CREATE\nOR REPLACE, which is only crash safe.\n\nExamples\n--------\n\ncreate table if not exists test (\na bigint auto_increment primary key,\nname varchar(128) charset utf8,\nkey name (name(32))\n) engine=InnoDB default charset latin1;\n\nThis example shows a couple of things:\n\n* Usage of IF NOT EXISTS; If the table already existed, it will not be\ncreated. There will not be any error for the client, just a warning.\n* How to create a PRIMARY KEY that is automatically generated.\n* How to specify a table-specific character set and another for a column.\n* How to create an index (name) that is only partly indexed (to save space).\n\nThe following clauses will work from MariaDB 10.2.1 only.\n\nCREATE TABLE t1(\n a int DEFAULT (1+1),\n b int DEFAULT (a+1),\n expires DATETIME DEFAULT(NOW() + INTERVAL 1 YEAR),\n x BLOB DEFAULT USER()\n);\n\nURL: https://mariadb.com/kb/en/create-table/') WHERE help_topic_id = 703; +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (703,38,'CREATE TABLE','Syntax\n------\n\nCREATE [OR REPLACE] [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name\n (create_definition,...) [table_options ]... [partition_options]\nCREATE [OR REPLACE] [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name\n [(create_definition,...)] [table_options ]... [partition_options]\n select_statement\nCREATE [OR REPLACE] [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name\n { LIKE old_table_name | (LIKE old_table_name) }\nselect_statement:\n [IGNORE | REPLACE] [AS] SELECT ... (Some legal select statement)\n\nDescription\n-----------\n\nUse the CREATE TABLE statement to create a table with the given name.\n\nIn its most basic form, the CREATE TABLE statement provides a table name\nfollowed by a list of columns, indexes, and constraints. By default, the table\nis created in the default database. Specify a database with db_name.tbl_name.\nIf you quote the table name, you must quote the database name and table name\nseparately as `db_name`.`tbl_name`. This is particularly useful for CREATE\nTABLE ... SELECT, because it allows to create a table into a database, which\ncontains data from other databases. See Identifier Qualifiers.\n\nIf a table with the same name exists, error 1050 results. Use IF NOT EXISTS to\nsuppress this error and issue a note instead. Use SHOW WARNINGS to see notes.\n\nThe CREATE TABLE statement automatically commits the current transaction,\nexcept when using the TEMPORARY keyword.\n\nFor valid identifiers to use as table names, see Identifier Names.\n\nNote: if the default_storage_engine is set to ColumnStore then it needs\nsetting on all UMs. Otherwise when the tables using the default engine are\nreplicated across UMs they will use the wrong engine. You should therefore not\nuse this option as a session variable with ColumnStore.\n\nMicrosecond precision can be between 0-6. If no precision is specified it is\nassumed to be 0, for backward compatibility reasons.\n\nPrivileges\n----------\n\nExecuting the CREATE TABLE statement requires the CREATE privilege for the\ntable or the database.\n\nCREATE OR REPLACE\n-----------------\n\nIf the OR REPLACE clause is used and the table already exists, then instead of\nreturning an error, the server will drop the existing table and replace it\nwith the newly defined table.\n\nThis syntax was originally added to make replication more robust if it has to\nrollback and repeat statements such as CREATE ... SELECT on replicas.\n\nCREATE OR REPLACE TABLE table_name (a int);\n\nis basically the same as:\n\nDROP TABLE IF EXISTS table_name;\nCREATE TABLE table_name (a int);\n\nwith the following exceptions:\n\n* If table_name was locked with LOCK TABLES it will continue to be locked\nafter the statement.\n* Temporary tables are only dropped if the TEMPORARY keyword was used. (With\nDROP TABLE, temporary tables are preferred to be dropped before normal\ntables).\n\nThings to be Aware of With CREATE OR REPLACE\n--------------------------------------------\n\n* The table is dropped first (if it existed), after that the CREATE is done.\nBecause of this, if the CREATE fails, then the table will not exist anymore\nafter the statement. If the table was used with LOCK TABLES it will be\nunlocked.\n* One can\'t use OR REPLACE together with IF EXISTS.\n* Slaves in replication will by default use CREATE OR REPLACE when replicating\nCREATE statements that don\'\'t use IF EXISTS. This can be changed by setting\nthe variable slave-ddl-exec-mode to STRICT.\n\nCREATE TABLE IF NOT EXISTS\n--------------------------\n\nIf the IF NOT EXISTS clause is used, then the table will only be created if a\ntable with the same name does not already exist. If the table already exists,\nthen a warning will be triggered by default.\n\nCREATE TEMPORARY TABLE\n----------------------\n\nUse the TEMPORARY keyword to create a temporary table that is only available\nto the current session. Temporary tables are dropped when the session ends.\nTemporary table names are specific to the session. They will not conflict with\nother temporary tables from other sessions even if they share the same name.\nThey will shadow names of non-temporary tables or views, if they are\nidentical. A temporary table can have the same name as a non-temporary table\nwhich is located in the same database. In that case, their name will reference\nthe temporary table when used in SQL statements. You must have the CREATE\nTEMPORARY TABLES privilege on the database to create temporary tables. If no\nstorage engine is specified, the default_tmp_storage_engine setting will\ndetermine the engine.\n\nROCKSDB temporary tables cannot be created by setting the\ndefault_tmp_storage_engine system variable, or using CREATE TEMPORARY TABLE\nLIKE. Before MariaDB 10.7, they could be specified, but would silently fail,\nand a MyISAM table would be created instead. From MariaDB 10.7 an error is\nreturned. Explicitly creating a temporary table with ENGINE=ROCKSDB has never\nbeen permitted.\n\nCREATE TABLE ... LIKE\n---------------------\n\nUse the LIKE clause instead of a full table definition to create a table with\nthe same definition as another table, including columns, indexes, and table\noptions. Foreign key definitions, as well as any DATA DIRECTORY or INDEX\nDIRECTORY table options specified on the original table, will not be created.\n\nCREATE TABLE ... SELECT\n-----------------------\n\nYou can create a table containing data from other tables using the CREATE ...\nSELECT statement. Columns will be created in the table for each field returned\nby the SELECT query.\n\nYou can also define some columns normally and add other columns from a SELECT.\nYou can also create columns in the normal way and assign them some values\nusing the query, this is done to force a certain type or other field\ncharacteristics. The columns that are not named in the query will be placed\nbefore the others. For example:\n\nCREATE TABLE test (a INT NOT NULL, b CHAR(10)) ENGINE=MyISAM\n SELECT 5 AS b, c, d FROM another_table;\n\nRemember that the query just returns data. If you want to use the same\nindexes, or the same columns attributes ([NOT] NULL, DEFAULT, AUTO_INCREMENT)\nin the new table, you need to specify them manually. Types and sizes are not\nautomatically preserved if no data returned by the SELECT requires the full\nsize, and VARCHAR could be converted into CHAR. The CAST() function can be\nused to forcee the new table to use certain types.\n\nAliases (AS) are taken into account, and they should always be used when you\nSELECT an expression (function, arithmetical operation, etc).\n\nIf an error occurs during the query, the table will not be created at all.\n\nIf the new table has a primary key or UNIQUE indexes, you can use the IGNORE\nor REPLACE keywords to handle duplicate key errors during the query. IGNORE\nmeans that the newer values must not be inserted an identical value exists in\nthe index. REPLACE means that older values must be overwritten.\n\nIf the columns in the new table are more than the rows returned by the query,\nthe columns populated by the query will be placed after other columns. Note\nthat if the strict SQL_MODE is on, and the columns that are not names in the\nquery do not have a DEFAULT value, an error will raise and no rows will be\ncopied.\n\nConcurrent inserts are not used during the execution of a CREATE ... SELECT.\n\nIf the table already exists, an error similar to the following will be\nreturned:\n\nERROR 1050 (42S01): Table \'t\' already exists\n\nIf the IF NOT EXISTS clause is used and the table exists, a note will be\nproduced instead of an error.\n\nTo insert rows from a query into an existing table, INSERT ... SELECT can be\nused.\n\nColumn Definitions\n------------------\n\ncreate_definition:\n { col_name column_definition | index_definition | period_definition | CHECK\n(expr) }\ncolumn_definition:\n data_type\n [NOT NULL | NULL] [DEFAULT default_value | (expression)]\n [ON UPDATE [NOW | CURRENT_TIMESTAMP] [(precision)]]\n [AUTO_INCREMENT] [ZEROFILL] [UNIQUE [KEY] | [PRIMARY] KEY]\n [INVISIBLE] [{WITH|WITHOUT} SYSTEM VERSIONING]\n [COMMENT \'string\'] [REF_SYSTEM_ID = value]\n [reference_definition]\n | data_type [GENERATED ALWAYS]\n AS { { ROW {START|END} } | { (expression) [VIRTUAL | PERSISTENT | STORED] } }\n [UNIQUE [KEY]] [COMMENT \'string\']\nconstraint_definition:\n CONSTRAINT [constraint_name] CHECK (expression)\nNote: Until MariaDB 10.4, MariaDB accepts the shortcut format with a\nREFERENCES clause only in ALTER TABLE and CREATE TABLE statements, but that\nsyntax does nothing. For example:\n\nCREATE TABLE b(for_key INT REFERENCES a(not_key));\n\nMariaDB simply parses it without returning any error or warning, for\ncompatibility with other DBMS\'s. Before MariaDB 10.2.1 this was also true for\nCHECK constraints. However, only the syntax described below creates foreign\nkeys.\n\nFrom MariaDB 10.5, MariaDB will attempt to apply the constraint. See Foreign\nKeys examples.\n\nEach definition either creates a column in the table or specifies and index or\nconstraint on one or more columns. See Indexes below for details on creating\nindexes.\n\nCreate a column by specifying a column name and a data type, optionally\nfollowed by column options. See Data Types for a full list of data types\nallowed in MariaDB.\n\nNULL and NOT NULL\n-----------------\n\nUse the NULL or NOT NULL options to specify that values in the column may or\nmay not be NULL, respectively. By default, values may be NULL. See also NULL\nValues in MariaDB.\n\nDEFAULT Column Option\n---------------------\n\nMariaDB starting with 10.2.1\n----------------------------\nThe DEFAULT clause was enhanced in MariaDB 10.2.1. Some enhancements include\n\n* BLOB and TEXT columns now support DEFAULT.\n* The DEFAULT clause can now be used with an expression or function.\n\nSpecify a default value using the DEFAULT clause. If you don\'t specify DEFAULT\nthen the following rules apply:\n\n* If the column is not defined with NOT NULL, AUTO_INCREMENT or TIMESTAMP, an\nexplicit DEFAULT NULL will be added.\nNote that in MySQL and in MariaDB before 10.1.6, you may get an explicit\nDEFAULT for primary key parts, if not specified with NOT NULL.\n\nThe default value will be used if you INSERT a row without specifying a value\nfor that column, or if you specify DEFAULT for that column. Before MariaDB\n10.2.1 you couldn\'t usually provide an expression or function to evaluate at\ninsertion time. You had to provide a constant default value instead. The one\nexception is that you may use CURRENT_TIMESTAMP as the default value for a\nTIMESTAMP column to use the current timestamp at insertion time.\n\nCURRENT_TIMESTAMP may also be used as the default value for a DATETIME\n\nFrom MariaDB 10.2.1 you can use most functions in DEFAULT. Expressions should\nhave parentheses around them. If you use a non deterministic function in\nDEFAULT then all inserts to the table will be replicated in row mode. You can\neven refer to earlier columns in the DEFAULT expression (excluding\nAUTO_INCREMENT columns):\n\nCREATE TABLE t1 (a int DEFAULT (1+1), b int DEFAULT (a+1));\nCREATE TABLE t2 (a bigint primary key DEFAULT UUID_SHORT());\n\nThe DEFAULT clause cannot contain any stored functions or subqueries, and a\ncolumn used in the clause must already have been defined earlier in the\nstatement.\n\nSince MariaDB 10.2.1, it is possible to assign BLOB or TEXT columns a DEFAULT\nvalue. In earlier versions, assigning a default to these columns was not\npossible.\n\nMariaDB starting with 10.3.3\n----------------------------\nStarting from 10.3.3 you can also use DEFAULT (NEXT VALUE FOR sequence)\n\nAUTO_INCREMENT Column Option\n----------------------------\n\nUse AUTO_INCREMENT to create a column whose value can can be set automatically\nfrom a simple counter. You can only use AUTO_INCREMENT on a column with an\ninteger type. The column must be a key, and there can only be one\nAUTO_INCREMENT column in a table. If you insert a row without specifying a\nvalue for that column (or if you specify 0, NULL, or DEFAULT as the value),\nthe actual value will be taken from the counter, with each insertion\nincrementing the counter by one. You can still insert a value explicitly. If\nyou insert a value that is greater than the current counter value, the counter\nis set based on the new value. An AUTO_INCREMENT column is implicitly NOT\nNULL. Use LAST_INSERT_ID to get the AUTO_INCREMENT value most recently used by\nan INSERT statement.\n\nZEROFILL Column Option\n----------------------\n\nIf the ZEROFILL column option is specified for a column using a numeric data\ntype, then the column will be set to UNSIGNED and the spaces used by default\nto pad the field are replaced with zeros. ZEROFILL is ignored in expressions\nor as part of a UNION. ZEROFILL is a non-standard MySQL and MariaDB\nenhancement.\n\nPRIMARY KEY Column Option\n-------------------------\n\nUse PRIMARY KEY to make a column a primary key. A primary key is a special\ntype of a unique key. There can be at most one primary key per table, and it\nis implicitly NOT NULL.\n\nSpecifying a column as a unique key creates a unique index on that column. See\nthe Index Definitions section below for more information.\n\nUNIQUE KEY Column Option\n------------------------\n\nUse UNIQUE KEY (or just UNIQUE) to specify that all values in the column must\nbe distinct from each other. Unless the column is NOT NULL, there may be\nmultiple rows with NULL in the column.\n\nSpecifying a column as a unique key creates a unique index on that column. See\nthe Index Definitions section below for more information.\n\nCOMMENT Column Option\n---------------------\n\nYou can provide a comment for each column using the COMMENT clause. The\nmaximum length is 1024 characters. Use the SHOW FULL COLUMNS statement to see\ncolumn comments.\n\nREF_SYSTEM_ID\n-------------\n\nREF_SYSTEM_ID can be used to specify Spatial Reference System IDs for spatial\ndata type columns. For example:\n\nCREATE TABLE t1(g GEOMETRY(9,4) REF_SYSTEM_ID=101);\n\nGenerated Columns\n-----------------\n\nA generated column is a column in a table that cannot explicitly be set to a\nspecific value in a DML query. Instead, its value is automatically generated\nbased on an expression. This expression might generate the value based on the\nvalues of other columns in the table, or it might generate the value by\ncalling built-in functions or user-defined functions (UDFs).\n\nThere are two types of generated columns:\n\n* PERSISTENT or STORED: This type\'s value is actually stored in the table.','','https://mariadb.com/kb/en/create-table/'); +update help_topic set description = CONCAT(description, '\n* VIRTUAL: This type\'s value is not stored at all. Instead, the value is\ngenerated dynamically when the table is queried. This type is the default.\n\nGenerated columns are also sometimes called computed columns or virtual\ncolumns.\n\nFor a complete description about generated columns and their limitations, see\nGenerated (Virtual and Persistent/Stored) Columns.\n\nCOMPRESSED\n----------\n\nMariaDB starting with 10.3.3\n----------------------------\nCertain columns may be compressed. See Storage-Engine Independent Column\nCompression.\n\nINVISIBLE\n---------\n\nMariaDB starting with 10.3.3\n----------------------------\nColumns may be made invisible, and hidden in certain contexts. See Invisible\nColumns.\n\nWITH SYSTEM VERSIONING Column Option\n------------------------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nColumns may be explicitly marked as included from system versioning. See\nSystem-versioned tables for details.\n\nWITHOUT SYSTEM VERSIONING Column Option\n---------------------------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nColumns may be explicitly marked as excluded from system versioning. See\nSystem-versioned tables for details.\n\nIndex Definitions\n-----------------\n\nindex_definition:\n {INDEX|KEY} [index_name] [index_type] (index_col_name,...) [index_option]\n...\n {{{|}}} {FULLTEXT|SPATIAL} [INDEX|KEY] [index_name] (index_col_name,...)\n[index_option] ...\n {{{|}}} [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (index_col_name,...)\n[index_option] ...\n {{{|}}} [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY] [index_name] [index_type]\n(index_col_name,...) [index_option] ...\n {{{|}}} [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,...)\nreference_definition\n\nindex_col_name:\n col_name [(length)] [ASC | DESC]\n\nindex_type:\n USING {BTREE | HASH | RTREE}\n\nindex_option:\n [ KEY_BLOCK_SIZE [=] value\n {{{|}}} index_type\n {{{|}}} WITH PARSER parser_name\n {{{|}}} COMMENT \'string\'\n {{{|}}} CLUSTERING={YES| NO} ]\n [ IGNORED | NOT IGNORED ]\n\nreference_definition:\n REFERENCES tbl_name (index_col_name,...)\n [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]\n [ON DELETE reference_option]\n [ON UPDATE reference_option]\n\nreference_option:\n RESTRICT | CASCADE | SET NULL | NO ACTION\n\nINDEX and KEY are synonyms.\n\nIndex names are optional, if not specified an automatic name will be assigned.\nIndex name are needed to drop indexes and appear in error messages when a\nconstraint is violated.\n\nIndex Categories\n----------------\n\nPlain Indexes\n-------------\n\nPlain indexes are regular indexes that are not unique, and are not acting as a\nprimary key or a foreign key. They are also not the \"specialized\" FULLTEXT or\nSPATIAL indexes.\n\nSee Getting Started with Indexes: Plain Indexes for more information.\n\nPRIMARY KEY\n-----------\n\nFor PRIMARY KEY indexes, you can specify a name for the index, but it is\nignored, and the name of the index is always PRIMARY. From MariaDB 10.3.18 and\nMariaDB 10.4.8, a warning is explicitly issued if a name is specified. Before\nthen, the name was silently ignored.\n\nSee Getting Started with Indexes: Primary Key for more information.\n\nUNIQUE\n------\n\nThe UNIQUE keyword means that the index will not accept duplicated values,\nexcept for NULLs. An error will raise if you try to insert duplicate values in\na UNIQUE index.\n\nFor UNIQUE indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nSee Getting Started with Indexes: Unique Index for more information.\n\nFOREIGN KEY\n-----------\n\nFor FOREIGN KEY indexes, a reference definition must be provided.\n\nFor FOREIGN KEY indexes, you can specify a name for the constraint, using the\nCONSTRAINT keyword. That name will be used in error messages.\n\nFirst, you have to specify the name of the target (parent) table and a column\nor a column list which must be indexed and whose values must match to the\nforeign key\'s values. The MATCH clause is accepted to improve the\ncompatibility with other DBMS\'s, but has no meaning in MariaDB. The ON DELETE\nand ON UPDATE clauses specify what must be done when a DELETE (or a REPLACE)\nstatements attempts to delete a referenced row from the parent table, and when\nan UPDATE statement attempts to modify the referenced foreign key columns in a\nparent table row, respectively. The following options are allowed:\n\n* RESTRICT: The delete/update operation is not performed. The statement\nterminates with a 1451 error (SQLSTATE \'2300\').\n* NO ACTION: Synonym for RESTRICT.\n* CASCADE: The delete/update operation is performed in both tables.\n* SET NULL: The update or delete goes ahead in the parent table, and the\ncorresponding foreign key fields in the child table are set to NULL. (They\nmust not be defined as NOT NULL for this to succeed).\n* SET DEFAULT: This option is currently implemented only for the PBXT storage\nengine, which is disabled by default and no longer maintained. It sets the\nchild table\'s foreign key fields to their DEFAULT values when the referenced\nparent table key entries are updated or deleted.\n\nIf either clause is omitted, the default behavior for the omitted clause is\nRESTRICT.\n\nSee Foreign Keys for more information.\n\nFULLTEXT\n--------\n\nUse the FULLTEXT keyword to create full-text indexes.\n\nSee Full-Text Indexes for more information.\n\nSPATIAL\n-------\n\nUse the SPATIAL keyword to create geometric indexes.\n\nSee SPATIAL INDEX for more information.\n\nIndex Options\n-------------\n\nKEY_BLOCK_SIZE Index Option\n---------------------------\n\nThe KEY_BLOCK_SIZE index option is similar to the KEY_BLOCK_SIZE table option.\n\nWith the InnoDB storage engine, if you specify a non-zero value for the\nKEY_BLOCK_SIZE table option for the whole table, then the table will\nimplicitly be created with the ROW_FORMAT table option set to COMPRESSED.\nHowever, this does not happen if you just set the KEY_BLOCK_SIZE index option\nfor one or more indexes in the table. The InnoDB storage engine ignores the\nKEY_BLOCK_SIZE index option. However, the SHOW CREATE TABLE statement may\nstill report it for the index.\n\nFor information about the KEY_BLOCK_SIZE index option, see the KEY_BLOCK_SIZE\ntable option below.\n\nIndex Types\n-----------\n\nEach storage engine supports some or all index types. See Storage Engine Index\nTypes for details on permitted index types for each storage engine.\n\nDifferent index types are optimized for different kind of operations:\n\n* BTREE is the default type, and normally is the best choice. It is supported\nby all storage engines. It can be used to compare a column\'s value with a\nvalue using the =, >, >=, <, <=, BETWEEN, and LIKE operators. BTREE can also\nbe used to find NULL values. Searches against an index prefix are possible.\n* HASH is only supported by the MEMORY storage engine. HASH indexes can only\nbe used for =, <=, and >= comparisons. It can not be used for the ORDER BY\nclause. Searches against an index prefix are not possible.\n* RTREE is the default for SPATIAL indexes, but if the storage engine does not\nsupport it BTREE can be used.\n\nIndex columns names are listed between parenthesis. After each column, a\nprefix length can be specified. If no length is specified, the whole column\nwill be indexed. ASC and DESC can be specified for compatibility with are\nDBMS\'s, but have no meaning in MariaDB.\n\nWITH PARSER Index Option\n------------------------\n\nThe WITH PARSER index option only applies to FULLTEXT indexes and contains the\nfulltext parser name. The fulltext parser must be an installed plugin.\n\nCOMMENT Index Option\n--------------------\n\nA comment of up to 1024 characters is permitted with the COMMENT index option.\n\nThe COMMENT index option allows you to specify a comment with user-readable\ntext describing what the index is for. This information is not used by the\nserver itself.\n\nCLUSTERING Index Option\n-----------------------\n\nThe CLUSTERING index option is only valid for tables using the TokuDB storage\nengine.\n\nIGNORED / NOT IGNORED\n---------------------\n\nMariaDB starting with 10.6.0\n----------------------------\nFrom MariaDB 10.6.0, indexes can be specified to be ignored by the optimizer.\nSee Ignored Indexes.\n\nPeriods\n-------\n\nMariaDB starting with 10.3.4\n----------------------------\n\nperiod_definition:\n PERIOD FOR SYSTEM_TIME (start_column_name, end_column_name)\nMariaDB supports a subset of the standard syntax for periods. At the moment\nit\'s only used for creating System-versioned tables. Both columns must be\ncreated, must be either of a TIMESTAMP(6) or BIGINT UNSIGNED type, and be\ngenerated as ROW START and ROW END accordingly. See System-versioned tables\nfor details.\n\nThe table must also have the WITH SYSTEM VERSIONING clause.\n\nConstraint Expressions\n----------------------\n\nMariaDB starting with 10.2.1\n----------------------------\nMariaDB 10.2.1 introduced new ways to define a constraint.\n\nNote: Before MariaDB 10.2.1, constraint expressions were accepted in the\nsyntax but ignored.\n\nMariaDB 10.2.1 introduced two ways to define a constraint:\n\n* CHECK(expression) given as part of a column definition.\n* CONSTRAINT [constraint_name] CHECK (expression)\n\nBefore a row is inserted or updated, all constraints are evaluated in the\norder they are defined. If any constraints fails, then the row will not be\nupdated. One can use most deterministic functions in a constraint, including\nUDFs.\n\ncreate table t1 (a int check(a>0) ,b int check (b> 0), constraint abc check\n(a>b));\n\nIf you use the second format and you don\'t give a name to the constraint, then\nthe constraint will get a auto generated name. This is done so that you can\nlater delete the constraint with ALTER TABLE DROP constraint_name.\n\nOne can disable all constraint expression checks by setting the variable\ncheck_constraint_checks to OFF. This is useful for example when loading a\ntable that violates some constraints that you want to later find and fix in\nSQL.\n\nSee CONSTRAINT for more information.\n\nTable Options\n-------------\n\nFor each individual table you create (or alter), you can set some table\noptions. The general syntax for setting options is:\n\n = , [ = ...]\n\nThe equal sign is optional.\n\nSome options are supported by the server and can be used for all tables, no\nmatter what storage engine they use; other options can be specified for all\nstorage engines, but have a meaning only for some engines. Also, engines can\nextend CREATE TABLE with new options.\n\nIf the IGNORE_BAD_TABLE_OPTIONS SQL_MODE is enabled, wrong table options\ngenerate a warning; otherwise, they generate an error.\n\ntable_option: \n [STORAGE] ENGINE [=] engine_name\n | AUTO_INCREMENT [=] value\n | AVG_ROW_LENGTH [=] value\n | [DEFAULT] CHARACTER SET [=] charset_name\n | CHECKSUM [=] {0 | 1}\n | [DEFAULT] COLLATE [=] collation_name\n | COMMENT [=] \'string\'\n | CONNECTION [=] \'connect_string\'\n | DATA DIRECTORY [=] \'absolute path to directory\'\n | DELAY_KEY_WRITE [=] {0 | 1}\n | ENCRYPTED [=] {YES | NO}\n | ENCRYPTION_KEY_ID [=] value\n | IETF_QUOTES [=] {YES | NO}\n | INDEX DIRECTORY [=] \'absolute path to directory\'\n | INSERT_METHOD [=] { NO | FIRST | LAST }\n | KEY_BLOCK_SIZE [=] value\n | MAX_ROWS [=] value\n | MIN_ROWS [=] value\n | PACK_KEYS [=] {0 | 1 | DEFAULT}\n | PAGE_CHECKSUM [=] {0 | 1}\n | PAGE_COMPRESSED [=] {0 | 1}\n | PAGE_COMPRESSION_LEVEL [=] {0 .. 9}\n | PASSWORD [=] \'string\'\n | ROW_FORMAT [=] {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT|PAGE}\n | SEQUENCE [=] {0|1}\n | STATS_AUTO_RECALC [=] {DEFAULT|0|1}\n | STATS_PERSISTENT [=] {DEFAULT|0|1}\n | STATS_SAMPLE_PAGES [=] {DEFAULT|value}\n | TABLESPACE tablespace_name\n | TRANSACTIONAL [=] {0 | 1}\n | UNION [=] (tbl_name[,tbl_name]...)\n | WITH SYSTEM VERSIONING\n\n[STORAGE] ENGINE\n----------------\n\n[STORAGE] ENGINE specifies a storage engine for the table. If this option is\nnot used, the default storage engine is used instead. That is, the\ndefault_storage_engine session option value if it is set, or the value\nspecified for the --default-storage-engine mysqld startup option, or the\ndefault storage engine, InnoDB. If the specified storage engine is not\ninstalled and active, the default value will be used, unless the\nNO_ENGINE_SUBSTITUTION SQL MODE is set (default). This is only true for CREATE\nTABLE, not for ALTER TABLE. For a list of storage engines that are present in\nyour server, issue a SHOW ENGINES.\n\nAUTO_INCREMENT\n--------------\n\nAUTO_INCREMENT specifies the initial value for the AUTO_INCREMENT primary key.\nThis works for MyISAM, Aria, InnoDB, MEMORY, and ARCHIVE tables. You can\nchange this option with ALTER TABLE, but in that case the new value must be\nhigher than the highest value which is present in the AUTO_INCREMENT column.\nIf the storage engine does not support this option, you can insert (and then\ndelete) a row having the wanted value - 1 in the AUTO_INCREMENT column.\n\nAVG_ROW_LENGTH\n--------------\n\nAVG_ROW_LENGTH is the average rows size. It only applies to tables using\nMyISAM and Aria storage engines that have the ROW_FORMAT table option set to\nFIXED format.\n\nMyISAM uses MAX_ROWS and AVG_ROW_LENGTH to decide the maximum size of a table\n(default: 256TB, or the maximum file size allowed by the system).\n\n[DEFAULT] CHARACTER SET/CHARSET\n-------------------------------\n\n[DEFAULT] CHARACTER SET (or [DEFAULT] CHARSET) is used to set a default\ncharacter set for the table. This is the character set used for all columns\nwhere an explicit character set is not specified. If this option is omitted or\nDEFAULT is specified, database\'s default character set will be used. See\nSetting Character Sets and Collations for details on setting the character\nsets.\n\nCHECKSUM/TABLE_CHECKSUM\n-----------------------\n\nCHECKSUM (or TABLE_CHECKSUM) can be set to 1 to maintain a live checksum for\nall table\'s rows. This makes write operations slower, but CHECKSUM TABLE will\nbe very fast. This option is only supported for MyISAM and Aria tables.\n\n[DEFAULT] COLLATE\n-----------------\n\n[DEFAULT] COLLATE is used to set a default collation for the table. This is\nthe collation used for all columns where an explicit character set is not\nspecified. If this option is omitted or DEFAULT is specified, database\'s') WHERE help_topic_id = 703; +update help_topic set description = CONCAT(description, '\ndefault option will be used. See Setting Character Sets and Collations for\ndetails on setting the collations\n\nCOMMENT\n-------\n\nCOMMENT is a comment for the table. The maximum length is 2048 characters.\nAlso used to define table parameters when creating a Spider table.\n\nCONNECTION\n----------\n\nCONNECTION is used to specify a server name or a connection string for a\nSpider, CONNECT, Federated or FederatedX table.\n\nDATA DIRECTORY/INDEX DIRECTORY\n------------------------------\n\nDATA DIRECTORY and INDEX DIRECTORY are supported for MyISAM and Aria, and DATA\nDIRECTORY is also supported by InnoDB if the innodb_file_per_table server\nsystem variable is enabled, but only in CREATE TABLE, not in ALTER TABLE. So,\ncarefully choose a path for InnoDB tables at creation time, because it cannot\nbe changed without dropping and re-creating the table. These options specify\nthe paths for data files and index files, respectively. If these options are\nomitted, the database\'s directory will be used to store data files and index\nfiles. Note that these table options do not work for partitioned tables (use\nthe partition options instead), or if the server has been invoked with the\n--skip-symbolic-links startup option. To avoid the overwriting of old files\nwith the same name that could be present in the directories, you can use the\n--keep_files_on_create option (an error will be issued if files already\nexist). These options are ignored if the NO_DIR_IN_CREATE SQL_MODE is enabled\n(useful for replication slaves). Also note that symbolic links cannot be used\nfor InnoDB tables.\n\nDATA DIRECTORY works by creating symlinks from where the table would normally\nhave been (inside the datadir) to where the option specifies. For security\nreasons, to avoid bypassing the privilege system, the server does not permit\nsymlinks inside the datadir. Therefore, DATA DIRECTORY cannot be used to\nspecify a location inside the datadir. An attempt to do so will result in an\nerror 1210 (HY000) Incorrect arguments to DATA DIRECTORY.\n\nDELAY_KEY_WRITE\n---------------\n\nDELAY_KEY_WRITE is supported by MyISAM and Aria, and can be set to 1 to speed\nup write operations. In that case, when data are modified, the indexes are not\nupdated until the table is closed. Writing the changes to the index file\naltogether can be much faster. However, note that this option is applied only\nif the delay_key_write server variable is set to \'ON\'. If it is \'OFF\' the\ndelayed index writes are always disabled, and if it is \'ALL\' the delayed index\nwrites are always used, disregarding the value of DELAY_KEY_WRITE.\n\nENCRYPTED\n---------\n\nThe ENCRYPTED table option can be used to manually set the encryption status\nof an InnoDB table. See InnoDB Encryption for more information.\n\nAria does not support the ENCRYPTED table option. See MDEV-18049.\n\nSee Data-at-Rest Encryption for more information.\n\nENCRYPTION_KEY_ID\n-----------------\n\nThe ENCRYPTION_KEY_ID table option can be used to manually set the encryption\nkey of an InnoDB table. See InnoDB Encryption for more information.\n\nAria does not support the ENCRYPTION_KEY_ID table option. See MDEV-18049.\n\nSee Data-at-Rest Encryption for more information.\n\nIETF_QUOTES\n-----------\n\nFor the CSV storage engine, the IETF_QUOTES option, when set to YES, enables\nIETF-compatible parsing of embedded quote and comma characters. Enabling this\noption for a table improves compatibility with other tools that use CSV, but\nis not compatible with MySQL CSV tables, or MariaDB CSV tables created without\nthis option. Disabled by default.\n\nINSERT_METHOD\n-------------\n\nINSERT_METHOD is only used with MERGE tables. This option determines in which\nunderlying table the new rows should be inserted. If you set it to \'NO\' (which\nis the default) no new rows can be added to the table (but you will still be\nable to perform INSERTs directly against the underlying tables). FIRST means\nthat the rows are inserted into the first table, and LAST means that thet are\ninserted into the last table.\n\nKEY_BLOCK_SIZE\n--------------\n\nKEY_BLOCK_SIZE is used to determine the size of key blocks, in bytes or\nkilobytes. However, this value is just a hint, and the storage engine could\nmodify or ignore it. If KEY_BLOCK_SIZE is set to 0, the storage engine\'s\ndefault value will be used.\n\nWith the InnoDB storage engine, if you specify a non-zero value for the\nKEY_BLOCK_SIZE table option for the whole table, then the table will\nimplicitly be created with the ROW_FORMAT table option set to COMPRESSED.\n\nMIN_ROWS/MAX_ROWS\n-----------------\n\nMIN_ROWS and MAX_ROWS let the storage engine know how many rows you are\nplanning to store as a minimum and as a maximum. These values will not be used\nas real limits, but they help the storage engine to optimize the table.\nMIN_ROWS is only used by MEMORY storage engine to decide the minimum memory\nthat is always allocated. MAX_ROWS is used to decide the minimum size for\nindexes.\n\nPACK_KEYS\n---------\n\nPACK_KEYS can be used to determine whether the indexes will be compressed. Set\nit to 1 to compress all keys. With a value of 0, compression will not be used.\nWith the DEFAULT value, only long strings will be compressed. Uncompressed\nkeys are faster.\n\nPAGE_CHECKSUM\n-------------\n\nPAGE_CHECKSUM is only applicable to Aria tables, and determines whether\nindexes and data should use page checksums for extra safety.\n\nPAGE_COMPRESSED\n---------------\n\nPAGE_COMPRESSED is used to enable InnoDB page compression for InnoDB tables.\n\nPAGE_COMPRESSION_LEVEL\n----------------------\n\nPAGE_COMPRESSION_LEVEL is used to set the compression level for InnoDB page\ncompression for InnoDB tables. The table must also have the PAGE_COMPRESSED\ntable option set to 1.\n\nValid values for PAGE_COMPRESSION_LEVEL are 1 (the best speed) through 9 (the\nbest compression), .\n\nPASSWORD\n--------\n\nPASSWORD is unused.\n\nRAID_TYPE\n---------\n\nRAID_TYPE is an obsolete option, as the raid support has been disabled since\nMySQL 5.0.\n\nROW_FORMAT\n----------\n\nThe ROW_FORMAT table option specifies the row format for the data file.\nPossible values are engine-dependent.\n\nSupported MyISAM Row Formats\n----------------------------\n\nFor MyISAM, the supported row formats are:\n\n* FIXED\n* DYNAMIC\n* COMPRESSED\n\nThe COMPRESSED row format can only be set by the myisampack command line tool.\n\nSee MyISAM Storage Formats for more information.\n\nSupported Aria Row Formats\n--------------------------\n\nFor Aria, the supported row formats are:\n\n* PAGE\n* FIXED\n* DYNAMIC.\n\nSee Aria Storage Formats for more information.\n\nSupported InnoDB Row Formats\n----------------------------\n\nFor InnoDB, the supported row formats are:\n\n* COMPACT\n* REDUNDANT\n* COMPRESSED\n* DYNAMIC.\n\nIf the ROW_FORMAT table option is set to FIXED for an InnoDB table, then the\nserver will either return an error or a warning depending on the value of the\ninnodb_strict_mode system variable. If the innodb_strict_mode system variable\nis set to OFF, then a warning is issued, and MariaDB will create the table\nusing the default row format for the specific MariaDB server version. If the\ninnodb_strict_mode system variable is set to ON, then an error will be raised.\n\nSee InnoDB Storage Formats for more information.\n\nOther Storage Engines and ROW_FORMAT\n------------------------------------\n\nOther storage engines do not support the ROW_FORMAT table option.\n\nSEQUENCE\n--------\n\nMariaDB starting with 10.3\n--------------------------\nIf the table is a sequence, then it will have the SEQUENCE set to 1.\n\nSTATS_AUTO_RECALC\n-----------------\n\nSTATS_AUTO_RECALC indicates whether to automatically recalculate persistent\nstatistics (see STATS_PERSISTENT, below) for an InnoDB table. If set to 1,\nstatistics will be recalculated when more than 10% of the data has changed.\nWhen set to 0, stats will be recalculated only when an ANALYZE TABLE is run.\nIf set to DEFAULT, or left out, the value set by the innodb_stats_auto_recalc\nsystem variable applies. See InnoDB Persistent Statistics.\n\nSTATS_PERSISTENT\n----------------\n\nSTATS_PERSISTENT indicates whether the InnoDB statistics created by ANALYZE\nTABLE will remain on disk or not. It can be set to 1 (on disk), 0 (not on\ndisk, the pre-MariaDB 10 behavior), or DEFAULT (the same as leaving out the\noption), in which case the value set by the innodb_stats_persistent system\nvariable will apply. Persistent statistics stored on disk allow the statistics\nto survive server restarts, and provide better query plan stability. See\nInnoDB Persistent Statistics.\n\nSTATS_SAMPLE_PAGES\n------------------\n\nSTATS_SAMPLE_PAGES indicates how many pages are used to sample index\nstatistics. If 0 or DEFAULT, the default value, the innodb_stats_sample_pages\nvalue is used. See InnoDB Persistent Statistics.\n\nTRANSACTIONAL\n-------------\n\nTRANSACTIONAL is only applicable for Aria tables. In future Aria tables\ncreated with this option will be fully transactional, but currently this\nprovides a form of crash protection. See Aria Storage Engine for more details.\n\nUNION\n-----\n\nUNION must be specified when you create a MERGE table. This option contains a\ncomma-separated list of MyISAM tables which are accessed by the new table. The\nlist is enclosed between parenthesis. Example: UNION = (t1,t2)\n\nWITH SYSTEM VERSIONING\n----------------------\n\nWITH SYSTEM VERSIONING is used for creating System-versioned tables.\n\nPartitions\n----------\n\npartition_options:\n PARTITION BY\n { [LINEAR] HASH(expr)\n | [LINEAR] KEY(column_list)\n | RANGE(expr)\n | LIST(expr)\n | SYSTEM_TIME [INTERVAL time_quantity time_unit] [LIMIT num] }\n [PARTITIONS num]\n [SUBPARTITION BY\n { [LINEAR] HASH(expr)\n | [LINEAR] KEY(column_list) }\n [SUBPARTITIONS num]\n ]\n [(partition_definition [, partition_definition] ...)]\npartition_definition:\n PARTITION partition_name\n [VALUES {LESS THAN {(expr) | MAXVALUE} | IN (value_list)}]\n [[STORAGE] ENGINE [=] engine_name]\n [COMMENT [=] \'comment_text\' ]\n [DATA DIRECTORY [=] \'data_dir\']\n [INDEX DIRECTORY [=] \'index_dir\']\n [MAX_ROWS [=] max_number_of_rows]\n [MIN_ROWS [=] min_number_of_rows]\n [TABLESPACE [=] tablespace_name]\n [NODEGROUP [=] node_group_id]\n [(subpartition_definition [, subpartition_definition] ...)]\nsubpartition_definition:\n SUBPARTITION logical_name\n [[STORAGE] ENGINE [=] engine_name]\n [COMMENT [=] \'comment_text\' ]\n [DATA DIRECTORY [=] \'data_dir\']\n [INDEX DIRECTORY [=] \'index_dir\']\n [MAX_ROWS [=] max_number_of_rows]\n [MIN_ROWS [=] min_number_of_rows]\n [TABLESPACE [=] tablespace_name]\n [NODEGROUP [=] node_group_id]\nIf the PARTITION BY clause is used, the table will be partitioned. A partition\nmethod must be explicitly indicated for partitions and subpartitions.\nPartition methods are:\n\n* [LINEAR] HASH creates a hash key which will be used to read and write rows.\nThe partition function can be any valid SQL expression which returns an\nINTEGER number. Thus, it is possible to use the HASH method on an integer\ncolumn, or on functions which accept integer columns as an argument. However,\nVALUES LESS THAN and VALUES IN clauses can not be used with HASH. An example:\n\nCREATE TABLE t1 (a INT, b CHAR(5), c DATETIME)\n PARTITION BY HASH ( YEAR(c) );\n\n[LINEAR] HASH can be used for subpartitions, too.\n\n* [LINEAR] KEY is similar to HASH, but the index has an even distribution of\ndata. Also, the expression can only be a column or a list of columns. VALUES\nLESS THAN and VALUES IN clauses can not be used with KEY.\n* RANGE partitions the rows using on a range of values, using the VALUES LESS\nTHAN operator. VALUES IN is not allowed with RANGE. The partition function can\nbe any valid SQL expression which returns a single value.\n* LIST assigns partitions based on a table\'s column with a restricted set of\npossible values. It is similar to RANGE, but VALUES IN must be used for at\nleast 1 columns, and VALUES LESS THAN is disallowed.\n* SYSTEM_TIME partitioning is used for System-versioned tables to store\nhistorical data separately from current data.\n\nOnly HASH and KEY can be used for subpartitions, and they can be [LINEAR].\n\nIt is possible to define up to 1024 partitions and subpartitions.\n\nThe number of defined partitions can be optionally specified as PARTITION\ncount. This can be done to avoid specifying all partitions individually. But\nyou can also declare each individual partition and, additionally, specify a\nPARTITIONS count clause; in the case, the number of PARTITIONs must equal\ncount.\n\nAlso see Partitioning Types Overview.\n\nSequences\n---------\n\nMariaDB starting with 10.3\n--------------------------\nCREATE TABLE can also be used to create a SEQUENCE. See CREATE SEQUENCE and\nSequence Overview.\n\nAtomic DDL\n----------\n\nMariaDB starting with 10.6.1\n----------------------------\nMariaDB 10.6.1 supports Atomic DDL. CREATE TABLE is atomic, except for CREATE\nOR REPLACE, which is only crash safe.\n\nExamples\n--------\n\ncreate table if not exists test (\na bigint auto_increment primary key,\nname varchar(128) charset utf8,\nkey name (name(32))\n) engine=InnoDB default charset latin1;\n\nThis example shows a couple of things:\n\n* Usage of IF NOT EXISTS; If the table already existed, it will not be\ncreated. There will not be any error for the client, just a warning.\n* How to create a PRIMARY KEY that is automatically generated.\n* How to specify a table-specific character set and another for a column.\n* How to create an index (name) that is only partly indexed (to save space).\n\nThe following clauses will work from MariaDB 10.2.1 only.\n\nCREATE TABLE t1(\n a int DEFAULT (1+1),\n b int DEFAULT (a+1),\n expires DATETIME DEFAULT(NOW() + INTERVAL 1 YEAR),\n x BLOB DEFAULT USER()\n);\n\nURL: https://mariadb.com/kb/en/create-table/') WHERE help_topic_id = 703; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (704,38,'DROP TABLE','Syntax\n------\n\nDROP [TEMPORARY] TABLE [IF EXISTS] [/*COMMENT TO SAVE*/]\n tbl_name [, tbl_name] ...\n [WAIT n|NOWAIT]\n [RESTRICT | CASCADE]\n\nDescription\n-----------\n\nDROP TABLE removes one or more tables. You must have the DROP privilege for\neach table. All table data and the table definition are removed, as well as\ntriggers associated to the table, so be careful with this statement! If any of\nthe tables named in the argument list do not exist, MariaDB returns an error\nindicating by name which non-existing tables it was unable to drop, but it\nalso drops all of the tables in the list that do exist.\n\nImportant: When a table is dropped, user privileges on the table are not\nautomatically dropped. See GRANT.\n\nIf another thread is using the table in an explicit transaction or an\nautocommit transaction, then the thread acquires a metadata lock (MDL) on the\ntable. The DROP TABLE statement will wait in the \"Waiting for table metadata\nlock\" thread state until the MDL is released. MDLs are released in the\nfollowing cases:\n\n* If an MDL is acquired in an explicit transaction, then the MDL will be\nreleased when the transaction ends.\n* If an MDL is acquired in an autocommit transaction, then the MDL will be\nreleased when the statement ends.\n* Transactional and non-transactional tables are handled the same.\n\nNote that for a partitioned table, DROP TABLE permanently removes the table\ndefinition, all of its partitions, and all of the data which was stored in\nthose partitions. It also removes the partitioning definition (.par) file\nassociated with the dropped table.\n\nFor each referenced table, DROP TABLE drops a temporary table with that name,\nif it exists. If it does not exist, and the TEMPORARY keyword is not used, it\ndrops a non-temporary table with the same name, if it exists. The TEMPORARY\nkeyword ensures that a non-temporary table will not accidentally be dropped.\n\nUse IF EXISTS to prevent an error from occurring for tables that do not exist.\nA NOTE is generated for each non-existent table when using IF EXISTS. See SHOW\nWARNINGS.\n\nIf a foreign key references this table, the table cannot be dropped. In this\ncase, it is necessary to drop the foreign key first.\n\nRESTRICT and CASCADE are allowed to make porting from other database systems\neasier. In MariaDB, they do nothing.\n\nThe comment before the table names (/*COMMENT TO SAVE*/) is stored in the\nbinary log. That feature can be used by replication tools to send their\ninternal messages.\n\nIt is possible to specify table names as db_name.tab_name. This is useful to\ndelete tables from multiple databases with one statement. See Identifier\nQualifiers for details.\n\nThe DROP privilege is required to use DROP TABLE on non-temporary tables. For\ntemporary tables, no privilege is required, because such tables are only\nvisible for the current session.\n\nNote: DROP TABLE automatically commits the current active transaction, unless\nyou use the TEMPORARY keyword.\n\nMariaDB starting with 10.5.4\n----------------------------\nFrom MariaDB 10.5.4, DROP TABLE reliably deletes table remnants inside a\nstorage engine even if the .frm file is missing. Before then, a missing .frm\nfile would result in the statement failing.\n\nMariaDB starting with 10.3.1\n----------------------------\n\nWAIT/NOWAIT\n-----------\n\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nDROP TABLE in replication\n-------------------------\n\nDROP TABLE has the following characteristics in replication:\n\n* DROP TABLE IF EXISTS are always logged.\n* DROP TABLE without IF EXISTS for tables that don\'t exist are not written to\nthe binary log.\n* Dropping of TEMPORARY tables are prefixed in the log with TEMPORARY. These\ndrops are only logged when running statement or mixed mode replication.\n* One DROP TABLE statement can be logged with up to 3 different DROP\nstatements:\nDROP TEMPORARY TABLE list_of_non_transactional_temporary_tables\nDROP TEMPORARY TABLE list_of_transactional_temporary_tables\nDROP TABLE list_of_normal_tables\n\nDROP TABLE on the primary is treated on the replica as DROP TABLE IF EXISTS.\nYou can change that by setting slave-ddl-exec-mode to STRICT.\n\nDropping an Internal #sql-... Table\n-----------------------------------\n\nFrom MariaDB 10.6, DROP TABLE is atomic and the following does not apply.\nUntil MariaDB 10.5, if the mariadbd/mysqld process is killed during an ALTER\nTABLE you may find a table named #sql-... in your data directory. In MariaDB\n10.3, InnoDB tables with this prefix will be deleted automatically during\nstartup. From MariaDB 10.4, these temporary tables will always be deleted\nautomatically.\n\nIf you want to delete one of these tables explicitly you can do so by using\nthe following syntax:\n\nDROP TABLE `#mysql50##sql-...`;\n\nWhen running an ALTER TABLE…ALGORITHM=INPLACE that rebuilds the table, InnoDB\nwill create an internal #sql-ib table. Until MariaDB 10.3.2, for these tables,\nthe .frm file will be called something else. In order to drop such a table\nafter a server crash, you must rename the #sql*.frm file to match the\n#sql-ib*.ibd file.\n\nFrom MariaDB 10.3.3, the same name as the .frm file is used for the\nintermediate copy of the table. The #sql-ib names are used by TRUNCATE and\ndelayed DROP.\n\nFrom MariaDB 10.2.19 and MariaDB 10.3.10, the #sql-ib tables will be deleted\nautomatically.\n\nDropping All Tables in a Database\n---------------------------------\n\nThe best way to drop all tables in a database is by executing DROP DATABASE,\nwhich will drop the database itself, and all tables in it.\n\nHowever, if you want to drop all tables in the database, but you also want to\nkeep the database itself and any other non-table objects in it, then you would\nneed to execute DROP TABLE to drop each individual table. You can construct\nthese DROP TABLE commands by querying the TABLES table in the\ninformation_schema database. For example:\n\nSELECT CONCAT(\'DROP TABLE IF EXISTS `\', TABLE_SCHEMA, \'`.`\', TABLE_NAME, \'`;\')\nFROM information_schema.TABLES\nWHERE TABLE_SCHEMA = \'mydb\';\n\nAtomic DROP TABLE\n-----------------\n\nMariaDB starting with 10.6.1\n----------------------------\nFrom MariaDB 10.6, DROP TABLE for a single table is atomic (MDEV-25180) for\nmost engines, including InnoDB, MyRocks, MyISAM and Aria.\n\nThis means that if there is a crash (server down or power outage) during DROP\nTABLE, all tables that have been processed so far will be completely dropped,\nincluding related trigger files and status entries, and the binary log will\ninclude a DROP TABLE statement for the dropped tables. Tables for which the\ndrop had not started will be left intact.\n\nIn older MariaDB versions, there was a small chance that, during a server\ncrash happening in the middle of DROP TABLE, some storage engines that were\nusing multiple storage files, like MyISAM, could have only a part of its\ninternal files dropped.\n\nIn MariaDB 10.5, DROP TABLE was extended to be able to delete a table that was\nonly partly dropped (MDEV-11412) as explained above. Atomic DROP TABLE is the\nfinal piece to make DROP TABLE fully reliable.\n\nDropping multiple tables is crash-safe.\n\nSee Atomic DDL for more information.\n\nExamples\n--------\n\nDROP TABLE Employees, Customers;\n\nNotes\n-----\n\nBeware that DROP TABLE can drop both tables and sequences. This is mainly done\nto allow old tools like mysqldump to work with sequences.\n\nURL: https://mariadb.com/kb/en/drop-table/','','https://mariadb.com/kb/en/drop-table/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (705,38,'RENAME TABLE','Syntax\n------\n\nRENAME TABLE[S] [IF EXISTS] tbl_name \n [WAIT n | NOWAIT]\n TO new_tbl_name\n [, tbl_name2 TO new_tbl_name2] ...\n\nDescription\n-----------\n\nThis statement renames one or more tables or views, but not the privileges\nassociated with them.\n\nIF EXISTS\n---------\n\nMariaDB starting with 10.5.2\n----------------------------\nIf this directive is used, one will not get an error if the table to be\nrenamed doesn\'t exist.\n\nThe rename operation is done atomically, which means that no other session can\naccess any of the tables while the rename is running. For example, if you have\nan existing table old_table, you can create another table new_table that has\nthe same structure but is empty, and then replace the existing table with the\nempty one as follows (assuming that backup_table does not already exist):\n\nCREATE TABLE new_table (...);\nRENAME TABLE old_table TO backup_table, new_table TO old_table;\n\ntbl_name can optionally be specified as db_name.tbl_name. See Identifier\nQualifiers. This allows to use RENAME to move a table from a database to\nanother (as long as they are on the same filesystem):\n\nRENAME TABLE db1.t TO db2.t;\n\nNote that moving a table to another database is not possible if it has some\ntriggers. Trying to do so produces the following error:\n\nERROR 1435 (HY000): Trigger in wrong schema\n\nAlso, views cannot be moved to another database:\n\nERROR 1450 (HY000): Changing schema from \'old_db\' to \'new_db\' is not allowed.\n\nMultiple tables can be renamed in a single statement. The presence or absence\nof the optional S (RENAME TABLE or RENAME TABLES) has no impact, whether a\nsingle or multiple tables are being renamed.\n\nIf a RENAME TABLE renames more than one table and one renaming fails, all\nrenames executed by the same statement are rolled back.\n\nRenames are always executed in the specified order. Knowing this, it is also\npossible to swap two tables\' names:\n\nRENAME TABLE t1 TO tmp_table,\n t2 TO t1,\n tmp_table TO t2;\n\nWAIT/NOWAIT\n-----------\n\nMariaDB starting with 10.3.0\n----------------------------\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nPrivileges\n----------\n\nExecuting the RENAME TABLE statement requires the DROP, CREATE and INSERT\nprivileges for the table or the database.\n\nAtomic RENAME TABLE\n-------------------\n\nMariaDB starting with 10.6.1\n----------------------------\nFrom MariaDB 10.6, RENAME TABLE is atomic for most engines, including InnoDB,\nMyRocks, MyISAM and Aria (MDEV-23842). This means that if there is a crash\n(server down or power outage) during RENAME TABLE, all tables will revert to\ntheir original names and any changes to trigger files will be reverted.\n\nIn older MariaDB version there was a small chance that, during a server crash\nhappening in the middle of RENAME TABLE, some tables could have been renamed\n(in the worst case partly) while others would not be renamed.\n\nSee Atomic DDL for more information.\n\nURL: https://mariadb.com/kb/en/rename-table/','','https://mariadb.com/kb/en/rename-table/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (706,38,'TRUNCATE TABLE','Syntax\n------\n\nTRUNCATE [TABLE] tbl_name\n [WAIT n | NOWAIT]\n\nDescription\n-----------\n\nTRUNCATE TABLE empties a table completely. It requires the DROP privilege. See\nGRANT.\n\ntbl_name can also be specified in the form db_name.tbl_name (see Identifier\nQualifiers).\n\nLogically, TRUNCATE TABLE is equivalent to a DELETE statement that deletes all\nrows, but there are practical differences under some circumstances.\n\nTRUNCATE TABLE will fail for an InnoDB table if any FOREIGN KEY constraints\nfrom other tables reference the table, returning the error:\n\nERROR 1701 (42000): Cannot truncate a table referenced in a foreign key\nconstraint\n\nForeign Key constraints between columns in the same table are permitted.\n\nFor an InnoDB table, if there are no FOREIGN KEY constraints, InnoDB performs\nfast truncation by dropping the original table and creating an empty one with\nthe same definition, which is much faster than deleting rows one by one. The\nAUTO_INCREMENT counter is reset by TRUNCATE TABLE, regardless of whether there\nis a FOREIGN KEY constraint.\n\nThe count of rows affected by TRUNCATE TABLE is accurate only when it is\nmapped to a DELETE statement.\n\nFor other storage engines, TRUNCATE TABLE differs from DELETE in the following\nways:\n\n* Truncate operations drop and re-create the table, which is much\n faster than deleting rows one by one, particularly for large tables.\n* Truncate operations cause an implicit commit.\n* Truncation operations cannot be performed if the session holds an\n active table lock.\n* Truncation operations do not return a meaningful value for the number\n of deleted rows. The usual result is \"0 rows affected,\" which should\n be interpreted as \"no information.\"\n* As long as the table format file tbl_name.frm is valid, the\n table can be re-created as an empty table\n with TRUNCATE TABLE, even if the data or index files have become\n corrupted.\n* The table handler does not remember the last\n used AUTO_INCREMENT value, but starts counting\n from the beginning. This is true even for MyISAM and InnoDB, which normally\n do not reuse sequence values.\n* When used with partitioned tables, TRUNCATE TABLE preserves\n the partitioning; that is, the data and index files are dropped and\n re-created, while the partition definitions (.par) file is\n unaffected.\n* Since truncation of a table does not make any use of DELETE,\n the TRUNCATE statement does not invoke ON DELETE triggers.\n* TRUNCATE TABLE will only reset the values in the Performance Schema summary\ntables to zero or null, and will not remove the rows.\n\nFor the purposes of binary logging and replication, TRUNCATE TABLE is treated\nas DROP TABLE followed by CREATE TABLE (DDL rather than DML).\n\nTRUNCATE TABLE does not work on views. Currently, TRUNCATE TABLE drops all\nhistorical records from a system-versioned table.\n\nMariaDB starting with 10.3.0\n----------------------------\n\nWAIT/NOWAIT\n-----------\n\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nOracle-mode\n-----------\n\nOracle-mode from MariaDB 10.3 permits the optional keywords REUSE STORAGE or\nDROP STORAGE to be used.\n\nTRUNCATE [TABLE] tbl_name [{DROP | REUSE} STORAGE] [WAIT n | NOWAIT]\n\nThese have no effect on the operation.\n\nPerformance\n-----------\n\nTRUNCATE TABLE is faster than DELETE, because it drops and re-creates a table.\n\nWith InnoDB, TRUNCATE TABLE is slower if innodb_file_per_table=ON is set (the\ndefault). This is because TRUNCATE TABLE unlinks the underlying tablespace\nfile, which can be an expensive operation. See MDEV-8069 for more details.\n\nThe performance issues with innodb_file_per_table=ON can be exacerbated in\ncases where the InnoDB buffer pool is very large and\ninnodb_adaptive_hash_index=ON is set. In that case, using DROP TABLE followed\nby CREATE TABLE instead of TRUNCATE TABLE may perform better. Setting\ninnodb_adaptive_hash_index=OFF (it defaults to ON before MariaDB 10.5) can\nalso help. In MariaDB 10.2 only, from MariaDB 10.2.19, this performance can\nalso be improved by setting innodb_safe_truncate=OFF. See MDEV-9459 for more\ndetails.\n\nSetting innodb_adaptive_hash_index=OFF can also improve TRUNCATE TABLE\nperformance in general. See MDEV-16796 for more details.\n\nURL: https://mariadb.com/kb/en/truncate-table/','','https://mariadb.com/kb/en/truncate-table/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (707,38,'CREATE DATABASE','Syntax\n------\n\nCREATE [OR REPLACE] {DATABASE | SCHEMA} [IF NOT EXISTS] db_name\n [create_specification] ...\n\ncreate_specification:\n [DEFAULT] CHARACTER SET [=] charset_name\n | [DEFAULT] COLLATE [=] collation_name\n | COMMENT [=] \'comment\'\n\nDescription\n-----------\n\nCREATE DATABASE creates a database with the given name. To use this statement,\nyou need the CREATE privilege for the database. CREATE SCHEMA is a synonym for\nCREATE DATABASE.\n\nFor valid identifiers to use as database names, see Identifier Names.\n\nOR REPLACE\n----------\n\nMariaDB starting with 10.1.3\n----------------------------\nThe OR REPLACE clause was added in MariaDB 10.1.3\n\nIf the optional OR REPLACE clause is used, it acts as a shortcut for:\n\nDROP DATABASE IF EXISTS db_name;\nCREATE DATABASE db_name ...;\n\nIF NOT EXISTS\n-------------\n\nWhen the IF NOT EXISTS clause is used, MariaDB will return a warning instead\nof an error if the specified database already exists.\n\nCOMMENT\n-------\n\nMariaDB starting with 10.5.0\n----------------------------\nFrom MariaDB 10.5.0, it is possible to add a comment of a maximum of 1024\nbytes. If the comment length exceeds this length, a error/warning code 4144 is\nthrown. The database comment is also added to the db.opt file, as well as to\nthe information_schema.schemata table.\n\nExamples\n--------\n\nCREATE DATABASE db1;\nQuery OK, 1 row affected (0.18 sec)\n\nCREATE DATABASE db1;\nERROR 1007 (HY000): Can\'t create database \'db1\'; database exists\n\nCREATE OR REPLACE DATABASE db1;\nQuery OK, 2 rows affected (0.00 sec)\n\nCREATE DATABASE IF NOT EXISTS db1;\nQuery OK, 1 row affected, 1 warning (0.01 sec)\n\nSHOW WARNINGS;\n+-------+------+----------------------------------------------+\n| Level | Code | Message |\n+-------+------+----------------------------------------------+\n| Note | 1007 | Can\'t create database \'db1\'; database exists |\n+-------+------+----------------------------------------------+\n\nSetting the character sets and collation. See Setting Character Sets and\nCollations for more details.\n\nCREATE DATABASE czech_slovak_names \n CHARACTER SET = \'keybcs2\'\n COLLATE = \'keybcs2_bin\';\n\nComments, from MariaDB 10.5.0:\n\nCREATE DATABASE presentations COMMENT \'Presentations for conferences\';\n\nURL: https://mariadb.com/kb/en/create-database/','','https://mariadb.com/kb/en/create-database/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (708,38,'CREATE EVENT','Syntax\n------\n\nCREATE [OR REPLACE]\n [DEFINER = { user | CURRENT_USER | role | CURRENT_ROLE }]\n EVENT\n [IF NOT EXISTS]\n event_name\n ON SCHEDULE schedule\n [ON COMPLETION [NOT] PRESERVE]\n [ENABLE | DISABLE | DISABLE ON SLAVE]\n [COMMENT \'comment\']\n DO sql_statement;\n\nschedule:\n AT timestamp [+ INTERVAL interval] ...\n | EVERY interval\n [STARTS timestamp [+ INTERVAL interval] ...]\n [ENDS timestamp [+ INTERVAL interval] ...]\n\ninterval:\n quantity {YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE |\n WEEK | SECOND | YEAR_MONTH | DAY_HOUR | DAY_MINUTE |\n DAY_SECOND | HOUR_MINUTE | HOUR_SECOND | MINUTE_SECOND}\n\nDescription\n-----------\n\nThis statement creates and schedules a new event. It requires the EVENT\nprivilege for the schema in which the event is to be created.\n\nThe minimum requirements for a valid CREATE EVENT statement are as follows:\n\n* The keywords CREATE EVENT plus an event name, which uniquely identifies\n the event in the current schema. (Prior to MySQL 5.1.12, the event name\n needed to be unique only among events created by the same user on a given\n database.)\n* An ON SCHEDULE clause, which determines when and how often the event\n executes.\n* A DO clause, which contains the SQL statement to be executed by an\n event.\n\nHere is an example of a minimal CREATE EVENT statement:\n\nCREATE EVENT myevent\n ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR\n DO\n UPDATE myschema.mytable SET mycol = mycol + 1;\n\nThe previous statement creates an event named myevent. This event executes\nonce — one hour following its creation — by running an SQL statement that\nincrements the value of the myschema.mytable table\'s mycol column by 1.\n\nThe event_name must be a valid MariaDB identifier with a maximum length of 64\ncharacters. It may be delimited using back ticks, and may be qualified with\nthe name of a database schema. An event is associated with both a MariaDB user\n(the definer) and a schema, and its name must be unique among names of events\nwithin that schema. In general, the rules governing event names are the same\nas those for names of stored routines. See Identifier Names.\n\nIf no schema is indicated as part of event_name, the default (current) schema\nis assumed.\n\nFor valid identifiers to use as event names, see Identifier Names.\n\nOR REPLACE\n----------\n\nThe OR REPLACE clause was included in MariaDB 10.1.4. If used and the event\nalready exists, instead of an error being returned, the existing event will be\ndropped and replaced by the newly defined event.\n\nIF NOT EXISTS\n-------------\n\nIf the IF NOT EXISTS clause is used, MariaDB will return a warning instead of\nan error if the event already exists. Cannot be used together with OR REPLACE.\n\nON SCHEDULE\n-----------\n\nThe ON SCHEDULE clause can be used to specify when the event must be triggered.\n\nAT\n--\n\nIf you want to execute the event only once (one time event), you can use the\nAT keyword, followed by a timestamp. If you use CURRENT_TIMESTAMP, the event\nacts as soon as it is created. As a convenience, you can add one or more\nintervals to that timestamp. You can also specify a timestamp in the past, so\nthat the event is stored but not triggered, until you modify it via ALTER\nEVENT.\n\nThe following example shows how to create an event that will be triggered\ntomorrow at a certain time:\n\nCREATE EVENT example\nON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 DAY + INTERVAL 3 HOUR\nDO something;\n\nYou can also specify that an event must be triggered at a regular interval\n(recurring event). In such cases, use the EVERY clause followed by the\ninterval.\n\nIf an event is recurring, you can specify when the first execution must happen\nvia the STARTS clause and a maximum time for the last execution via the ENDS\nclause. STARTS and ENDS clauses are followed by a timestamp and, optionally,\none or more intervals. The ENDS clause can specify a timestamp in the past, so\nthat the event is stored but not executed until you modify it via ALTER EVENT.\n\nIn the following example, next month a recurring event will be triggered\nhourly for a week:\n\nCREATE EVENT example\nON SCHEDULE EVERY 1 HOUR\nSTARTS CURRENT_TIMESTAMP + INTERVAL 1 MONTH\nENDS CURRENT_TIMESTAMP + INTERVAL 1 MONTH + INTERVAL 1 WEEK\nDO some_task;\n\nIntervals consist of a quantity and a time unit. The time units are the same\nused for other staments and time functions, except that you can\'t use\nmicroseconds for events. For simple time units, like HOUR or MINUTE, the\nquantity is an integer number, for example \'10 MINUTE\'. For composite time\nunits, like HOUR_MINUTE or HOUR_SECOND, the quantity must be a string with all\ninvolved simple values and their separators, for example \'2:30\' or \'2:30:30\'.\n\nON COMPLETION [NOT] PRESERVE\n----------------------------\n\nThe ON COMPLETION clause can be used to specify if the event must be deleted\nafter its last execution (that is, after its AT or ENDS timestamp is past). By\ndefault, events are dropped when they are expired. To explicitly state that\nthis is the desired behaviour, you can use ON COMPLETION NOT PRESERVE.\nInstead, if you want the event to be preserved, you can use ON COMPLETION\nPRESERVE.\n\nIn you specify ON COMPLETION NOT PRESERVE, and you specify a timestamp in the\npast for AT or ENDS clause, the event will be immediatly dropped. In such\ncases, you will get a Note 1558: \"Event execution time is in the past and ON\nCOMPLETION NOT PRESERVE is set. The event was dropped immediately after\ncreation\".\n\nENABLE/DISABLE/DISABLE ON SLAVE\n-------------------------------\n\nEvents are ENABLEd by default. If you want to stop MariaDB from executing an\nevent, you may specify DISABLE. When it is ready to be activated, you may\nenable it using ALTER EVENT. Another option is DISABLE ON SLAVE, which\nindicates that an event was created on a master and has been replicated to the\nslave, which is prevented from executing the event. If DISABLE ON SLAVE is\nspecifically set, the event will be disabled everywhere. It will not be\nexecuted on the mater or the slaves.\n\nCOMMENT\n-------\n\nThe COMMENT clause may be used to set a comment for the event. Maximum length\nfor comments is 64 characters. The comment is a string, so it must be quoted.\nTo see events comments, you can query the INFORMATION_SCHEMA.EVENTS table (the\ncolumn is named EVENT_COMMENT).\n\nExamples\n--------\n\nMinimal CREATE EVENT statement:\n\nCREATE EVENT myevent\n ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR\n DO\n UPDATE myschema.mytable SET mycol = mycol + 1;\n\nAn event that will be triggered tomorrow at a certain time:\n\nCREATE EVENT example\nON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 DAY + INTERVAL 3 HOUR\nDO something;\n\nNext month a recurring event will be triggered hourly for a week:\n\nCREATE EVENT example\nON SCHEDULE EVERY 1 HOUR\nSTARTS CURRENT_TIMESTAMP + INTERVAL 1 MONTH\nENDS CURRENT_TIMESTAMP + INTERVAL 1 MONTH + INTERVAL 1 WEEK\nDO some_task;\n\nOR REPLACE and IF NOT EXISTS:\n\nCREATE EVENT myevent\n ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR\n DO\n UPDATE myschema.mytable SET mycol = mycol + 1;\nERROR 1537 (HY000): Event \'myevent\' already exists\n\nCREATE OR REPLACE EVENT myevent\n ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR\n DO\n UPDATE myschema.mytable SET mycol = mycol + 1;;\nQuery OK, 0 rows affected (0.00 sec)\n\nCREATE EVENT IF NOT EXISTS myevent\n ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR\n DO\n UPDATE myschema.mytable SET mycol = mycol + 1;\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+--------------------------------+\n| Level | Code | Message |\n+-------+------+--------------------------------+\n| Note | 1537 | Event \'myevent\' already exists |\n+-------+------+--------------------------------+\n\nURL: https://mariadb.com/kb/en/create-event/','','https://mariadb.com/kb/en/create-event/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (709,38,'CREATE FUNCTION','Syntax\n------\n\nCREATE [OR REPLACE]\n [DEFINER = {user | CURRENT_USER | role | CURRENT_ROLE }]\n [AGGREGATE] FUNCTION [IF NOT EXISTS] func_name ([func_parameter[,...]])\n RETURNS type\n [characteristic ...]\n RETURN func_body\nfunc_parameter:\n [ IN | OUT | INOUT | IN OUT ] param_name type\ntype:\n Any valid MariaDB data type\ncharacteristic:\n LANGUAGE SQL\n | [NOT] DETERMINISTIC\n | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }\n | SQL SECURITY { DEFINER | INVOKER }\n | COMMENT \'string\'\nfunc_body:\n Valid SQL procedure statement\n\nDescription\n-----------\n\nUse the CREATE FUNCTION statement to create a new stored function. You must\nhave the CREATE ROUTINE database privilege to use CREATE FUNCTION. A function\ntakes any number of arguments and returns a value from the function body. The\nfunction body can be any valid SQL expression as you would use, for example,\nin any select expression. If you have the appropriate privileges, you can call\nthe function exactly as you would any built-in function. See Security below\nfor details on privileges.\n\nYou can also use a variant of the CREATE FUNCTION statement to install a\nuser-defined function (UDF) defined by a plugin. See CREATE FUNCTION (UDF) for\ndetails.\n\nYou can use a SELECT statement for the function body by enclosing it in\nparentheses, exactly as you would to use a subselect for any other expression.\nThe SELECT statement must return a single value. If more than one column is\nreturned when the function is called, error 1241 results. If more than one row\nis returned when the function is called, error 1242 results. Use a LIMIT\nclause to ensure only one row is returned.\n\nYou can also replace the RETURN clause with a BEGIN...END compound statement.\nThe compound statement must contain a RETURN statement. When the function is\ncalled, the RETURN statement immediately returns its result, and any\nstatements after RETURN are effectively ignored.\n\nBy default, a function is associated with the current database. To associate\nthe function explicitly with a given database, specify the fully-qualified\nname as db_name.func_name when you create it. If the function name is the same\nas the name of a built-in function, you must use the fully qualified name when\nyou call it.\n\nThe parameter list enclosed within parentheses must always be present. If\nthere are no parameters, an empty parameter list of () should be used.\nParameter names are not case sensitive.\n\nEach parameter can be declared to use any valid data type, except that the\nCOLLATE attribute cannot be used.\n\nFor valid identifiers to use as function names, see Identifier Names.\n\nIN | OUT | INOUT | IN OUT\n-------------------------\n\nMariaDB starting with 10.8.0\n----------------------------\nThe function parameter qualifiers for IN, OUT, INOUT, and IN OUT were added in\na 10.8.0 preview release. Prior to 10.8.0 quantifiers were supported only in\nprocedures.\n\nOUT, INOUT and its equivalent IN OUT, are only valid if called from SET and\nnot SELECT. These quantifiers are especially useful for creating functions\nwith more than one return value. This allows functions to be more complex and\nnested.\n\nDELIMITER $$\nCREATE FUNCTION add_func3(IN a INT, IN b INT, OUT c INT) RETURNS INT\nBEGIN\n SET c = 100;\n RETURN a + b;\nEND;\n$$\nDELIMITER ;\n\nSET @a = 2;\nSET @b = 3;\nSET @c = 0;\nSET @res= add_func3(@a, @b, @c);\n\nSELECT add_func3(@a, @b, @c);\nERROR 4186 (HY000): OUT or INOUT argument 3 for function add_func3 is not\nallowed here\n\nDELIMITER $$\nCREATE FUNCTION add_func4(IN a INT, IN b INT, d INT) RETURNS INT\nBEGIN\n DECLARE c, res INT;\n SET res = add_func3(a, b, c) + d;\n if (c > 99) then\n return 3;\n else\n return res;\n end if;\nEND;\n$$\n\nDELIMITER ;\n\nSELECT add_func4(1,2,3);\n+------------------+\n| add_func4(1,2,3) |\n+------------------+\n| 3 |\n+------------------+\n\nAGGREGATE\n---------\n\nMariaDB starting with 10.3.3\n----------------------------\nFrom MariaDB 10.3.3, it is possible to create stored aggregate functions as\nwell. See Stored Aggregate Functions for details.\n\nRETURNS\n-------\n\nThe RETURNS clause specifies the return type of the function. NULL values are\npermitted with all return types.\n\nWhat happens if the RETURN clause returns a value of a different type? It\ndepends on the SQL_MODE in effect at the moment of the function creation.\n\nIf the SQL_MODE is strict (STRICT_ALL_TABLES or STRICT_TRANS_TABLES flags are\nspecified), a 1366 error will be produced.\n\nOtherwise, the value is coerced to the proper type. For example, if a function\nspecifies an ENUM or SET value in the RETURNS clause, but the RETURN clause\nreturns an integer, the value returned from the function is the string for the\ncorresponding ENUM member of set of SET members.\n\nMariaDB stores the SQL_MODE system variable setting that is in effect at the\ntime a routine is created, and always executes the routine with this setting\nin force, regardless of the server SQL mode in effect when the routine is\ninvoked.\n\nLANGUAGE SQL\n------------\n\nLANGUAGE SQL is a standard SQL clause, and it can be used in MariaDB for\nportability. However that clause has no meaning, because SQL is the only\nsupported language for stored functions.\n\nA function is deterministic if it can produce only one result for a given list\nof parameters. If the result may be affected by stored data, server variables,\nrandom numbers or any value that is not explicitly passed, then the function\nis not deterministic. Also, a function is non-deterministic if it uses\nnon-deterministic functions like NOW() or CURRENT_TIMESTAMP(). The optimizer\nmay choose a faster execution plan if it known that the function is\ndeterministic. In such cases, you should declare the routine using the\nDETERMINISTIC keyword. If you want to explicitly state that the function is\nnot deterministic (which is the default) you can use the NOT DETERMINISTIC\nkeywords.\n\nIf you declare a non-deterministic function as DETERMINISTIC, you may get\nincorrect results. If you declare a deterministic function as NOT\nDETERMINISTIC, in some cases the queries will be slower.\n\nOR REPLACE\n----------\n\nMariaDB starting with 10.1.3\n----------------------------\nIf the optional OR REPLACE clause is used, it acts as a shortcut for:\n\nDROP FUNCTION IF EXISTS function_name;\nCREATE FUNCTION function_name ...;\n\nwith the exception that any existing privileges for the function are not\ndropped.\n\nIF NOT EXISTS\n-------------\n\nMariaDB starting with 10.1.3\n----------------------------\nIf the IF NOT EXISTS clause is used, MariaDB will return a warning instead of\nan error if the function already exists. Cannot be used together with OR\nREPLACE.\n\n[NOT] DETERMINISTIC\n-------------------\n\nThe [NOT] DETERMINISTIC clause also affects binary logging, because the\nSTATEMENT format can not be used to store or replicate non-deterministic\nstatements.\n\nCONTAINS SQL, NO SQL, READS SQL DATA, and MODIFIES SQL DATA are informative\nclauses that tell the server what the function does. MariaDB does not check in\nany way whether the specified clause is correct. If none of these clauses are\nspecified, CONTAINS SQL is used by default.\n\nMODIFIES SQL DATA\n-----------------\n\nMODIFIES SQL DATA means that the function contains statements that may modify\ndata stored in databases. This happens if the function contains statements\nlike DELETE, UPDATE, INSERT, REPLACE or DDL.\n\nREADS SQL DATA\n--------------\n\nREADS SQL DATA means that the function reads data stored in databases, but\ndoes not modify any data. This happens if SELECT statements are used, but\nthere no write operations are executed.\n\nCONTAINS SQL\n------------\n\nCONTAINS SQL means that the function contains at least one SQL statement, but\nit does not read or write any data stored in a database. Examples include SET\nor DO.\n\nNO SQL\n------\n\nNO SQL means nothing, because MariaDB does not currently support any language\nother than SQL.\n\nOracle Mode\n-----------\n\nMariaDB starting with 10.3\n--------------------------\nFrom MariaDB 10.3, a subset of Oracle\'s PL/SQL language has been supported in\naddition to the traditional SQL/PSM-based MariaDB syntax. See Oracle mode from\nMariaDB 10.3 for details on changes when running Oracle mode.\n\nSecurity\n--------\n\nYou must have the EXECUTE privilege on a function to call it. MariaDB\nautomatically grants the EXECUTE and ALTER ROUTINE privileges to the account\nthat called CREATE FUNCTION, even if the DEFINER clause was used.\n\nEach function has an account associated as the definer. By default, the\ndefiner is the account that created the function. Use the DEFINER clause to\nspecify a different account as the definer. You must have the SUPER privilege,\nor, from MariaDB 10.5.2, the SET USER privilege, to use the DEFINER clause.\nSee Account Names for details on specifying accounts.\n\nThe SQL SECURITY clause specifies what privileges are used when a function is\ncalled. If SQL SECURITY is INVOKER, the function body will be evaluated using\nthe privileges of the user calling the function. If SQL SECURITY is DEFINER,\nthe function body is always evaluated using the privileges of the definer\naccount. DEFINER is the default.\n\nThis allows you to create functions that grant limited access to certain data.\nFor example, say you have a table that stores some employee information, and\nthat you\'ve granted SELECT privileges only on certain columns to the user\naccount roger.\n\nCREATE TABLE employees (name TINYTEXT, dept TINYTEXT, salary INT);\nGRANT SELECT (name, dept) ON employees TO roger;\n\nTo allow the user the get the maximum salary for a department, define a\nfunction and grant the EXECUTE privilege:\n\nCREATE FUNCTION max_salary (dept TINYTEXT) RETURNS INT RETURN\n (SELECT MAX(salary) FROM employees WHERE employees.dept = dept);\nGRANT EXECUTE ON FUNCTION max_salary TO roger;\n\nSince SQL SECURITY defaults to DEFINER, whenever the user roger calls this\nfunction, the subselect will execute with your privileges. As long as you have\nprivileges to select the salary of each employee, the caller of the function\nwill be able to get the maximum salary for each department without being able\nto see individual salaries.\n\nCharacter sets and collations\n-----------------------------\n\nFunction return types can be declared to use any valid character set and\ncollation. If used, the COLLATE attribute needs to be preceded by a CHARACTER\nSET attribute.\n\nIf the character set and collation are not specifically set in the statement,\nthe database defaults at the time of creation will be used. If the database\ndefaults change at a later stage, the stored function character set/collation\nwill not be changed at the same time; the stored function needs to be dropped\nand recreated to ensure the same character set/collation as the database is\nused.\n\nExamples\n--------\n\nThe following example function takes a parameter, performs an operation using\nan SQL function, and returns the result.\n\nCREATE FUNCTION hello (s CHAR(20))\n RETURNS CHAR(50) DETERMINISTIC\n RETURN CONCAT(\'Hello, \',s,\'!\');\n\nSELECT hello(\'world\');\n+----------------+\n| hello(\'world\') |\n+----------------+\n| Hello, world! |\n+----------------+\n\nYou can use a compound statement in a function to manipulate data with\nstatements like INSERT and UPDATE. The following example creates a counter\nfunction that uses a temporary table to store the current value. Because the\ncompound statement contains statements terminated with semicolons, you have to\nfirst change the statement delimiter with the DELIMITER statement to allow the\nsemicolon to be used in the function body. See Delimiters in the mysql client\nfor more.\n\nCREATE TEMPORARY TABLE counter (c INT);\nINSERT INTO counter VALUES (0);\nDELIMITER //\nCREATE FUNCTION counter () RETURNS INT\n BEGIN\n UPDATE counter SET c = c + 1;\n RETURN (SELECT c FROM counter LIMIT 1);\n END //\nDELIMITER ;\n\nCharacter set and collation:\n\nCREATE FUNCTION hello2 (s CHAR(20))\n RETURNS CHAR(50) CHARACTER SET \'utf8\' COLLATE \'utf8_bin\' DETERMINISTIC\n RETURN CONCAT(\'Hello, \',s,\'!\');\n\nURL: https://mariadb.com/kb/en/create-function/','','https://mariadb.com/kb/en/create-function/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (710,38,'CREATE INDEX','Syntax\n------\n\nCREATE [OR REPLACE] [UNIQUE|FULLTEXT|SPATIAL] INDEX \n [IF NOT EXISTS] index_name\n [index_type]\n ON tbl_name (index_col_name,...)\n [WAIT n | NOWAIT]\n [index_option]\n [algorithm_option | lock_option] ...\n\nindex_col_name:\n col_name [(length)] [ASC | DESC]\n\nindex_type:\n USING {BTREE | HASH | RTREE}\n\nindex_option:\n [ KEY_BLOCK_SIZE [=] value\n | index_type\n | WITH PARSER parser_name\n | COMMENT \'string\'\n | CLUSTERING={YES| NO} ]\n [ IGNORED | NOT IGNORED ]\n\nalgorithm_option:\n ALGORITHM [=] {DEFAULT|INPLACE|COPY|NOCOPY|INSTANT}\n\nlock_option:\n LOCK [=] {DEFAULT|NONE|SHARED|EXCLUSIVE}\n\nDescription\n-----------\n\nCREATE INDEX is mapped to an ALTER TABLE statement to create indexes. See\nALTER TABLE. CREATE INDEX cannot be used to create a PRIMARY KEY; use ALTER\nTABLE instead.\n\nIf another connection is using the table, a metadata lock is active, and this\nstatement will wait until the lock is released. This is also true for\nnon-transactional tables.\n\nAnother shortcut, DROP INDEX, allows the removal of an index.\n\nFor valid identifiers to use as index names, see Identifier Names.\n\nNote that KEY_BLOCK_SIZE is currently ignored in CREATE INDEX, although it is\nincluded in the output of SHOW CREATE TABLE.\n\nPrivileges\n----------\n\nExecuting the CREATE INDEX statement requires the INDEX privilege for the\ntable or the database.\n\nOnline DDL\n----------\n\nOnline DDL is supported with the ALGORITHM and LOCK clauses.\n\nSee InnoDB Online DDL Overview for more information on online DDL with InnoDB.\n\nCREATE OR REPLACE INDEX\n-----------------------\n\nMariaDB starting with 10.1.4\n----------------------------\nThe OR REPLACE clause was added in MariaDB 10.1.4.\n\nIf the OR REPLACE clause is used and if the index already exists, then instead\nof returning an error, the server will drop the existing index and replace it\nwith the newly defined index.\n\nCREATE INDEX IF NOT EXISTS\n--------------------------\n\nIf the IF NOT EXISTS clause is used, then the index will only be created if an\nindex with the same name does not already exist. If the index already exists,\nthen a warning will be triggered by default.\n\nIndex Definitions\n-----------------\n\nSee CREATE TABLE: Index Definitions for information about index definitions.\n\nWAIT/NOWAIT\n-----------\n\nMariaDB starting with 10.3.0\n----------------------------\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nALGORITHM\n---------\n\nSee ALTER TABLE: ALGORITHM for more information.\n\nLOCK\n----\n\nSee ALTER TABLE: LOCK for more information.\n\nProgress Reporting\n------------------\n\nMariaDB provides progress reporting for CREATE INDEX statement for clients\nthat support the new progress reporting protocol. For example, if you were\nusing the mysql client, then the progress report might look like this::\n\nCREATE INDEX ON tab (num);;\nStage: 1 of 2 \'copy to tmp table\' 46% of stage\n\nThe progress report is also shown in the output of the SHOW PROCESSLIST\nstatement and in the contents of the information_schema.PROCESSLIST table.\n\nSee Progress Reporting for more information.\n\nWITHOUT OVERLAPS\n----------------\n\nMariaDB starting with 10.5.3\n----------------------------\nThe WITHOUT OVERLAPS clause allows one to constrain a primary or unique index\nsuch that application-time periods cannot overlap.\n\nExamples\n--------\n\nCreating a unique index:\n\nCREATE UNIQUE INDEX HomePhone ON Employees(Home_Phone);\n\nOR REPLACE and IF NOT EXISTS:\n\nCREATE INDEX xi ON xx5 (x);\nQuery OK, 0 rows affected (0.03 sec)\n\nCREATE INDEX xi ON xx5 (x);\nERROR 1061 (42000): Duplicate key name \'xi\'\n\nCREATE OR REPLACE INDEX xi ON xx5 (x);\nQuery OK, 0 rows affected (0.03 sec)\n\nCREATE INDEX IF NOT EXISTS xi ON xx5 (x);\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+-------------------------+\n| Level | Code | Message |\n+-------+------+-------------------------+\n| Note | 1061 | Duplicate key name \'xi\' |\n+-------+------+-------------------------+\n\nFrom MariaDB 10.5.3, creating a unique index for an application-time period\ntable with a WITHOUT OVERLAPS constraint:\n\nCREATE UNIQUE INDEX u ON rooms (room_number, p WITHOUT OVERLAPS);\n\nURL: https://mariadb.com/kb/en/create-index/','','https://mariadb.com/kb/en/create-index/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (710,38,'CREATE INDEX','Syntax\n------\n\nCREATE [OR REPLACE] [UNIQUE|FULLTEXT|SPATIAL] INDEX \n [IF NOT EXISTS] index_name\n [index_type]\n ON tbl_name (index_col_name,...)\n [WAIT n | NOWAIT]\n [index_option]\n [algorithm_option | lock_option] ...\n\nindex_col_name:\n col_name [(length)] [ASC | DESC]\n\nindex_type:\n USING {BTREE | HASH | RTREE}\n\nindex_option:\n [ KEY_BLOCK_SIZE [=] value\n | index_type\n | WITH PARSER parser_name\n | COMMENT \'string\'\n | CLUSTERING={YES| NO} ]\n [ IGNORED | NOT IGNORED ]\n\nalgorithm_option:\n ALGORITHM [=] {DEFAULT|INPLACE|COPY|NOCOPY|INSTANT}\n\nlock_option:\n LOCK [=] {DEFAULT|NONE|SHARED|EXCLUSIVE}\n\nDescription\n-----------\n\nCREATE INDEX is mapped to an ALTER TABLE statement to create indexes. See\nALTER TABLE. CREATE INDEX cannot be used to create a PRIMARY KEY; use ALTER\nTABLE instead.\n\nIf another connection is using the table, a metadata lock is active, and this\nstatement will wait until the lock is released. This is also true for\nnon-transactional tables.\n\nAnother shortcut, DROP INDEX, allows the removal of an index.\n\nFor valid identifiers to use as index names, see Identifier Names.\n\nNote that KEY_BLOCK_SIZE is currently ignored in CREATE INDEX, although it is\nincluded in the output of SHOW CREATE TABLE.\n\nPrivileges\n----------\n\nExecuting the CREATE INDEX statement requires the INDEX privilege for the\ntable or the database.\n\nOnline DDL\n----------\n\nOnline DDL is supported with the ALGORITHM and LOCK clauses.\n\nSee InnoDB Online DDL Overview for more information on online DDL with InnoDB.\n\nCREATE OR REPLACE INDEX\n-----------------------\n\nIf the OR REPLACE clause is used and if the index already exists, then instead\nof returning an error, the server will drop the existing index and replace it\nwith the newly defined index.\n\nCREATE INDEX IF NOT EXISTS\n--------------------------\n\nIf the IF NOT EXISTS clause is used, then the index will only be created if an\nindex with the same name does not already exist. If the index already exists,\nthen a warning will be triggered by default.\n\nIndex Definitions\n-----------------\n\nSee CREATE TABLE: Index Definitions for information about index definitions.\n\nWAIT/NOWAIT\n-----------\n\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nALGORITHM\n---------\n\nSee ALTER TABLE: ALGORITHM for more information.\n\nLOCK\n----\n\nSee ALTER TABLE: LOCK for more information.\n\nProgress Reporting\n------------------\n\nMariaDB provides progress reporting for CREATE INDEX statement for clients\nthat support the new progress reporting protocol. For example, if you were\nusing the mysql client, then the progress report might look like this::\n\nCREATE INDEX i ON tab (num);\nStage: 1 of 2 \'copy to tmp table\' 46% of stage\n\nThe progress report is also shown in the output of the SHOW PROCESSLIST\nstatement and in the contents of the information_schema.PROCESSLIST table.\n\nSee Progress Reporting for more information.\n\nWITHOUT OVERLAPS\n----------------\n\nMariaDB starting with 10.5.3\n----------------------------\nThe WITHOUT OVERLAPS clause allows one to constrain a primary or unique index\nsuch that application-time periods cannot overlap.\n\nExamples\n--------\n\nCreating a unique index:\n\nCREATE UNIQUE INDEX HomePhone ON Employees(Home_Phone);\n\nOR REPLACE and IF NOT EXISTS:\n\nCREATE INDEX xi ON xx5 (x);\nQuery OK, 0 rows affected (0.03 sec)\n\nCREATE INDEX xi ON xx5 (x);\nERROR 1061 (42000): Duplicate key name \'xi\'\n\nCREATE OR REPLACE INDEX xi ON xx5 (x);\nQuery OK, 0 rows affected (0.03 sec)\n\nCREATE INDEX IF NOT EXISTS xi ON xx5 (x);\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+-------------------------+\n| Level | Code | Message |\n+-------+------+-------------------------+\n| Note | 1061 | Duplicate key name \'xi\' |\n+-------+------+-------------------------+\n\nFrom MariaDB 10.5.3, creating a unique index for an application-time period\ntable with a WITHOUT OVERLAPS constraint:\n\nCREATE UNIQUE INDEX u ON rooms (room_number, p WITHOUT OVERLAPS);\n\nURL: https://mariadb.com/kb/en/create-index/','','https://mariadb.com/kb/en/create-index/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (711,38,'CREATE PACKAGE','MariaDB starting with 10.3.5\n----------------------------\nOracle-style packages were introduced in MariaDB 10.3.5.\n\nSyntax\n------\n\nCREATE\n [ OR REPLACE]\n [DEFINER = { user | CURRENT_USER | role | CURRENT_ROLE }]\n PACKAGE [ IF NOT EXISTS ]\n [ db_name . ] package_name\n [ package_characteristic ... ]\n{ AS | IS }\n [ package_specification_element ... ]\nEND [ package_name ]\n\npackage_characteristic:\n COMMENT \'string\'\n | SQL SECURITY { DEFINER | INVOKER }\n\npackage_specification_element:\n FUNCTION_SYM package_specification_function ;\n | PROCEDURE_SYM package_specification_procedure ;\n\npackage_specification_function:\n func_name [ ( func_param [, func_param]... ) ]\n RETURNS func_return_type\n [ package_routine_characteristic... ]\n\npackage_specification_procedure:\n proc_name [ ( proc_param [, proc_param]... ) ]\n [ package_routine_characteristic... ]\n\nfunc_return_type:\n type\n\nfunc_param:\n param_name [ IN | OUT | INOUT | IN OUT ] type\n\nproc_param:\n param_name [ IN | OUT | INOUT | IN OUT ] type\n\ntype:\n Any valid MariaDB explicit or anchored data type\n\npackage_routine_characteristic:\n COMMENT \'string\'\n | LANGUAGE SQL\n | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }\n | SQL SECURITY { DEFINER | INVOKER }\n\nDescription\n-----------\n\nThe CREATE PACKAGE statement can be used when Oracle SQL_MODE is set.\n\nThe CREATE PACKAGE creates the specification for a stored package (a\ncollection of logically related stored objects). A stored package\nspecification declares public routines (procedures and functions) of the\npackage, but does not implement these routines.\n\nA package whose specification was created by the CREATE PACKAGE statement,\nshould later be implemented using the CREATE PACKAGE BODY statement.\n\nFunction parameter quantifiers IN | OUT | INOUT | IN OUT\n--------------------------------------------------------\n\nMariaDB starting with 10.8.0\n----------------------------\nThe function parameter quantifiers for IN, OUT, INOUT, and IN OUT where added\nin a 10.8.0 preview release. Prior to 10.8.0 quantifiers were supported only\nin procedures.\n\nOUT, INOUT and its equivalent IN OUT, are only valid if called from SET and\nnot SELECT. These quantifiers are especially useful for creating functions and\nprocedures with more than one return value. This allows functions and\nprocedures to be more complex and nested.\n\nExamples\n--------\n\nSET sql_mode=ORACLE;\nDELIMITER $$\nCREATE OR REPLACE PACKAGE employee_tools AS\n FUNCTION getSalary(eid INT) RETURN DECIMAL(10,2);\n PROCEDURE raiseSalary(eid INT, amount DECIMAL(10,2));\n PROCEDURE raiseSalaryStd(eid INT);\n PROCEDURE hire(ename TEXT, esalary DECIMAL(10,2));\nEND;\n$$\nDELIMITER ;\n\nURL: https://mariadb.com/kb/en/create-package/','','https://mariadb.com/kb/en/create-package/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (712,38,'CREATE PACKAGE BODY','MariaDB starting with 10.3.5\n----------------------------\nOracle-style packages were introduced in MariaDB 10.3.5.\n\nSyntax\n------\n\nCREATE [ OR REPLACE ]\n [DEFINER = { user | CURRENT_USER | role | CURRENT_ROLE }]\n PACKAGE BODY\n [ IF NOT EXISTS ]\n [ db_name . ] package_name\n [ package_characteristic... ]\n{ AS | IS }\n package_implementation_declare_section\n package_implementation_executable_section\nEND [ package_name]\n\npackage_implementation_declare_section:\n package_implementation_item_declaration\n [ package_implementation_item_declaration... ]\n [ package_implementation_routine_definition... ]\n | package_implementation_routine_definition\n [ package_implementation_routine_definition...]\n\npackage_implementation_item_declaration:\n variable_declaration ;\n\nvariable_declaration:\n variable_name[,...] type [:= expr ]\n\npackage_implementation_routine_definition:\n FUNCTION package_specification_function\n [ package_implementation_function_body ] ;\n | PROCEDURE package_specification_procedure\n [ package_implementation_procedure_body ] ;\n\npackage_implementation_function_body:\n { AS | IS } package_routine_body [func_name]\n\npackage_implementation_procedure_body:\n { AS | IS } package_routine_body [proc_name]\n\npackage_routine_body:\n [ package_routine_declarations ]\n BEGIN\n statements [ EXCEPTION exception_handlers ]\n END\n\npackage_routine_declarations:\n package_routine_declaration \';\' [package_routine_declaration \';\']...\n\npackage_routine_declaration:\n variable_declaration\n | condition_name CONDITION FOR condition_value\n | user_exception_name EXCEPTION\n | CURSOR_SYM cursor_name\n [ ( cursor_formal_parameters ) ]\n IS select_statement\n ;\n\npackage_implementation_executable_section:\n END\n | BEGIN\n statement ; [statement ; ]...\n [EXCEPTION exception_handlers]\n END\n\nexception_handlers:\n exception_handler [exception_handler...]\n\nexception_handler:\n WHEN_SYM condition_value [, condition_value]...\n THEN_SYM statement ; [statement ;]...\n\ncondition_value:\n condition_name\n | user_exception_name\n | SQLWARNING\n | SQLEXCEPTION\n | NOT FOUND\n | OTHERS_SYM\n | SQLSTATE [VALUE] sqlstate_value\n | mariadb_error_code\n\nDescription\n-----------\n\nThe CREATE PACKAGE BODY statement can be used when Oracle SQL_MODE is set.\n\nThe CREATE PACKAGE BODY statement creates the package body for a stored\npackage. The package specification must be previously created using the CREATE\nPACKAGE statement.\n\nA package body provides implementations of the package public routines and can\noptionally have:\n\n* package-wide private variables\n* package private routines\n* forward declarations for private routines\n* an executable initialization section\n\nExamples\n--------\n\nSET sql_mode=ORACLE;\nDELIMITER $$\nCREATE OR REPLACE PACKAGE employee_tools AS\n FUNCTION getSalary(eid INT) RETURN DECIMAL(10,2);\n PROCEDURE raiseSalary(eid INT, amount DECIMAL(10,2));\n PROCEDURE raiseSalaryStd(eid INT);\n PROCEDURE hire(ename TEXT, esalary DECIMAL(10,2));\nEND;\n$$\nCREATE PACKAGE BODY employee_tools AS\n -- package body variables\n stdRaiseAmount DECIMAL(10,2):=500;\n\n-- private routines\n PROCEDURE log (eid INT, ecmnt TEXT) AS\n BEGIN\n INSERT INTO employee_log (id, cmnt) VALUES (eid, ecmnt);\n END;\n\n-- public routines\n PROCEDURE hire(ename TEXT, esalary DECIMAL(10,2)) AS\n eid INT;\n BEGIN\n INSERT INTO employee (name, salary) VALUES (ename, esalary);\n eid:= last_insert_id();\n log(eid, \'hire \' || ename);\n END;\n\nFUNCTION getSalary(eid INT) RETURN DECIMAL(10,2) AS\n nSalary DECIMAL(10,2);\n BEGIN\n SELECT salary INTO nSalary FROM employee WHERE id=eid;\n log(eid, \'getSalary id=\' || eid || \' salary=\' || nSalary);\n RETURN nSalary;\n END;\n\nPROCEDURE raiseSalary(eid INT, amount DECIMAL(10,2)) AS\n BEGIN\n UPDATE employee SET salary=salary+amount WHERE id=eid;\n log(eid, \'raiseSalary id=\' || eid || \' amount=\' || amount);\n END;\n\nPROCEDURE raiseSalaryStd(eid INT) AS\n BEGIN\n raiseSalary(eid, stdRaiseAmount);\n log(eid, \'raiseSalaryStd id=\' || eid);\n END;\n\nBEGIN\n -- This code is executed when the current session\n -- accesses any of the package routines for the first time\n log(0, \'Session \' || connection_id() || \' \' || current_user || \' started\');\nEND;\n$$\n\nDELIMITER ;\n\nURL: https://mariadb.com/kb/en/create-package-body/','','https://mariadb.com/kb/en/create-package-body/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (713,38,'CREATE PROCEDURE','Syntax\n------\n\nCREATE\n [OR REPLACE]\n [DEFINER = { user | CURRENT_USER | role | CURRENT_ROLE }]\n PROCEDURE [IF NOT EXISTS] sp_name ([proc_parameter[,...]])\n [characteristic ...] routine_body\n\nproc_parameter:\n [ IN | OUT | INOUT ] param_name type\n\ntype:\n Any valid MariaDB data type\n\ncharacteristic:\n LANGUAGE SQL\n | [NOT] DETERMINISTIC\n | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }\n | SQL SECURITY { DEFINER | INVOKER }\n | COMMENT \'string\'\n\nroutine_body:\n Valid SQL procedure statement\n\nDescription\n-----------\n\nCreates a stored procedure. By default, a routine is associated with the\ndefault database. To associate the routine explicitly with a given database,\nspecify the name as db_name.sp_name when you create it.\n\nWhen the routine is invoked, an implicit USE db_name is performed (and undone\nwhen the routine terminates). The causes the routine to have the given default\ndatabase while it executes. USE statements within stored routines are\ndisallowed.\n\nWhen a stored procedure has been created, you invoke it by using the CALL\nstatement (see CALL).\n\nTo execute the CREATE PROCEDURE statement, it is necessary to have the CREATE\nROUTINE privilege. By default, MariaDB automatically grants the ALTER ROUTINE\nand EXECUTE privileges to the routine creator. See also Stored Routine\nPrivileges.\n\nThe DEFINER and SQL SECURITY clauses specify the security context to be used\nwhen checking access privileges at routine execution time, as described here.\nRequires the SUPER privilege, or, from MariaDB 10.5.2, the SET USER privilege.\n\nIf the routine name is the same as the name of a built-in SQL function, you\nmust use a space between the name and the following parenthesis when defining\nthe routine, or a syntax error occurs. This is also true when you invoke the\nroutine later. For this reason, we suggest that it is better to avoid re-using\nthe names of existing SQL functions for your own stored routines.\n\nThe IGNORE_SPACE SQL mode applies to built-in functions, not to stored\nroutines. It is always allowable to have spaces after a routine name,\nregardless of whether IGNORE_SPACE is enabled.\n\nThe parameter list enclosed within parentheses must always be present. If\nthere are no parameters, an empty parameter list of () should be used.\nParameter names are not case sensitive.\n\nEach parameter can be declared to use any valid data type, except that the\nCOLLATE attribute cannot be used.\n\nFor valid identifiers to use as procedure names, see Identifier Names.\n\nThings to be Aware of With CREATE OR REPLACE\n--------------------------------------------\n\n* One can\'t use OR REPLACE together with IF EXISTS.\n\nCREATE PROCEDURE IF NOT EXISTS\n------------------------------\n\nIf the IF NOT EXISTS clause is used, then the procedure will only be created\nif a procedure with the same name does not already exist. If the procedure\nalready exists, then a warning will be triggered by default.\n\nIN/OUT/INOUT\n------------\n\nEach parameter is an IN parameter by default. To specify otherwise for a\nparameter, use the keyword OUT or INOUT before the parameter name.\n\nAn IN parameter passes a value into a procedure. The procedure might modify\nthe value, but the modification is not visible to the caller when the\nprocedure returns. An OUT parameter passes a value from the procedure back to\nthe caller. Its initial value is NULL within the procedure, and its value is\nvisible to the caller when the procedure returns. An INOUT parameter is\ninitialized by the caller, can be modified by the procedure, and any change\nmade by the procedure is visible to the caller when the procedure returns.\n\nFor each OUT or INOUT parameter, pass a user-defined variable in the CALL\nstatement that invokes the procedure so that you can obtain its value when the\nprocedure returns. If you are calling the procedure from within another stored\nprocedure or function, you can also pass a routine parameter or local routine\nvariable as an IN or INOUT parameter.\n\nDETERMINISTIC/NOT DETERMINISTIC\n-------------------------------\n\nDETERMINISTIC and NOT DETERMINISTIC apply only to functions. Specifying\nDETERMINISTC or NON-DETERMINISTIC in procedures has no effect. The default\nvalue is NOT DETERMINISTIC. Functions are DETERMINISTIC when they always\nreturn the same value for the same input. For example, a truncate or substring\nfunction. Any function involving data, therefore, is always NOT DETERMINISTIC.\n\nCONTAINS SQL/NO SQL/READS SQL DATA/MODIFIES SQL DATA\n----------------------------------------------------\n\nCONTAINS SQL, NO SQL, READS SQL DATA, and MODIFIES SQL DATA are informative\nclauses that tell the server what the function does. MariaDB does not check in\nany way whether the specified clause is correct. If none of these clauses are\nspecified, CONTAINS SQL is used by default.\n\nMODIFIES SQL DATA means that the function contains statements that may modify\ndata stored in databases. This happens if the function contains statements\nlike DELETE, UPDATE, INSERT, REPLACE or DDL.\n\nREADS SQL DATA means that the function reads data stored in databases, but\ndoes not modify any data. This happens if SELECT statements are used, but\nthere no write operations are executed.\n\nCONTAINS SQL means that the function contains at least one SQL statement, but\nit does not read or write any data stored in a database. Examples include SET\nor DO.\n\nNO SQL means nothing, because MariaDB does not currently support any language\nother than SQL.\n\nThe routine_body consists of a valid SQL procedure statement. This can be a\nsimple statement such as SELECT or INSERT, or it can be a compound statement\nwritten using BEGIN and END. Compound statements can contain declarations,\nloops, and other control structure statements. See Programmatic and Compound\nStatements for syntax details.\n\nMariaDB allows routines to contain DDL statements, such as CREATE and DROP.\nMariaDB also allows stored procedures (but not stored functions) to contain\nSQL transaction statements such as COMMIT.\n\nFor additional information about statements that are not allowed in stored\nroutines, see Stored Routine Limitations.\n\nInvoking stored procedure from within programs\n----------------------------------------------\n\nFor information about invoking stored procedures from within programs written\nin a language that has a MariaDB/MySQL interface, see CALL.\n\nOR REPLACE\n----------\n\nIf the optional OR REPLACE clause is used, it acts as a shortcut for:\n\nDROP PROCEDURE IF EXISTS name;\nCREATE PROCEDURE name ...;\n\nwith the exception that any existing privileges for the procedure are not\ndropped.\n\nsql_mode\n--------\n\nMariaDB stores the sql_mode system variable setting that is in effect at the\ntime a routine is created, and always executes the routine with this setting\nin force, regardless of the server SQL mode in effect when the routine is\ninvoked.\n\nCharacter Sets and Collations\n-----------------------------\n\nProcedure parameters can be declared with any character set/collation. If the\ncharacter set and collation are not specifically set, the database defaults at\nthe time of creation will be used. If the database defaults change at a later\nstage, the stored procedure character set/collation will not be changed at the\nsame time; the stored procedure needs to be dropped and recreated to ensure\nthe same character set/collation as the database is used.\n\nOracle Mode\n-----------\n\nMariaDB starting with 10.3\n--------------------------\nFrom MariaDB 10.3, a subset of Oracle\'s PL/SQL language has been supported in\naddition to the traditional SQL/PSM-based MariaDB syntax. See Oracle mode from\nMariaDB 10.3 for details on changes when running Oracle mode.\n\nExamples\n--------\n\nThe following example shows a simple stored procedure that uses an OUT\nparameter. It uses the DELIMITER command to set a new delimiter for the\nduration of the process — see Delimiters in the mysql client.\n\nDELIMITER //\n\nCREATE PROCEDURE simpleproc (OUT param1 INT)\n BEGIN\n SELECT COUNT(*) INTO param1 FROM t;\n END;\n//\n\nDELIMITER ;\n\nCALL simpleproc(@a);\n\nSELECT @a;\n+------+\n| @a |\n+------+\n| 1 |\n+------+\n\nCharacter set and collation:\n\nDELIMITER //\n\nCREATE PROCEDURE simpleproc2 (\n OUT param1 CHAR(10) CHARACTER SET \'utf8\' COLLATE \'utf8_bin\'\n)\n BEGIN\n SELECT CONCAT(\'a\'),f1 INTO param1 FROM t;\n END;\n//\n\nDELIMITER ;\n\nCREATE OR REPLACE:\n\nDELIMITER //\n\nCREATE PROCEDURE simpleproc2 (\n OUT param1 CHAR(10) CHARACTER SET \'utf8\' COLLATE \'utf8_bin\'\n)\n BEGIN\n SELECT CONCAT(\'a\'),f1 INTO param1 FROM t;\n END;\n//\nERROR 1304 (42000): PROCEDURE simpleproc2 already exists\n\nDELIMITER ;\n\nDELIMITER //\n\nCREATE OR REPLACE PROCEDURE simpleproc2 (\n OUT param1 CHAR(10) CHARACTER SET \'utf8\' COLLATE \'utf8_bin\'\n)\n BEGIN\n SELECT CONCAT(\'a\'),f1 INTO param1 FROM t;\n END;\n//\nERROR 1304 (42000): PROCEDURE simpleproc2 already exists\n\nDELIMITER ;\nQuery OK, 0 rows affected (0.03 sec)\n\nURL: https://mariadb.com/kb/en/create-procedure/','','https://mariadb.com/kb/en/create-procedure/'); @@ -824,8 +824,8 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (716,38,'CREATE TRIGGER','Syntax\n------\n\nCREATE [OR REPLACE]\n [DEFINER = { user | CURRENT_USER | role | CURRENT_ROLE }]\n TRIGGER [IF NOT EXISTS] trigger_name trigger_time trigger_event\n ON tbl_name FOR EACH ROW\n [{ FOLLOWS | PRECEDES } other_trigger_name ]\n trigger_stmt;\n\nDescription\n-----------\n\nThis statement creates a new trigger. A trigger is a named database object\nthat is associated with a table, and that activates when a particular event\noccurs for the table. The trigger becomes associated with the table named\ntbl_name, which must refer to a permanent table. You cannot associate a\ntrigger with a TEMPORARY table or a view.\n\nCREATE TRIGGER requires the TRIGGER privilege for the table associated with\nthe trigger.\n\nMariaDB starting with 10.2.3\n----------------------------\nYou can have multiple triggers for the same trigger_time and trigger_event.\n\nFor valid identifiers to use as trigger names, see Identifier Names.\n\nOR REPLACE\n----------\n\nMariaDB starting with 10.1.4\n----------------------------\nIf used and the trigger already exists, instead of an error being returned,\nthe existing trigger will be dropped and replaced by the newly defined trigger.\n\nDEFINER\n-------\n\nThe DEFINER clause determines the security context to be used when checking\naccess privileges at trigger activation time. Usage requires the SUPER\nprivilege, or, from MariaDB 10.5.2, the SET USER privilege.\n\nIF NOT EXISTS\n-------------\n\nMariaDB starting with 10.1.4\n----------------------------\nIf the IF NOT EXISTS clause is used, the trigger will only be created if a\ntrigger of the same name does not exist. If the trigger already exists, by\ndefault a warning will be returned.\n\ntrigger_time\n------------\n\ntrigger_time is the trigger action time. It can be BEFORE or AFTER to indicate\nthat the trigger activates before or after each row to be modified.\n\ntrigger_event\n-------------\n\ntrigger_event indicates the kind of statement that activates the trigger. The\ntrigger_event can be one of the following:\n\n* INSERT: The trigger is activated whenever a new row is inserted into the\ntable; for example, through INSERT, LOAD DATA, and REPLACE statements.\n* UPDATE: The trigger is activated whenever a row is modified; for example,\nthrough UPDATE statements.\n* DELETE: The trigger is activated whenever a row is deleted from the table;\nfor example, through DELETE and REPLACE statements. However, DROP TABLE and\nTRUNCATE statements on the table do not activate this trigger, because they do\nnot use DELETE. Dropping a partition does not activate DELETE triggers, either.\n\nFOLLOWS/PRECEDES other_trigger_name\n-----------------------------------\n\nMariaDB starting with 10.2.3\n----------------------------\nThe FOLLOWS other_trigger_name and PRECEDES other_trigger_name options were\nadded in MariaDB 10.2.3 as part of supporting multiple triggers per action\ntime. This is the same syntax used by MySQL 5.7, although MySQL 5.7 does not\nhave multi-trigger support.\n\nFOLLOWS adds the new trigger after another trigger while PRECEDES adds the new\ntrigger before another trigger. If neither option is used, the new trigger is\nadded last for the given action and time.\n\nFOLLOWS and PRECEDES are not stored in the trigger definition. However the\ntrigger order is guaranteed to not change over time. mariadb-dump/mysqldump\nand other backup methods will not change trigger order. You can verify the\ntrigger order from the ACTION_ORDER column in INFORMATION_SCHEMA.TRIGGERS\ntable.\n\nSELECT trigger_name, action_order FROM information_schema.triggers \n WHERE event_object_table=\'t1\';\n\nAtomic DDL\n----------\n\nMariaDB starting with 10.6.1\n----------------------------\nMariaDB 10.6.1 supports Atomic DDL and CREATE TRIGGER is atomic.\n\nExamples\n--------\n\nCREATE DEFINER=`root`@`localhost` TRIGGER increment_animal\n AFTER INSERT ON animals FOR EACH ROW\n UPDATE animal_count SET animal_count.animals = animal_count.animals+1;\n\nOR REPLACE and IF NOT EXISTS\n\nCREATE DEFINER=`root`@`localhost` TRIGGER increment_animal\n AFTER INSERT ON animals FOR EACH ROW\n UPDATE animal_count SET animal_count.animals = animal_count.animals+1;\nERROR 1359 (HY000): Trigger already exists\n\nCREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER increment_animal\n AFTER INSERT ON animals FOR EACH ROW\n UPDATE animal_count SET animal_count.animals = animal_count.animals+1;\nQuery OK, 0 rows affected (0.12 sec)\n\nCREATE DEFINER=`root`@`localhost` TRIGGER IF NOT EXISTS increment_animal\n AFTER INSERT ON animals FOR EACH ROW\n UPDATE animal_count SET animal_count.animals = animal_count.animals+1;\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+------------------------+\n| Level | Code | Message |\n+-------+------+------------------------+\n| Note | 1359 | Trigger already exists |\n+-------+------+------------------------+\n1 row in set (0.00 sec)\n\nURL: https://mariadb.com/kb/en/create-trigger/','','https://mariadb.com/kb/en/create-trigger/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (717,38,'CREATE VIEW','Syntax\n------\n\nCREATE\n [OR REPLACE]\n [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]\n [DEFINER = { user | CURRENT_USER | role | CURRENT_ROLE }]\n [SQL SECURITY { DEFINER | INVOKER }]\n VIEW [IF NOT EXISTS] view_name [(column_list)]\n AS select_statement\n [WITH [CASCADED | LOCAL] CHECK OPTION]\n\nDescription\n-----------\n\nThe CREATE VIEW statement creates a new view, or replaces an existing one if\nthe OR REPLACE clause is given. If the view does not exist, CREATE OR REPLACE\nVIEW is the same as CREATE VIEW. If the view does exist, CREATE OR REPLACE\nVIEW is the same as ALTER VIEW.\n\nThe select_statement is a SELECT statement that provides the definition of the\nview. (When you select from the view, you select in effect using the SELECT\nstatement.) select_statement can select from base tables or other views.\n\nThe view definition is \"frozen\" at creation time, so changes to the underlying\ntables afterwards do not affect the view definition. For example, if a view is\ndefined as SELECT * on a table, new columns added to the table later do not\nbecome part of the view. A SHOW CREATE VIEW shows that such queries are\nrewritten and column names are included in the view definition.\n\nThe view definition must be a query that does not return errors at view\ncreation times. However, the base tables used by the views might be altered\nlater and the query may not be valid anymore. In this case, querying the view\nwill result in an error. CHECK TABLE helps in finding this kind of problems.\n\nThe ALGORITHM clause affects how MariaDB processes the view. The DEFINER and\nSQL SECURITY clauses specify the security context to be used when checking\naccess privileges at view invocation time. The WITH CHECK OPTION clause can be\ngiven to constrain inserts or updates to rows in tables referenced by the\nview. These clauses are described later in this section.\n\nThe CREATE VIEW statement requires the CREATE VIEW privilege for the view, and\nsome privilege for each column selected by the SELECT statement. For columns\nused elsewhere in the SELECT statement you must have the SELECT privilege. If\nthe OR REPLACE clause is present, you must also have the DROP privilege for\nthe view.\n\nA view belongs to a database. By default, a new view is created in the default\ndatabase. To create the view explicitly in a given database, specify the name\nas db_name.view_name when you create it.\n\nCREATE VIEW test.v AS SELECT * FROM t;\n\nBase tables and views share the same namespace within a database, so a\ndatabase cannot contain a base table and a view that have the same name.\n\nViews must have unique column names with no duplicates, just like base tables.\nBy default, the names of the columns retrieved by the SELECT statement are\nused for the view column names. To define explicit names for the view columns,\nthe optional column_list clause can be given as a list of comma-separated\nidentifiers. The number of names in column_list must be the same as the number\nof columns retrieved by the SELECT statement.\n\nMySQL until 5.1.28\n------------------\nPrior to MySQL 5.1.29, When you modify an existing view, the current view\ndefinition is backed up and saved. It is stored in that table\'s database\ndirectory, in a subdirectory named arc. The backup file for a view v is named\nv.frm-00001. If you alter the view again, the next backup is named\nv.frm-00002. The three latest view backup definitions are stored. Backed up\nview definitions are not preserved by mysqldump, or any other such programs,\nbut you can retain them using a file copy operation. However, they are not\nneeded for anything but to provide you with a backup of your previous view\ndefinition. It is safe to remove these backup definitions, but only while\nmysqld is not running. If you delete the arc subdirectory or its files while\nmysqld is running, you will receive an error the next time you try to alter\nthe view:\n\nMariaDB [test]> ALTER VIEW v AS SELECT * FROM t; \nERROR 6 (HY000): Error on delete of \'.\\test\\arc/v.frm-0004\' (Errcode: 2)\n\nColumns retrieved by the SELECT statement can be simple references to table\ncolumns. They can also be expressions that use functions, constant values,\noperators, and so forth.\n\nUnqualified table or view names in the SELECT statement are interpreted with\nrespect to the default database. A view can refer to tables or views in other\ndatabases by qualifying the table or view name with the proper database name.\n\nA view can be created from many kinds of SELECT statements. It can refer to\nbase tables or other views. It can use joins, UNION, and subqueries. The\nSELECT need not even refer to any tables. The following example defines a view\nthat selects two columns from another table, as well as an expression\ncalculated from those columns:\n\nCREATE TABLE t (qty INT, price INT);\n\nINSERT INTO t VALUES(3, 50);\n\nCREATE VIEW v AS SELECT qty, price, qty*price AS value FROM t;\n\nSELECT * FROM v;\n+------+-------+-------+\n| qty | price | value |\n+------+-------+-------+\n| 3 | 50 | 150 |\n+------+-------+-------+\n\nA view definition is subject to the following restrictions:\n\n* The SELECT statement cannot contain a subquery in the FROM clause.\n* The SELECT statement cannot refer to system or user variables.\n* Within a stored program, the definition cannot refer to program parameters\nor local variables.\n* The SELECT statement cannot refer to prepared statement parameters.\n* Any table or view referred to in the definition must exist. However, after a\nview has been created, it is possible to drop a table or view that the\ndefinition refers to. In this case, use of the view results in an error. To\ncheck a view definition for problems of this kind, use the CHECK TABLE\nstatement.\n* The definition cannot refer to a TEMPORARY table, and you cannot create a\nTEMPORARY view.\n* Any tables named in the view definition must exist at definition time.\n* You cannot associate a trigger with a view.\n* For valid identifiers to use as view names, see Identifier Names.\n\nORDER BY is allowed in a view definition, but it is ignored if you select from\na view using a statement that has its own ORDER BY.\n\nFor other options or clauses in the definition, they are added to the options\nor clauses of the statement that references the view, but the effect is\nundefined. For example, if a view definition includes a LIMIT clause, and you\nselect from the view using a statement that has its own LIMIT clause, it is\nundefined which limit applies. This same principle applies to options such as\nALL, DISTINCT, or SQL_SMALL_RESULT that follow the SELECT keyword, and to\nclauses such as INTO, FOR UPDATE, and LOCK IN SHARE MODE.\n\nThe PROCEDURE clause cannot be used in a view definition, and it cannot be\nused if a view is referenced in the FROM clause.\n\nIf you create a view and then change the query processing environment by\nchanging system variables, that may affect the results that you get from the\nview:\n\nCREATE VIEW v (mycol) AS SELECT \'abc\';\n\nSET sql_mode = \'\';\n\nSELECT \"mycol\" FROM v;\n+-------+\n| mycol |\n+-------+\n| mycol | \n+-------+\n\nSET sql_mode = \'ANSI_QUOTES\';\n\nSELECT \"mycol\" FROM v;\n+-------+\n| mycol |\n+-------+\n| abc | \n+-------+\n\nThe DEFINER and SQL SECURITY clauses determine which MariaDB account to use\nwhen checking access privileges for the view when a statement is executed that\nreferences the view. They were added in MySQL 5.1.2. The legal SQL SECURITY\ncharacteristic values are DEFINER and INVOKER. These indicate that the\nrequired privileges must be held by the user who defined or invoked the view,\nrespectively. The default SQL SECURITY value is DEFINER.\n\nIf a user value is given for the DEFINER clause, it should be a MariaDB\naccount in \'user_name\'@\'host_name\' format (the same format used in the GRANT\nstatement). The user_name and host_name values both are required. The definer\ncan also be given as CURRENT_USER or CURRENT_USER(). The default DEFINER value\nis the user who executes the CREATE VIEW statement. This is the same as\nspecifying DEFINER = CURRENT_USER explicitly.\n\nIf you specify the DEFINER clause, these rules determine the legal DEFINER\nuser values:\n\n* If you do not have the SUPER privilege, or, from MariaDB 10.5.2, the SET\nUSER privilege, the only legal user value is your own account, either\nspecified literally or by using CURRENT_USER. You cannot set the definer to\nsome other account.\n* If you have the SUPER privilege, or, from MariaDB 10.5.2, the SET USER\nprivilege, you can specify any syntactically legal account name. If the\naccount does not actually exist, a warning is generated.\n* If the SQL SECURITY value is DEFINER but the definer account does not exist\nwhen the view is referenced, an error occurs.\n\nWithin a view definition, CURRENT_USER returns the view\'s DEFINER value by\ndefault. For views defined with the SQL SECURITY INVOKER characteristic,\nCURRENT_USER returns the account for the view\'s invoker. For information about\nuser auditing within views, see\nhttp://dev.mysql.com/doc/refman/5.1/en/account-activity-auditing.html.\n\nWithin a stored routine that is defined with the SQL SECURITY DEFINER\ncharacteristic, CURRENT_USER returns the routine\'s DEFINER value. This also\naffects a view defined within such a program, if the view definition contains\na DEFINER value of CURRENT_USER.\n\nView privileges are checked like this:\n\n* At view definition time, the view creator must have the privileges needed to\nuse the top-level objects accessed by the view. For example, if the view\ndefinition refers to table columns, the creator must have privileges for the\ncolumns, as described previously. If the definition refers to a stored\nfunction, only the privileges needed to invoke the function can be checked.\nThe privileges required when the function runs can be checked only as it\nexecutes: For different invocations of the function, different execution paths\nwithin the function might be taken.\n* When a view is referenced, privileges for objects accessed by the view are\nchecked against the privileges held by the view creator or invoker, depending\non whether the SQL SECURITY characteristic is DEFINER or INVOKER, respectively.\n* If reference to a view causes execution of a stored function, privilege\nchecking for statements executed within the function depend on whether the\nfunction is defined with a SQL SECURITY characteristic of DEFINER or INVOKER.\nIf the security characteristic is DEFINER, the function runs with the\nprivileges of its creator. If the characteristic is INVOKER, the function runs\nwith the privileges determined by the view\'s SQL SECURITY characteristic.\n\nExample: A view might depend on a stored function, and that function might\ninvoke other stored routines. For example, the following view invokes a stored\nfunction f():\n\nCREATE VIEW v AS SELECT * FROM t WHERE t.id = f(t.name);\n\nSuppose that f() contains a statement such as this:\n\nIF name IS NULL then\n CALL p1();\nELSE\n CALL p2();\nEND IF;\n\nThe privileges required for executing statements within f() need to be checked\nwhen f() executes. This might mean that privileges are needed for p1() or\np2(), depending on the execution path within f(). Those privileges must be\nchecked at runtime, and the user who must possess the privileges is determined\nby the SQL SECURITY values of the view v and the function f().\n\nThe DEFINER and SQL SECURITY clauses for views are extensions to standard SQL.\nIn standard SQL, views are handled using the rules for SQL SECURITY INVOKER.\n\nIf you invoke a view that was created before MySQL 5.1.2, it is treated as\nthough it was created with a SQL SECURITY DEFINER clause and with a DEFINER\nvalue that is the same as your account. However, because the actual definer is\nunknown, MySQL issues a warning. To make the warning go away, it is sufficient\nto re-create the view so that the view definition includes a DEFINER clause.\n\nThe optional ALGORITHM clause is an extension to standard SQL. It affects how\nMariaDB processes the view. ALGORITHM takes three values: MERGE, TEMPTABLE, or\nUNDEFINED. The default algorithm is UNDEFINED if no ALGORITHM clause is\npresent. See View Algorithms for more information.\n\nSome views are updatable. That is, you can use them in statements such as\nUPDATE, DELETE, or INSERT to update the contents of the underlying table. For\na view to be updatable, there must be a one-to-one relationship between the\nrows in the view and the rows in the underlying table. There are also certain\nother constructs that make a view non-updatable. See Inserting and Updating\nwith Views.\n\nWITH CHECK OPTION\n-----------------\n\nThe WITH CHECK OPTION clause can be given for an updatable view to prevent\ninserts or updates to rows except those for which the WHERE clause in the\nselect_statement is true.\n\nIn a WITH CHECK OPTION clause for an updatable view, the LOCAL and CASCADED\nkeywords determine the scope of check testing when the view is defined in\nterms of another view. The LOCAL keyword restricts the CHECK OPTION only to\nthe view being defined. CASCADED causes the checks for underlying views to be\nevaluated as well. When neither keyword is given, the default is CASCADED.\n\nFor more information about updatable views and the WITH CHECK OPTION clause,\nsee Inserting and Updating with Views.\n\nIF NOT EXISTS\n-------------\n\nMariaDB starting with 10.1.3\n----------------------------\nThe IF NOT EXISTS clause was added in MariaDB 10.1.3\n\nWhen the IF NOT EXISTS clause is used, MariaDB will return a warning instead\nof an error if the specified view already exists. Cannot be used together with\nthe OR REPLACE clause.\n\nAtomic DDL\n----------\n\nMariaDB starting with 10.6.1\n----------------------------\nMariaDB 10.6.1 supports Atomic DDL and CREATE VIEW is atomic.\n\nExamples\n--------\n\nCREATE TABLE t (a INT, b INT) ENGINE = InnoDB;\n\nINSERT INTO t VALUES (1,1), (2,2), (3,3);\n\nCREATE VIEW v AS SELECT a, a*2 AS a2 FROM t;\n\nSELECT * FROM v;\n+------+------+\n| a | a2 |\n+------+------+\n| 1 | 2 |\n| 2 | 4 |\n| 3 | 6 |\n+------+------+\n\nOR REPLACE and IF NOT EXISTS:\n\nCREATE VIEW v AS SELECT a, a*2 AS a2 FROM t;\nERROR 1050 (42S01): Table \'v\' already exists\n\nCREATE OR REPLACE VIEW v AS SELECT a, a*2 AS a2 FROM t;\nQuery OK, 0 rows affected (0.04 sec)\n\nCREATE VIEW IF NOT EXISTS v AS SELECT a, a*2 AS a2 FROM t;\nQuery OK, 0 rows affected, 1 warning (0.01 sec)\n','','https://mariadb.com/kb/en/create-view/'); update help_topic set description = CONCAT(description, '\nSHOW WARNINGS;\n+-------+------+--------------------------+\n| Level | Code | Message |\n+-------+------+--------------------------+\n| Note | 1050 | Table \'v\' already exists |\n+-------+------+--------------------------+\n\nURL: https://mariadb.com/kb/en/create-view/') WHERE help_topic_id = 717; -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (718,38,'Generated (Virtual and Persistent/Stored) Columns','Syntax\n------\n\n [GENERATED ALWAYS] AS ( )\n[VIRTUAL | PERSISTENT | STORED] [UNIQUE] [UNIQUE KEY] [COMMENT ]\n\nMariaDB\'s generated columns syntax is designed to be similar to the syntax for\nMicrosoft SQL Server\'s computed columns and Oracle Database\'s virtual columns.\nIn MariaDB 10.2 and later, the syntax is also compatible with the syntax for\nMySQL\'s generated columns.\n\nDescription\n-----------\n\nA generated column is a column in a table that cannot explicitly be set to a\nspecific value in a DML query. Instead, its value is automatically generated\nbased on an expression. This expression might generate the value based on the\nvalues of other columns in the table, or it might generate the value by\ncalling built-in functions or user-defined functions (UDFs).\n\nThere are two types of generated columns:\n\n* PERSISTENT (a.k.a. STORED): This type\'s value is actually stored in the\ntable.\n* VIRTUAL: This type\'s value is not stored at all. Instead, the value is\ngenerated dynamically when the table is queried. This type is the default.\n\nGenerated columns are also sometimes called computed columns or virtual\ncolumns.\n\nSupported Features\n------------------\n\nStorage Engine Support\n----------------------\n\n* Generated columns can only be used with storage engines which support them.\nIf you try to use a storage engine that does not support them, then you will\nsee an error similar to the following:\n\nERROR 1910 (HY000): TokuDB storage engine does not support computed columns\n\n* InnoDB, Aria, MyISAM and CONNECT support generated columns.\n\n* A column in a MERGE table can be built on a PERSISTENT generated column.\nHowever, a column in a MERGE table can not be defined as a VIRTUAL and\nPERSISTENT generated column.\n\nData Type Support\n-----------------\n\n* All data types are supported when defining generated columns.\n\n* Using the ZEROFILL column option is supported when defining generated\ncolumns.\n\nMariaDB starting with 10.2.6\n----------------------------\nIn MariaDB 10.2.6 and later, the following statements apply to data types for\ngenerated columns:\n\n* Using the AUTO_INCREMENT column option is not supported when defining\ngenerated columns. Previously, it was supported, but this support was removed,\nbecause it would not work correctly. See MDEV-11117.\n\nIndex Support\n-------------\n\n* Using a generated column as a table\'s primary key is not supported. See\nMDEV-5590 for more information. If you try to use one as a primary key, then\nyou will see an error similar to the following:\n\nERROR 1903 (HY000): Primary key cannot be defined upon a computed column\n\n* Using PERSISTENT generated columns as part of a foreign key is supported.\n\n* Referencing PERSISTENT generated columns as part of a foreign key is also\nsupported.\nHowever, using the ON UPDATE CASCADE, ON UPDATE SET NULL, or ON DELETE SET\nNULL clauses is not supported. If you try to use an unsupported clause, then\nyou will see an error similar to the following:\n\nERROR 1905 (HY000): Cannot define foreign key with ON UPDATE SET NULL clause\non a computed column\n\nMariaDB starting with 10.2.3\n----------------------------\nIn MariaDB 10.2.3 and later, the following statements apply to indexes for\ngenerated columns:\n\n* Defining indexes on both VIRTUAL and PERSISTENT generated columns is\nsupported.\nIf an index is defined on a generated column, then the optimizer considers\nusing it in the same way as indexes based on \"real\" columns.\n\nMariaDB until 10.2.2\n--------------------\nIn MariaDB 10.2.2 and before, the following statements apply to indexes for\ngenerated columns:\n\n* Defining indexes on VIRTUAL generated columns is not supported.\n\n* Defining indexes on PERSISTENT generated columns is supported.\nIf an index is defined on a generated column, then the optimizer considers\nusing it in the same way as indexes based on \"real\" columns.\n\nStatement Support\n-----------------\n\n* Generated columns are used in DML queries just as if they were \"real\"\ncolumns.\nHowever, VIRTUAL and PERSISTENT generated columns differ in how their data is\nstored.\nValues for PERSISTENT generated columns are generated whenever a DML queries\ninserts or updates the row with the special DEFAULT value. This generates the\ncolumns value, and it is stored in the table like the other \"real\" columns.\nThis value can be read by other DML queries just like the other \"real\" columns.\nValues for VIRTUAL generated columns are not stored in the table. Instead, the\nvalue is generated dynamically whenever the column is queried. If other\ncolumns in a row are queried, but the VIRTUAL generated column is not one of\nthe queried columns, then the column\'s value is not generated.\n\n* The SELECT statement supports generated columns.\n\n* Generated columns can be referenced in the INSERT, UPDATE, and DELETE\nstatements.\nHowever, VIRTUAL or PERSISTENT generated columns cannot be explicitly set to\nany other values than NULL or DEFAULT. If a generated column is explicitly set\nto any other value, then the outcome depends on whether strict mode is enabled\nin sql_mode. If it is not enabled, then a warning will be raised and the\ndefault generated value will be used instead. If it is enabled, then an error\nwill be raised instead.\n\n* The CREATE TABLE statement has limited support for generated columns.\nIt supports defining generated columns in a new table.\nIt supports using generated columns to partition tables.\nIt does not support using the versioning clauses with generated columns.\n\n* The ALTER TABLE statement has limited support for generated columns.\nIt supports the MODIFY and CHANGE clauses for PERSISTENT generated columns.\nIt does not support the MODIFY clause for VIRTUAL generated columns if\nALGORITHM is not set to COPY. See MDEV-15476 for more information.\nIt does not support the CHANGE clause for VIRTUAL generated columns if\nALGORITHM is not set to COPY. See MDEV-17035 for more information.\nIt does not support altering a table if ALGORITHM is not set to COPY if the\ntable has a VIRTUAL generated column that is indexed. See MDEV-14046 for more\ninformation.\nIt does not support adding a VIRTUAL generated column with the ADD clause if\nthe same statement is also adding other columns if ALGORITHM is not set to\nCOPY. See MDEV-17468 for more information.\nIt also does not support altering an existing column into a VIRTUAL generated\ncolumn.\nIt supports using generated columns to partition tables.\nIt does not support using the versioning clauses with generated columns.\n\n* The SHOW CREATE TABLE statement supports generated columns.\n\n* The DESCRIBE statement can be used to check whether a table has generated\ncolumns.\nYou can tell which columns are generated by looking for the ones where the\nExtra column is set to either VIRTUAL or PERSISTENT. For example:\n\nDESCRIBE table1;\n+-------+-------------+------+-----+---------+------------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+-------------+------+-----+---------+------------+\n| a | int(11) | NO | | NULL | |\n| b | varchar(32) | YES | | NULL | |\n| c | int(11) | YES | | NULL | VIRTUAL |\n| d | varchar(5) | YES | | NULL | PERSISTENT |\n+-------+-------------+------+-----+---------+------------+\n\n* Generated columns can be properly referenced in the NEW and OLD rows in\ntriggers.\n\n* Stored procedures support generated columns.\n\n* The HANDLER statement supports generated columns.\n\nExpression Support\n------------------\n\n* Most legal, deterministic expressions which can be calculated are supported\nin expressions for generated columns.\n\n* Most built-in functions are supported in expressions for generated columns.\nHowever, some built-in functions can\'t be supported for technical reasons. For\nexample, If you try to use an unsupported function in an expression, an error\nis generated similar to the following:\n\nERROR 1901 (HY000): Function or expression \'dayname()\' cannot be used in the\nGENERATED ALWAYS AS clause of `v`\n\n* Subqueries are not supported in expressions for generated columns because\nthe underlying data can change.\n\n* Using anything that depends on data outside the row is not supported in\nexpressions for generated columns.\n\n* Stored functions are not supported in expressions for generated columns. See\nMDEV-17587 for more information.\n\nMariaDB starting with 10.2.1\n----------------------------\nIn MariaDB 10.2.1 and later, the following statements apply to expressions for\ngenerated columns:\n\n* Non-deterministic built-in functions are supported in expressions for not\nindexed VIRTUAL generated columns.\n\n* Non-deterministic built-in functions are not supported in expressions for\nPERSISTENT or indexed VIRTUAL generated columns.\n\n* User-defined functions (UDFs) are supported in expressions for generated\ncolumns.\nHowever, MariaDB can\'t check whether a UDF is deterministic, so it is up to\nthe user to be sure that they do not use non-deterministic UDFs with VIRTUAL\ngenerated columns.\n\n* Defining a generated column based on other generated columns defined before\nit in the table definition is supported. For example:\n\nCREATE TABLE t1 (a int as (1), b int as (a));\n\n* However, defining a generated column based on other generated columns\ndefined after in the table definition is not supported in expressions for\ngeneration columns because generated columns are calculated in the order they\nare defined.\n\n* Using an expression that exceeds 255 characters in length is supported in\nexpressions for generated columns. The new limit for the entire table\ndefinition, including all expressions for generated columns, is 65,535 bytes.\n\n* Using constant expressions is supported in expressions for generated\ncolumns. For example:\n\nCREATE TABLE t1 (a int as (1));\n\nMariaDB until 10.2.0\n--------------------\nIn MariaDB 10.2.0 and before, the following statements apply to expressions\nfor generated columns:\n\n* Non-deterministic built-in functions are not supported in expressions for\ngenerated columns.\n\n* User-defined functions (UDFs) are not supported in expressions for generated\ncolumns.\n\n* Defining a generated column based on other generated columns defined in the\ntable is not supported. Otherwise, it would generate errors like this:\n\nERROR 1900 (HY000): A computed column cannot be based on a computed column\n\n* Using an expression that exceeds 255 characters in length is not supported\nin expressions for generated columns.\n\n* Using constant expressions is not supported in expressions for generated\ncolumns. Otherwise, it would generate errors like this:\n\nERROR 1908 (HY000): Constant expression in computed column function is not\nallowed\n\nMaking Stored Values Consistent\n-------------------------------\n\nWhen a generated column is PERSISTENT or indexed, the value of the expression\nneeds to be consistent regardless of the SQL Mode flags in the current\nsession. If it is not, then the table will be seen as corrupted when the value\nthat should actually be returned by the computed expression and the value that\nwas previously stored and/or indexed using a different sql_mode setting\ndisagree.\n\nThere are currently two affected classes of inconsistencies: character padding\nand unsigned subtraction:\n\n* For a VARCHAR or TEXT generated column the length of the value returned can\nvary depending on the PAD_CHAR_TO_FULL_LENGTH sql_mode flag. To make the\nvalue consistent, create the generated column using an RTRIM() or RPAD()\nfunction. Alternately, create the generated column as a CHAR column so that\nits data is always fully padded.\n\n* If a SIGNED generated column is based on the subtraction of an UNSIGNED\nvalue, the resulting value can vary depending on how large the value is and\nthe NO_UNSIGNED_SUBTRACTION sql_mode flag. To make the value consistent, use\nCAST() to ensure that each UNSIGNED operand is SIGNED before the subtraction.\n\nMariaDB starting with 10.5\n--------------------------\nBeginning in MariaDB 10.5, there is a fatal error generated when trying to\ncreate a generated column whose value can change depending on the SQL Mode\nwhen its data is PERSISTENT or indexed.\n\nFor an existing generated column that has a potentially inconsistent value, a\nwarning about a bad expression is generated the first time it is used (if\nwarnings are enabled).\n\nBeginning in MariaDB 10.4.8, MariaDB 10.3.18, and MariaDB 10.2.27 a\npotentially inconsistent generated column outputs a warning when created or\nfirst used (without restricting their creation).\n\nHere is an example of two tables that would be rejected in MariaDB 10.5 and\nwarned about in the other listed versions:\n\nCREATE TABLE bad_pad (\n txt CHAR(5),\n -- CHAR -> VARCHAR or CHAR -> TEXT can\'t be persistent or indexed:\n vtxt VARCHAR(5) AS (txt) PERSISTENT\n);\n\nCREATE TABLE bad_sub (\n num1 BIGINT UNSIGNED,\n num2 BIGINT UNSIGNED,\n -- The resulting value can vary for some large values\n vnum BIGINT AS (num1 - num2) VIRTUAL,\n KEY(vnum)\n);\n\nThe warnings for the above tables look like this:\n\nWarning (Code 1901): Function or expression \'`txt`\' cannot be used in the\nGENERATED ALWAYS AS clause of `vtxt`\nWarning (Code 1105): Expression depends on the @@sql_mode value\nPAD_CHAR_TO_FULL_LENGTH\n\nWarning (Code 1901): Function or expression \'`num1` - `num2`\' cannot be used\nin the GENERATED ALWAYS AS clause of `vnum`\nWarning (Code 1105): Expression depends on the @@sql_mode value\nNO_UNSIGNED_SUBTRACTION\n\nTo work around the issue, force the padding or type to make the generated\ncolumn\'s expression return a consistent value. For example:\n\nCREATE TABLE good_pad (\n txt CHAR(5),\n -- Using RTRIM() or RPAD() makes the value consistent:\n vtxt VARCHAR(5) AS (RTRIM(txt)) PERSISTENT,\n -- When not persistent or indexed, it is OK for the value to vary by mode:\n vtxt2 VARCHAR(5) AS (txt) VIRTUAL,\n -- CHAR -> CHAR is always OK:\n txt2 CHAR(5) AS (txt) PERSISTENT\n);\n\nCREATE TABLE good_sub (\n num1 BIGINT UNSIGNED,\n num2 BIGINT UNSIGNED,\n -- The indexed value will always be consistent in this expression:\n vnum BIGINT AS (CAST(num1 AS SIGNED) - CAST(num2 AS SIGNED)) VIRTUAL,\n KEY(vnum)\n);\n\nMySQL Compatibility Support\n---------------------------\n\nMariaDB starting with 10.2.1\n----------------------------\nIn MariaDB 10.2.1 and later, the following statements apply to MySQL','','https://mariadb.com/kb/en/generated-columns/'); -update help_topic set description = CONCAT(description, '\ncompatibility for generated columns:\n\n* The STORED keyword is supported as an alias for the PERSISTENT keyword.\n\n* Tables created with MySQL 5.7 or later that contain MySQL\'s generated\ncolumns can be imported into MariaDB without a dump and restore.\n\nMariaDB until 10.2.0\n--------------------\nIn MariaDB 10.2.0 and before, the following statements apply to MySQL\ncompatibility for generated columns:\n\n* The STORED keyword is not supported as an alias for the PERSISTENT keyword.\n\n* Tables created with MySQL 5.7 or later that contain MySQL\'s generated\ncolumns can not be imported into MariaDB without a dump and restore.\n\nImplementation Differences\n--------------------------\n\nGenerated columns are subject to various constraints in other DBMSs that are\nnot present in MariaDB\'s implementation. Generated columns may also be called\ncomputed columns or virtual columns in different implementations. The various\ndetails for a specific implementation can be found in the documentation for\neach specific DBMS.\n\nImplementation Differences Compared to Microsoft SQL Server\n-----------------------------------------------------------\n\nMariaDB\'s generated columns implementation does not enforce the following\nrestrictions that are present in Microsoft SQL Server\'s computed columns\nimplementation:\n\n* MariaDB allows server variables in generated column expressions, including\nthose that change dynamically, such as warning_count.\n* MariaDB allows the CONVERT_TZ() function to be called with a named time zone\nas an argument, even though time zone names and time offsets are configurable.\n* MariaDB allows the CAST() function to be used with non-unicode character\nsets, even though character sets are configurable and differ between\nbinaries/versions.\n* MariaDB allows FLOAT expressions to be used in generated columns. Microsoft\nSQL Server considers these expressions to be \"imprecise\" due to potential\ncross-platform differences in floating-point implementations and precision.\n* Microsoft SQL Server requires the ARITHABORT mode to be set, so that\ndivision by zero returns an error, and not a NULL.\n* Microsoft SQL Server requires QUOTED_IDENTIFIER to be set in sql_mode. In\nMariaDB, if data is inserted without ANSI_QUOTES set in sql_mode, then it will\nbe processed and stored differently in a generated column that contains quoted\nidentifiers.\n* In MariaDB 10.2.0 and before, it does not allow user-defined functions\n(UDFs) to be used in expressions for generated columns.\n\nMicrosoft SQL Server enforces the above restrictions by doing one of the\nfollowing things:\n\n* Refusing to create computed columns.\n* Refusing to allow updates to a table containing them.\n* Refusing to use an index over such a column if it can not be guaranteed that\nthe expression is fully deterministic.\n\nIn MariaDB, as long as the sql_mode, language, and other settings that were in\neffect during the CREATE TABLE remain unchanged, the generated column\nexpression will always be evaluated the same. If any of these things change,\nthen please be aware that the generated column expression might not be\nevaluated the same way as it previously was.\n\nIn MariaDB 5.2, you will get a warning if you try to update a virtual column.\nIn MariaDB 5.3 and later, this warning will be converted to an error if strict\nmode is enabled in sql_mode.\n\nDevelopment History\n-------------------\n\nGenerated columns was originally developed by Andrey Zhakov. It was then\nmodified by Sanja Byelkin and Igor Babaev at Monty Program for inclusion in\nMariaDB. Monty did the work on MariaDB 10.2 to lift a some of the old\nlimitations.\n\nExamples\n--------\n\nHere is an example table that uses both VIRTUAL and PERSISTENT virtual columns:\n\nUSE TEST;\n\nCREATE TABLE table1 (\n a INT NOT NULL,\n b VARCHAR(32),\n c INT AS (a mod 10) VIRTUAL,\n d VARCHAR(5) AS (left(b,5)) PERSISTENT);\n\nIf you describe the table, you can easily see which columns are virtual by\nlooking in the \"Extra\" column:\n\nDESCRIBE table1;\n+-------+-------------+------+-----+---------+------------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+-------------+------+-----+---------+------------+\n| a | int(11) | NO | | NULL | |\n| b | varchar(32) | YES | | NULL | |\n| c | int(11) | YES | | NULL | VIRTUAL |\n| d | varchar(5) | YES | | NULL | PERSISTENT |\n+-------+-------------+------+-----+---------+------------+\n\nTo find out what function(s) generate the value of the virtual column you can\nuse SHOW CREATE TABLE:\n\nSHOW CREATE TABLE table1;\n\n| table1 | CREATE TABLE `table1` (\n `a` int(11) NOT NULL,\n `b` varchar(32) DEFAULT NULL,\n `c` int(11) AS (a mod 10) VIRTUAL,\n `d` varchar(5) AS (left(b,5)) PERSISTENT\n) ENGINE=MyISAM DEFAULT CHARSET=latin1 |\n\nIf you try to insert non-default values into a virtual column, you will\nreceive a warning and what you tried to insert will be ignored and the derived\nvalue inserted instead:\n\nWARNINGS;\nShow warnings enabled.\n\nINSERT INTO table1 VALUES (1, \'some text\',default,default);\nQuery OK, 1 row affected (0.00 sec)\n\nINSERT INTO table1 VALUES (2, \'more text\',5,default);\nQuery OK, 1 row affected, 1 warning (0.00 sec)\n\nWarning (Code 1645): The value specified for computed column \'c\' in table\n\'table1\' has been ignored.\n\nINSERT INTO table1 VALUES (123, \'even more text\',default,\'something\');\nQuery OK, 1 row affected, 2 warnings (0.00 sec)\n\nWarning (Code 1645): The value specified for computed column \'d\' in table\n\'table1\' has been ignored.\nWarning (Code 1265): Data truncated for column \'d\' at row 1\n\nSELECT * FROM table1;\n+-----+----------------+------+-------+\n| a | b | c | d |\n+-----+----------------+------+-------+\n| 1 | some text | 1 | some |\n| 2 | more text | 2 | more |\n| 123 | even more text | 3 | even |\n+-----+----------------+------+-------+\n3 rows in set (0.00 sec)\n\nIf the ZEROFILL clause is specified, it should be placed directly after the\ntype definition, before the AS ():\n\nCREATE TABLE table2 (a INT, b INT ZEROFILL AS (a*2) VIRTUAL);\nINSERT INTO table2 (a) VALUES (1);\n\nSELECT * FROM table2;\n+------+------------+\n| a | b |\n+------+------------+\n| 1 | 0000000002 |\n+------+------------+\n1 row in set (0.00 sec)\n\nYou can also use virtual columns to implement a \"poor man\'s partial index\".\nSee example at the end of Unique Index.\n\nURL: https://mariadb.com/kb/en/generated-columns/') WHERE help_topic_id = 718; +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (718,38,'Generated (Virtual and Persistent/Stored) Columns','Syntax\n------\n\n [GENERATED ALWAYS] AS ( )\n[VIRTUAL | PERSISTENT | STORED] [UNIQUE] [UNIQUE KEY] [COMMENT ]\n\nMariaDB\'s generated columns syntax is designed to be similar to the syntax for\nMicrosoft SQL Server\'s computed columns and Oracle Database\'s virtual columns.\nIn MariaDB 10.2 and later, the syntax is also compatible with the syntax for\nMySQL\'s generated columns.\n\nDescription\n-----------\n\nA generated column is a column in a table that cannot explicitly be set to a\nspecific value in a DML query. Instead, its value is automatically generated\nbased on an expression. This expression might generate the value based on the\nvalues of other columns in the table, or it might generate the value by\ncalling built-in functions or user-defined functions (UDFs).\n\nThere are two types of generated columns:\n\n* PERSISTENT (a.k.a. STORED): This type\'s value is actually stored in the\ntable.\n* VIRTUAL: This type\'s value is not stored at all. Instead, the value is\ngenerated dynamically when the table is queried. This type is the default.\n\nGenerated columns are also sometimes called computed columns or virtual\ncolumns.\n\nSupported Features\n------------------\n\nStorage Engine Support\n----------------------\n\n* Generated columns can only be used with storage engines which support them.\nIf you try to use a storage engine that does not support them, then you will\nsee an error similar to the following:\n\nERROR 1910 (HY000): TokuDB storage engine does not support computed columns\n\n* InnoDB, Aria, MyISAM and CONNECT support generated columns.\n\n* A column in a MERGE table can be built on a PERSISTENT generated column.\nHowever, a column in a MERGE table can not be defined as a VIRTUAL and\nPERSISTENT generated column.\n\nData Type Support\n-----------------\n\n* All data types are supported when defining generated columns.\n\n* Using the ZEROFILL column option is supported when defining generated\ncolumns.\n\n* Using the AUTO_INCREMENT column option is not supported when defining\ngenerated columns. Until MariaDB 10.2.25, it was supported, but this support\nwas removed, because it would not work correctly. See MDEV-11117.\n\nIndex Support\n-------------\n\n* Using a generated column as a table\'s primary key is not supported. See\nMDEV-5590 for more information. If you try to use one as a primary key, then\nyou will see an error similar to the following:\n\nERROR 1903 (HY000): Primary key cannot be defined upon a computed column\n\n* Using PERSISTENT generated columns as part of a foreign key is supported.\n\n* Referencing PERSISTENT generated columns as part of a foreign key is also\nsupported.\nHowever, using the ON UPDATE CASCADE, ON UPDATE SET NULL, or ON DELETE SET\nNULL clauses is not supported. If you try to use an unsupported clause, then\nyou will see an error similar to the following:\n\nERROR 1905 (HY000): Cannot define foreign key with ON UPDATE SET NULL clause\non a computed column\n\n* Defining indexes on both VIRTUAL and PERSISTENT generated columns is\nsupported.\nIf an index is defined on a generated column, then the optimizer considers\nusing it in the same way as indexes based on \"real\" columns.\n\nStatement Support\n-----------------\n\n* Generated columns are used in DML queries just as if they were \"real\"\ncolumns.\nHowever, VIRTUAL and PERSISTENT generated columns differ in how their data is\nstored.\nValues for PERSISTENT generated columns are generated whenever a DML queries\ninserts or updates the row with the special DEFAULT value. This generates the\ncolumns value, and it is stored in the table like the other \"real\" columns.\nThis value can be read by other DML queries just like the other \"real\" columns.\nValues for VIRTUAL generated columns are not stored in the table. Instead, the\nvalue is generated dynamically whenever the column is queried. If other\ncolumns in a row are queried, but the VIRTUAL generated column is not one of\nthe queried columns, then the column\'s value is not generated.\n\n* The SELECT statement supports generated columns.\n\n* Generated columns can be referenced in the INSERT, UPDATE, and DELETE\nstatements.\nHowever, VIRTUAL or PERSISTENT generated columns cannot be explicitly set to\nany other values than NULL or DEFAULT. If a generated column is explicitly set\nto any other value, then the outcome depends on whether strict mode is enabled\nin sql_mode. If it is not enabled, then a warning will be raised and the\ndefault generated value will be used instead. If it is enabled, then an error\nwill be raised instead.\n\n* The CREATE TABLE statement has limited support for generated columns.\nIt supports defining generated columns in a new table.\nIt supports using generated columns to partition tables.\nIt does not support using the versioning clauses with generated columns.\n\n* The ALTER TABLE statement has limited support for generated columns.\nIt supports the MODIFY and CHANGE clauses for PERSISTENT generated columns.\nIt does not support the MODIFY clause for VIRTUAL generated columns if\nALGORITHM is not set to COPY. See MDEV-15476 for more information.\nIt does not support the CHANGE clause for VIRTUAL generated columns if\nALGORITHM is not set to COPY. See MDEV-17035 for more information.\nIt does not support altering a table if ALGORITHM is not set to COPY if the\ntable has a VIRTUAL generated column that is indexed. See MDEV-14046 for more\ninformation.\nIt does not support adding a VIRTUAL generated column with the ADD clause if\nthe same statement is also adding other columns if ALGORITHM is not set to\nCOPY. See MDEV-17468 for more information.\nIt also does not support altering an existing column into a VIRTUAL generated\ncolumn.\nIt supports using generated columns to partition tables.\nIt does not support using the versioning clauses with generated columns.\n\n* The SHOW CREATE TABLE statement supports generated columns.\n\n* The DESCRIBE statement can be used to check whether a table has generated\ncolumns.\nYou can tell which columns are generated by looking for the ones where the\nExtra column is set to either VIRTUAL or PERSISTENT. For example:\n\nDESCRIBE table1;\n+-------+-------------+------+-----+---------+------------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+-------------+------+-----+---------+------------+\n| a | int(11) | NO | | NULL | |\n| b | varchar(32) | YES | | NULL | |\n| c | int(11) | YES | | NULL | VIRTUAL |\n| d | varchar(5) | YES | | NULL | PERSISTENT |\n+-------+-------------+------+-----+---------+------------+\n\n* Generated columns can be properly referenced in the NEW and OLD rows in\ntriggers.\n\n* Stored procedures support generated columns.\n\n* The HANDLER statement supports generated columns.\n\nExpression Support\n------------------\n\n* Most legal, deterministic expressions which can be calculated are supported\nin expressions for generated columns.\n\n* Most built-in functions are supported in expressions for generated columns.\nHowever, some built-in functions can\'t be supported for technical reasons. For\nexample, If you try to use an unsupported function in an expression, an error\nis generated similar to the following:\n\nERROR 1901 (HY000): Function or expression \'dayname()\' cannot be used in the\nGENERATED ALWAYS AS clause of `v`\n\n* Subqueries are not supported in expressions for generated columns because\nthe underlying data can change.\n\n* Using anything that depends on data outside the row is not supported in\nexpressions for generated columns.\n\n* Stored functions are not supported in expressions for generated columns. See\nMDEV-17587 for more information.\n\n* Non-deterministic built-in functions are supported in expressions for not\nindexed VIRTUAL generated columns.\n\n* Non-deterministic built-in functions are not supported in expressions for\nPERSISTENT or indexed VIRTUAL generated columns.\n\n* User-defined functions (UDFs) are supported in expressions for generated\ncolumns.\nHowever, MariaDB can\'t check whether a UDF is deterministic, so it is up to\nthe user to be sure that they do not use non-deterministic UDFs with VIRTUAL\ngenerated columns.\n\n* Defining a generated column based on other generated columns defined before\nit in the table definition is supported. For example:\n\nCREATE TABLE t1 (a int as (1), b int as (a));\n\n* However, defining a generated column based on other generated columns\ndefined after in the table definition is not supported in expressions for\ngeneration columns because generated columns are calculated in the order they\nare defined.\n\n* Using an expression that exceeds 255 characters in length is supported in\nexpressions for generated columns. The new limit for the entire table\ndefinition, including all expressions for generated columns, is 65,535 bytes.\n\n* Using constant expressions is supported in expressions for generated\ncolumns. For example:\n\nCREATE TABLE t1 (a int as (1));\n\nMaking Stored Values Consistent\n-------------------------------\n\nWhen a generated column is PERSISTENT or indexed, the value of the expression\nneeds to be consistent regardless of the SQL Mode flags in the current\nsession. If it is not, then the table will be seen as corrupted when the value\nthat should actually be returned by the computed expression and the value that\nwas previously stored and/or indexed using a different sql_mode setting\ndisagree.\n\nThere are currently two affected classes of inconsistencies: character padding\nand unsigned subtraction:\n\n* For a VARCHAR or TEXT generated column the length of the value returned can\nvary depending on the PAD_CHAR_TO_FULL_LENGTH sql_mode flag. To make the\nvalue consistent, create the generated column using an RTRIM() or RPAD()\nfunction. Alternately, create the generated column as a CHAR column so that\nits data is always fully padded.\n\n* If a SIGNED generated column is based on the subtraction of an UNSIGNED\nvalue, the resulting value can vary depending on how large the value is and\nthe NO_UNSIGNED_SUBTRACTION sql_mode flag. To make the value consistent, use\nCAST() to ensure that each UNSIGNED operand is SIGNED before the subtraction.\n\nMariaDB starting with 10.5\n--------------------------\nBeginning in MariaDB 10.5, there is a fatal error generated when trying to\ncreate a generated column whose value can change depending on the SQL Mode\nwhen its data is PERSISTENT or indexed.\n\nFor an existing generated column that has a potentially inconsistent value, a\nwarning about a bad expression is generated the first time it is used (if\nwarnings are enabled).\n\nBeginning in MariaDB 10.4.8, MariaDB 10.3.18, and MariaDB 10.2.27 a\npotentially inconsistent generated column outputs a warning when created or\nfirst used (without restricting their creation).\n\nHere is an example of two tables that would be rejected in MariaDB 10.5 and\nwarned about in the other listed versions:\n\nCREATE TABLE bad_pad (\n txt CHAR(5),\n -- CHAR -> VARCHAR or CHAR -> TEXT can\'t be persistent or indexed:\n vtxt VARCHAR(5) AS (txt) PERSISTENT\n);\n\nCREATE TABLE bad_sub (\n num1 BIGINT UNSIGNED,\n num2 BIGINT UNSIGNED,\n -- The resulting value can vary for some large values\n vnum BIGINT AS (num1 - num2) VIRTUAL,\n KEY(vnum)\n);\n\nThe warnings for the above tables look like this:\n\nWarning (Code 1901): Function or expression \'`txt`\' cannot be used in the\nGENERATED ALWAYS AS clause of `vtxt`\nWarning (Code 1105): Expression depends on the @@sql_mode value\nPAD_CHAR_TO_FULL_LENGTH\n\nWarning (Code 1901): Function or expression \'`num1` - `num2`\' cannot be used\nin the GENERATED ALWAYS AS clause of `vnum`\nWarning (Code 1105): Expression depends on the @@sql_mode value\nNO_UNSIGNED_SUBTRACTION\n\nTo work around the issue, force the padding or type to make the generated\ncolumn\'s expression return a consistent value. For example:\n\nCREATE TABLE good_pad (\n txt CHAR(5),\n -- Using RTRIM() or RPAD() makes the value consistent:\n vtxt VARCHAR(5) AS (RTRIM(txt)) PERSISTENT,\n -- When not persistent or indexed, it is OK for the value to vary by mode:\n vtxt2 VARCHAR(5) AS (txt) VIRTUAL,\n -- CHAR -> CHAR is always OK:\n txt2 CHAR(5) AS (txt) PERSISTENT\n);\n\nCREATE TABLE good_sub (\n num1 BIGINT UNSIGNED,\n num2 BIGINT UNSIGNED,\n -- The indexed value will always be consistent in this expression:\n vnum BIGINT AS (CAST(num1 AS SIGNED) - CAST(num2 AS SIGNED)) VIRTUAL,\n KEY(vnum)\n);\n\nMySQL Compatibility Support\n---------------------------\n\n* The STORED keyword is supported as an alias for the PERSISTENT keyword.\n\n* Tables created with MySQL 5.7 or later that contain MySQL\'s generated\ncolumns can be imported into MariaDB without a dump and restore.\n\nImplementation Differences\n--------------------------\n\nGenerated columns are subject to various constraints in other DBMSs that are\nnot present in MariaDB\'s implementation. Generated columns may also be called\ncomputed columns or virtual columns in different implementations. The various\ndetails for a specific implementation can be found in the documentation for\neach specific DBMS.\n\nImplementation Differences Compared to Microsoft SQL Server\n-----------------------------------------------------------\n\nMariaDB\'s generated columns implementation does not enforce the following\nrestrictions that are present in Microsoft SQL Server\'s computed columns\nimplementation:\n\n* MariaDB allows server variables in generated column expressions, including\nthose that change dynamically, such as warning_count.\n* MariaDB allows the CONVERT_TZ() function to be called with a named time zone\nas an argument, even though time zone names and time offsets are configurable.\n* MariaDB allows the CAST() function to be used with non-unicode character\nsets, even though character sets are configurable and differ between\nbinaries/versions.\n* MariaDB allows FLOAT expressions to be used in generated columns. Microsoft\nSQL Server considers these expressions to be \"imprecise\" due to potential\ncross-platform differences in floating-point implementations and precision.\n* Microsoft SQL Server requires the ARITHABORT mode to be set, so that\ndivision by zero returns an error, and not a NULL.\n* Microsoft SQL Server requires QUOTED_IDENTIFIER to be set in sql_mode. In\nMariaDB, if data is inserted without ANSI_QUOTES set in sql_mode, then it will','','https://mariadb.com/kb/en/generated-columns/'); +update help_topic set description = CONCAT(description, '\nbe processed and stored differently in a generated column that contains quoted\nidentifiers.\n\nMicrosoft SQL Server enforces the above restrictions by doing one of the\nfollowing things:\n\n* Refusing to create computed columns.\n* Refusing to allow updates to a table containing them.\n* Refusing to use an index over such a column if it can not be guaranteed that\nthe expression is fully deterministic.\n\nIn MariaDB, as long as the sql_mode, language, and other settings that were in\neffect during the CREATE TABLE remain unchanged, the generated column\nexpression will always be evaluated the same. If any of these things change,\nthen please be aware that the generated column expression might not be\nevaluated the same way as it previously was.\n\nIf you try to update a virtual column, you will get an error if the default\nstrict mode is enabled in sql_mode, or a warning otherwise.\n\nDevelopment History\n-------------------\n\nGenerated columns was originally developed by Andrey Zhakov. It was then\nmodified by Sanja Byelkin and Igor Babaev at Monty Program for inclusion in\nMariaDB. Monty did the work on MariaDB 10.2 to lift a some of the old\nlimitations.\n\nExamples\n--------\n\nHere is an example table that uses both VIRTUAL and PERSISTENT virtual columns:\n\nUSE TEST;\n\nCREATE TABLE table1 (\n a INT NOT NULL,\n b VARCHAR(32),\n c INT AS (a mod 10) VIRTUAL,\n d VARCHAR(5) AS (left(b,5)) PERSISTENT);\n\nIf you describe the table, you can easily see which columns are virtual by\nlooking in the \"Extra\" column:\n\nDESCRIBE table1;\n+-------+-------------+------+-----+---------+------------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+-------------+------+-----+---------+------------+\n| a | int(11) | NO | | NULL | |\n| b | varchar(32) | YES | | NULL | |\n| c | int(11) | YES | | NULL | VIRTUAL |\n| d | varchar(5) | YES | | NULL | PERSISTENT |\n+-------+-------------+------+-----+---------+------------+\n\nTo find out what function(s) generate the value of the virtual column you can\nuse SHOW CREATE TABLE:\n\nSHOW CREATE TABLE table1;\n\n| table1 | CREATE TABLE `table1` (\n `a` int(11) NOT NULL,\n `b` varchar(32) DEFAULT NULL,\n `c` int(11) AS (a mod 10) VIRTUAL,\n `d` varchar(5) AS (left(b,5)) PERSISTENT\n) ENGINE=MyISAM DEFAULT CHARSET=latin1 |\n\nIf you try to insert non-default values into a virtual column, you will\nreceive a warning and what you tried to insert will be ignored and the derived\nvalue inserted instead:\n\nWARNINGS;\nShow warnings enabled.\n\nINSERT INTO table1 VALUES (1, \'some text\',default,default);\nQuery OK, 1 row affected (0.00 sec)\n\nINSERT INTO table1 VALUES (2, \'more text\',5,default);\nQuery OK, 1 row affected, 1 warning (0.00 sec)\n\nWarning (Code 1645): The value specified for computed column \'c\' in table\n\'table1\' has been ignored.\n\nINSERT INTO table1 VALUES (123, \'even more text\',default,\'something\');\nQuery OK, 1 row affected, 2 warnings (0.00 sec)\n\nWarning (Code 1645): The value specified for computed column \'d\' in table\n\'table1\' has been ignored.\nWarning (Code 1265): Data truncated for column \'d\' at row 1\n\nSELECT * FROM table1;\n+-----+----------------+------+-------+\n| a | b | c | d |\n+-----+----------------+------+-------+\n| 1 | some text | 1 | some |\n| 2 | more text | 2 | more |\n| 123 | even more text | 3 | even |\n+-----+----------------+------+-------+\n3 rows in set (0.00 sec)\n\nIf the ZEROFILL clause is specified, it should be placed directly after the\ntype definition, before the AS ():\n\nCREATE TABLE table2 (a INT, b INT ZEROFILL AS (a*2) VIRTUAL);\nINSERT INTO table2 (a) VALUES (1);\n\nSELECT * FROM table2;\n+------+------------+\n| a | b |\n+------+------------+\n| 1 | 0000000002 |\n+------+------------+\n1 row in set (0.00 sec)\n\nYou can also use virtual columns to implement a \"poor man\'s partial index\".\nSee example at the end of Unique Index.\n\nURL: https://mariadb.com/kb/en/generated-columns/') WHERE help_topic_id = 718; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (719,38,'Invisible Columns','MariaDB starting with 10.3.3\n----------------------------\nInvisible columns (sometimes also called hidden columns) first appeared in\nMariaDB 10.3.3.\n\nColumns can be given an INVISIBLE attribute in a CREATE TABLE or ALTER TABLE\nstatement. These columns will then not be listed in the results of a SELECT *\nstatement, nor do they need to be assigned a value in an INSERT statement,\nunless INSERT explicitly mentions them by name.\n\nSince SELECT * does not return the invisible columns, new tables or views\ncreated in this manner will have no trace of the invisible columns. If\nspecifically referenced in the SELECT statement, the columns will be brought\ninto the view/new table, but the INVISIBLE attribute will not.\n\nInvisible columns can be declared as NOT NULL, but then require a DEFAULT\nvalue.\n\nIt is not possible for all columns in a table to be invisible.\n\nExamples\n--------\n\nCREATE TABLE t (x INT INVISIBLE);\nERROR 1113 (42000): A table must have at least 1 column\n\nCREATE TABLE t (x INT, y INT INVISIBLE, z INT INVISIBLE NOT NULL);\nERROR 4106 (HY000): Invisible column `z` must have a default value\n\nCREATE TABLE t (x INT, y INT INVISIBLE, z INT INVISIBLE NOT NULL DEFAULT 4);\n\nINSERT INTO t VALUES (1),(2);\n\nINSERT INTO t (x,y) VALUES (3,33);\n\nSELECT * FROM t;\n+------+\n| x |\n+------+\n| 1 |\n| 2 |\n| 3 |\n+------+\n\nSELECT x,y,z FROM t;\n+------+------+---+\n| x | y | z |\n+------+------+---+\n| 1 | NULL | 4 |\n| 2 | NULL | 4 |\n| 3 | 33 | 4 |\n+------+------+---+\n\nDESC t;\n+-------+---------+------+-----+---------+-----------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+---------+------+-----+---------+-----------+\n| x | int(11) | YES | | NULL | |\n| y | int(11) | YES | | NULL | INVISIBLE |\n| z | int(11) | NO | | 4 | INVISIBLE |\n+-------+---------+------+-----+---------+-----------+\n\nALTER TABLE t MODIFY x INT INVISIBLE, MODIFY y INT, MODIFY z INT NOT NULL\nDEFAULT 4;\n\nDESC t;\n+-------+---------+------+-----+---------+-----------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+---------+------+-----+---------+-----------+\n| x | int(11) | YES | | NULL | INVISIBLE |\n| y | int(11) | YES | | NULL | |\n| z | int(11) | NO | | 4 | |\n+-------+---------+------+-----+---------+-----------+\n\nCreating a view from a table with hidden columns:\n\nCREATE VIEW v1 AS SELECT * FROM t;\n\nDESC v1;\n+-------+---------+------+-----+---------+-------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+---------+------+-----+---------+-------+\n| y | int(11) | YES | | NULL | |\n| z | int(11) | NO | | 4 | |\n+-------+---------+------+-----+---------+-------+\n\nCREATE VIEW v2 AS SELECT x,y,z FROM t;\n\nDESC v2;\n+-------+---------+------+-----+---------+-------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+---------+------+-----+---------+-------+\n| x | int(11) | YES | | NULL | |\n| y | int(11) | YES | | NULL | |\n| z | int(11) | NO | | 4 | |\n+-------+---------+------+-----+---------+-------+\n\nAdding a Surrogate Primary Key:\n\ncreate table t1 (x bigint unsigned not null, y varchar(16), z text);\n\ninsert into t1 values (123, \'qq11\', \'ipsum\');\n\ninsert into t1 values (123, \'qq22\', \'lorem\');\n\nalter table t1 add pkid serial primary key invisible first;\n\ninsert into t1 values (123, \'qq33\', \'amet\');\n\nselect * from t1;\n+-----+------+-------+\n| x | y | z |\n+-----+------+-------+\n| 123 | qq11 | ipsum |\n| 123 | qq22 | lorem |\n| 123 | qq33 | amet |\n+-----+------+-------+\n\nselect pkid, z from t1;\n+------+-------+\n| pkid | z |\n+------+-------+\n| 1 | ipsum |\n| 2 | lorem |\n| 3 | amet |\n+------+-------+\n\nURL: https://mariadb.com/kb/en/invisible-columns/','','https://mariadb.com/kb/en/invisible-columns/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (720,38,'DROP DATABASE','Syntax\n------\n\nDROP {DATABASE | SCHEMA} [IF EXISTS] db_name\n\nDescription\n-----------\n\nDROP DATABASE drops all tables in the database and deletes the database. Be\nvery careful with this statement! To use DROP DATABASE, you need the DROP\nprivilege on the database. DROP SCHEMA is a synonym for DROP DATABASE.\n\nImportant: When a database is dropped, user privileges on the database are not\nautomatically dropped. See GRANT.\n\nIF EXISTS\n---------\n\nUse IF EXISTS to prevent an error from occurring for databases that do not\nexist. A NOTE is generated for each non-existent database when using IF\nEXISTS. See SHOW WARNINGS.\n\nAtomic DDL\n----------\n\nMariaDB starting with 10.6.1\n----------------------------\nMariaDB 10.6.1 supports Atomic DDL.\n\nDROP DATABASE is implemented as\n\nloop over all tables\n DROP TABLE table\n\nEach individual DROP TABLE is atomic while DROP DATABASE as a whole is\ncrash-safe.\n\nExamples\n--------\n\nDROP DATABASE bufg;\nQuery OK, 0 rows affected (0.39 sec)\n\nDROP DATABASE bufg;\nERROR 1008 (HY000): Can\'t drop database \'bufg\'; database doesn\'t exist\n\n\\W\nShow warnings enabled.\n\nDROP DATABASE IF EXISTS bufg;\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\nNote (Code 1008): Can\'t drop database \'bufg\'; database doesn\'t exist\n\nURL: https://mariadb.com/kb/en/drop-database/','','https://mariadb.com/kb/en/drop-database/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (721,38,'DROP EVENT','Syntax\n------\n\nDROP EVENT [IF EXISTS] event_name\n\nDescription\n-----------\n\nThis statement drops the event named event_name. The event immediately ceases\nbeing active, and is deleted completely from the server.\n\nIf the event does not exist, the error ERROR 1517 (HY000): Unknown event\n\'event_name\' results. You can override this and cause the statement to\ngenerate a NOTE for non-existent events instead by using IF EXISTS. See SHOW\nWARNINGS.\n\nThis statement requires the EVENT privilege. In MySQL 5.1.11 and earlier, an\nevent could be dropped only by its definer, or by a user having the SUPER\nprivilege.\n\nExamples\n--------\n\nDROP EVENT myevent3;\n\nUsing the IF EXISTS clause:\n\nDROP EVENT IF EXISTS myevent3;\nQuery OK, 0 rows affected, 1 warning (0.01 sec)\n\nSHOW WARNINGS;\n+-------+------+-------------------------------+\n| Level | Code | Message |\n+-------+------+-------------------------------+\n| Note | 1305 | Event myevent3 does not exist |\n+-------+------+-------------------------------+\n\nURL: https://mariadb.com/kb/en/drop-event/','','https://mariadb.com/kb/en/drop-event/'); @@ -918,8 +918,8 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (805,44,'WSREP_LAST_SEEN_GTID','MariaDB starting with 10.4.2\n----------------------------\nWSREP_LAST_SEEN_GTID was added as part of Galera 4 in MariaDB 10.4.2.\n\nSyntax\n------\n\nWSREP_LAST_SEEN_GTID()\n\nDescription\n-----------\n\nReturns the Global Transaction ID of the most recent write transaction\nobserved by the client.\n\nThe result can be useful to determine the transaction to provide to\nWSREP_SYNC_WAIT_UPTO_GTID for waiting and unblocking purposes.\n\nURL: https://mariadb.com/kb/en/wsrep_last_seen_gtid/','','https://mariadb.com/kb/en/wsrep_last_seen_gtid/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (806,44,'WSREP_LAST_WRITTEN_GTID','MariaDB starting with 10.4.2\n----------------------------\nWSREP_LAST_WRITTEN_GTID was added as part of Galera 4 in MariaDB 10.4.2.\n\nSyntax\n------\n\nWSREP_LAST_WRITTEN_GTID()\n\nDescription\n-----------\n\nReturns the Global Transaction ID of the most recent write transaction\nperformed by the client.\n\nURL: https://mariadb.com/kb/en/wsrep_last_written_gtid/','','https://mariadb.com/kb/en/wsrep_last_written_gtid/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (807,44,'WSREP_SYNC_WAIT_UPTO_GTID','MariaDB starting with 10.4.2\n----------------------------\nWSREP_SYNC_WAIT_UPTO_GTID was added as part of Galera 4 in MariaDB 10.4.2.\n\nSyntax\n------\n\nWSREP_SYNC_WAIT_UPTO_GTID(gtid[,timeout])\n\nDescription\n-----------\n\nBlocks the client until the transaction specified by the given Global\nTransaction ID is applied and committed by the node.\n\nThe optional timeout argument can be used to specify a block timeout in\nseconds. If not provided, the timeout will be indefinite.\n\nReturns the node that applied and committed the Global Transaction ID,\nER_LOCAL_WAIT_TIMEOUT if the function is timed out before this, or\nER_WRONG_ARGUMENTS if the function is given an invalid GTID.\n\nThe result from WSREP_LAST_SEEN_GTID can be useful to determine the\ntransaction to provide to WSREP_SYNC_WAIT_UPTO_GTID for waiting and unblocking\npurposes.\n\nURL: https://mariadb.com/kb/en/wsrep_sync_wait_upto_gtid/','','https://mariadb.com/kb/en/wsrep_sync_wait_upto_gtid/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (808,45,'System-Versioned Tables','MariaDB supports temporal data tables in the form of system-versioning tables\n(allowing you to query and operate on historic data, discussed below),\napplication-time periods (allow you to query and operate on a temporal range\nof data), and bitemporal tables (which combine both system-versioning and\napplication-time periods).\n\nSystem-Versioned Tables\n-----------------------\n\nMariaDB starting with 10.3.4\n----------------------------\nSupport for system-versioned tables was added in MariaDB 10.3.4.\n\nSystem-versioned tables store the history of all changes, not only data which\nis currently valid. This allows data analysis for any point in time, auditing\nof changes and comparison of data from different points in time. Typical uses\ncases are:\n\n* Forensic analysis & legal requirements to store data for N years.\n* Data analytics (retrospective, trends etc.), e.g. to get your staff\ninformation as of one year ago.\n* Point-in-time recovery - recover a table state as of particular point in\ntime.\n\nSystem-versioned tables were first introduced in the SQL:2011 standard.\n\nCreating a System-Versioned Table\n---------------------------------\n\nThe CREATE TABLE syntax has been extended to permit creating a\nsystem-versioned table. To be system-versioned, according to SQL:2011, a table\nmust have two generated columns, a period, and a special table option clause:\n\nCREATE TABLE t(\n x INT,\n start_timestamp TIMESTAMP(6) GENERATED ALWAYS AS ROW START,\n end_timestamp TIMESTAMP(6) GENERATED ALWAYS AS ROW END,\n PERIOD FOR SYSTEM_TIME(start_timestamp, end_timestamp)\n) WITH SYSTEM VERSIONING;\n\nIn MariaDB one can also use a simplified syntax:\n\nCREATE TABLE t (\n x INT\n) WITH SYSTEM VERSIONING;\n\nIn the latter case no extra columns will be created and they won\'t clutter the\noutput of, say, SELECT * FROM t. The versioning information will still be\nstored, and it can be accessed via the pseudo-columns ROW_START and ROW_END:\n\nSELECT x, ROW_START, ROW_END FROM t;\n\nAdding or Removing System Versioning To/From a Table\n----------------------------------------------------\n\nAn existing table can be altered to enable system versioning for it.\n\nCREATE TABLE t(\n x INT\n);\n\nALTER TABLE t ADD SYSTEM VERSIONING;\n\nSHOW CREATE TABLE t\\G\n*************************** 1. row ***************************\n Table: t\nCreate Table: CREATE TABLE `t` (\n `x` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING\n\nSimilarly, system versioning can be removed from a table:\n\nALTER TABLE t DROP SYSTEM VERSIONING;\n\nSHOW CREATE TABLE t\\G\n*************************** 1. row ***************************\n Table: t\nCreate Table: CREATE TABLE `t` (\n `x` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=latin1\n\nOne can also add system versioning with all columns created explicitly:\n\nALTER TABLE t ADD COLUMN ts TIMESTAMP(6) GENERATED ALWAYS AS ROW START,\n ADD COLUMN te TIMESTAMP(6) GENERATED ALWAYS AS ROW END,\n ADD PERIOD FOR SYSTEM_TIME(ts, te),\n ADD SYSTEM VERSIONING;\n\nSHOW CREATE TABLE t\\G\n*************************** 1. row ***************************\n Table: t\nCreate Table: CREATE TABLE `t` (\n `x` int(11) DEFAULT NULL,\n `ts` timestamp(6) GENERATED ALWAYS AS ROW START,\n `te` timestamp(6) GENERATED ALWAYS AS ROW END,\n PERIOD FOR SYSTEM_TIME (`ts`, `te`)\n) ENGINE=InnoDB DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING\n\nQuerying Historical Data\n------------------------\n\nSELECT\n------\n\nTo query the historical data one uses the clause FOR SYSTEM_TIME directly\nafter the table name (before the table alias, if any). SQL:2011 provides three\nsyntactic extensions:\n\n* AS OF is used to see the table as it was at a specific point in time in the\npast:\n\nSELECT * FROM t FOR SYSTEM_TIME AS OF TIMESTAMP\'2016-10-09 08:07:06\';\n\n* BETWEEN start AND end will show all rows that were visible at any point\nbetween two specified points in time. It works inclusively, a row visible\nexactly at start or exactly at end will be shown too.\n\nSELECT * FROM t FOR SYSTEM_TIME BETWEEN (NOW() - INTERVAL 1 YEAR) AND NOW();\n\n* FROM start TO end will also show all rows that were visible at any point\nbetween two specified points in time, including start, but excluding end.\n\nSELECT * FROM t FOR SYSTEM_TIME FROM \'2016-01-01 00:00:00\' TO \'2017-01-01\n00:00:00\';\n\nAdditionally MariaDB implements a non-standard extension:\n\n* ALL will show all rows, historical and current.\n\nSELECT * FROM t FOR SYSTEM_TIME ALL;\n\nIf the FOR SYSTEM_TIME clause is not used, the table will show the current\ndata, as if one had specified FOR SYSTEM_TIME AS OF CURRENT_TIMESTAMP.\n\nViews and Subqueries\n--------------------\n\nWhen a system-versioned tables is used in a view or in a subquery in the from\nclause, FOR SYSTEM_TIME can be used directly in the view or subquery body, or\n(non-standard) applied to the whole view when it\'s being used in a SELECT:\n\nCREATE VIEW v1 AS SELECT * FROM t FOR SYSTEM_TIME AS OF TIMESTAMP\'2016-10-09\n08:07:06\';\n\nOr\n\nCREATE VIEW v1 AS SELECT * FROM t;\nSELECT * FROM v1 FOR SYSTEM_TIME AS OF TIMESTAMP\'2016-10-09 08:07:06\';\n\nUse in Replication and Binary Logs\n----------------------------------\n\nTables that use system-versioning implicitly add the row_end column to the\nPrimary Key. While this is generally not an issue for most use cases, it can\nlead to problems when re-applying write statements from the binary log or in\nreplication environments, where a primary retries an SQL statement on the\nreplica.\n\nSpecifically, these writes include a value on the row_end column containing\nthe timestamp from when the write was initially made. The re-occurrence of the\nPrimary Key with the old system-versioning columns raises an error due to the\nduplication.\n\nTo mitigate this with MariaDB Replication, set the secure_timestamp system\nvariable to YES on the replica. When set, the replica uses its own system\nclock when applying to the row log, meaning that the primary can retry as many\ntimes as needed without causing a conflict. The retries generate new\nhistorical rows with new values for the row_start and row_end columns.\n\nTransaction-Precise History in InnoDB\n-------------------------------------\n\nA point in time when a row was inserted or deleted does not necessarily mean\nthat a change became visible at the same moment. With transactional tables, a\nrow might have been inserted in a long transaction, and became visible hours\nafter it was inserted.\n\nFor some applications — for example, when doing data analytics on one-year-old\ndata — this distinction does not matter much. For others — forensic analysis —\nit might be crucial.\n\nMariaDB supports transaction-precise history (only for the InnoDB storage\nengine) that allows seeing the data exactly as it would\'ve been seen by a new\nconnection doing a SELECT at the specified point in time — rows inserted\nbefore that point, but committed after will not be shown.\n\nTo use transaction-precise history, InnoDB needs to remember not timestamps,\nbut transaction identifier per row. This is done by creating generated columns\nas BIGINT UNSIGNED, not TIMESTAMP(6):\n\nCREATE TABLE t(\n x INT,\n start_trxid BIGINT UNSIGNED GENERATED ALWAYS AS ROW START,\n end_trxid BIGINT UNSIGNED GENERATED ALWAYS AS ROW END,\n PERIOD FOR SYSTEM_TIME(start_trxid, end_trxid)\n) WITH SYSTEM VERSIONING;\n\nThese columns must be specified explicitly, but they can be made INVISIBLE to\navoid cluttering SELECT * output.\n\nWhen one uses transaction-precise history, one can optionally use transaction\nidentifiers in the FOR SYSTEM_TIME clause:\n\nSELECT * FROM t FOR SYSTEM_TIME AS OF TRANSACTION 12345;\n\nThis will show the data, exactly as it was seen by the transaction with the\nidentifier 12345.\n\nStoring the History Separately\n------------------------------\n\nWhen the history is stored together with the current data, it increases the\nsize of the table, so current data queries — table scans and index searches —\nwill take more time, because they will need to skip over historical data. If\nmost queries on that table use only current data, it might make sense to store\nthe history separately, to reduce the overhead from versioning.\n\nThis is done by partitioning the table by SYSTEM_TIME. Because of the\npartition pruning optimization, all current data queries will only access one\npartition, the one that stores current data.\n\nThis example shows how to create such a partitioned table:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME (\n PARTITION p_hist HISTORY,\n PARTITION p_cur CURRENT\n );\n\nIn this example all history will be stored in the partition p_hist while all\ncurrent data will be in the partition p_cur. The table must have exactly one\ncurrent partition and at least one historical partition.\n\nPartitioning by SYSTEM_TIME also supports automatic partition rotation. One\ncan rotate historical partitions by time or by size. This example shows how to\nrotate partitions by size:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME LIMIT 100000 (\n PARTITION p0 HISTORY,\n PARTITION p1 HISTORY,\n PARTITION pcur CURRENT\n );\n\nMariaDB will start writing history rows into partition p0, and when it reaches\na size of 100000 rows, MariaDB will switch to partition p1. There are only two\nhistorical partitions, so when p1 overflows, MariaDB will issue a warning, but\nwill continue writing into it.\n\nSimilarly, one can rotate partitions by time:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 WEEK (\n PARTITION p0 HISTORY,\n PARTITION p1 HISTORY,\n PARTITION p2 HISTORY,\n PARTITION pcur CURRENT\n );\n\nThis means that the history for the first week after the table was created\nwill be stored in p0. The history for the second week — in p1, and all later\nhistory will go into p2. One can see the exact rotation time for each\npartition in the INFORMATION_SCHEMA.PARTITIONS table.\n\nIt is possible to combine partitioning by SYSTEM_TIME and subpartitions:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME\n SUBPARTITION BY KEY (x)\n SUBPARTITIONS 4 (\n PARTITION ph HISTORY,\n PARTITION pc CURRENT\n );\n\nDefault Partitions\n------------------\n\nMariaDB starting with 10.5.0\n----------------------------\nSince partitioning by current and historical data is such a typical usecase,\nfrom MariaDB 10.5, it is possible to use a simplified statement to do so. For\nexample, instead of\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING \n PARTITION BY SYSTEM_TIME (\n PARTITION p0 HISTORY,\n PARTITION pn CURRENT\n);\n\nyou can use\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING \n PARTITION BY SYSTEM_TIME;\n\nYou can also specify the number of partitions, which is useful if you want to\nrotate history by time, for example:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING \n PARTITION BY SYSTEM_TIME\n INTERVAL 1 MONTH\n PARTITIONS 12;\n\nSpecifying the number of partitions without specifying a rotation condition\nwill result in a warning:\n\nCREATE OR REPLACE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME PARTITIONS 12;\nQuery OK, 0 rows affected, 1 warning (0.518 sec)\n\nWarning (Code 4115): Maybe missing parameters: no rotation condition for\nmultiple HISTORY partitions.\n\nwhile specifying only 1 partition will result in an error:\n\nCREATE OR REPLACE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME PARTITIONS 1;\nERROR 4128 (HY000): Wrong partitions for `t`: must have at least one HISTORY\nand exactly one last CURRENT\n\nAutomatically Creating Partitions\n---------------------------------\n\nMariaDB starting with 10.9.1\n----------------------------\nFrom MariaDB 10.9.1, the AUTO keyword can be used to automatically create\nhistory partitions.\n\nFor example\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO;\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 MONTH\n STARTS \'2021-01-01 00:00:00\' AUTO PARTITIONS 12;\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME LIMIT 1000 AUTO;\n\nOr with explicit partitions:\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO\n (PARTITION p0 HISTORY, PARTITION pn CURRENT);\n\nTo disable or enable auto-creation one can use ALTER TABLE by adding or\nremoving AUTO from the partitioning specification:\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO;\n\n# Disables auto-creation:\nALTER TABLE t1 PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR;\n\n# Enables auto-creation:\nALTER TABLE t1 PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO;\n\nIf the rest of the partitioning specification is identical to CREATE TABLE, no\nrepartitioning will be done (for details see MDEV-27328).\n\nRemoving Old History\n--------------------\n\nBecause it stores all the history, a system-versioned table might grow very\nlarge over time. There are many options to trim down the space and remove the\nold history.\n\nOne can completely drop the versioning from the table and add it back again,\nthis will delete all the history:\n\nALTER TABLE t DROP SYSTEM VERSIONING;\nALTER TABLE t ADD SYSTEM VERSIONING;\n\nIt might be a rather time-consuming operation, though, as the table will need\nto be rebuilt, possibly twice (depending on the storage engine).\n\nAnother option would be to use partitioning and drop some of historical\npartitions:\n\nALTER TABLE t DROP PARTITION p0;\n\nNote, that one cannot drop a current partition or the only historical\npartition.\n\nAnd the third option; one can use a variant of the DELETE statement to prune\nthe history:\n\nDELETE HISTORY FROM t;\n\nor only old history up to a specific point in time:\n\nDELETE HISTORY FROM t BEFORE SYSTEM_TIME \'2016-10-09 08:07:06\';\n\nor to a specific transaction (with BEFORE SYSTEM_TIME TRANSACTION xxx).\n\nTo protect the integrity of the history, this statement requires a special\nDELETE HISTORY privilege.\n\nCurrently, using the DELETE HISTORY statement with a BEFORE SYSTEM_TIME\ngreater than the ROW_END of the active records (as a TIMESTAMP, this has a\nmaximum value of \'2038-01-19 03:14:07\' UTC) will result in the historical\nrecords being dropped, and the active records being deleted and moved to\nhistory. See MDEV-25468.\n','','https://mariadb.com/kb/en/system-versioned-tables/'); -update help_topic set description = CONCAT(description, '\nPrior to MariaDB 10.4.5, the TRUNCATE TABLE statement drops all historical\nrecords from a system-versioned-table.\n\nFrom MariaDB 10.4.5, historic data is protected from TRUNCATE statements, as\nper the SQL standard, and an Error 4137 is instead raised:\n\nTRUNCATE t;\nERROR 4137 (HY000): System-versioned tables do not support TRUNCATE TABLE\n\nExcluding Columns From Versioning\n---------------------------------\n\nAnother MariaDB extension allows to version only a subset of columns in a\ntable. This is useful, for example, if you have a table with user information\nthat should be versioned, but one column is, let\'s say, a login counter that\nis incremented often and is not interesting to version. Such a column can be\nexcluded from versioning by declaring it WITHOUT VERSIONING\n\nCREATE TABLE t (\n x INT,\n y INT WITHOUT SYSTEM VERSIONING\n) WITH SYSTEM VERSIONING;\n\nA column can also be declared WITH VERSIONING, that will automatically make\nthe table versioned. The statement below is equivalent to the one above:\n\nCREATE TABLE t (\n x INT WITH SYSTEM VERSIONING,\n y INT\n);\n\nChanges in other sections: https://mariadb.com/kb/en/create-table/\nhttps://mariadb.com/kb/en/alter-table/ https://mariadb.com/kb/en/join-syntax/\nhttps://mariadb.com/kb/en/partitioning-types-overview/\nhttps://mariadb.com/kb/en/date-and-time-units/\nhttps://mariadb.com/kb/en/delete/ https://mariadb.com/kb/en/grant/\n\nthey all reference back to this page\n\nAlso, TODO:\n\n* limitations (size, speed, adding history to unique not nullable columns)\n\nSystem Variables\n----------------\n\nThere are a number of system variables related to system-versioned tables:\n\nsystem_versioning_alter_history\n-------------------------------\n\n* Description: SQL:2011 does not allow ALTER TABLE on system-versioned tables.\nWhen this variable is set to ERROR, an attempt to alter a system-versioned\ntable will result in an error. When this variable is set to KEEP, ALTER TABLE\nwill be allowed, but the history will become incorrect — querying historical\ndata will show the new table structure. This mode is still useful, for\nexample, when adding new columns to a table. Note that if historical data\ncontains or would contain nulls, attempting to ALTER these columns to be NOT\nNULL will return an error (or warning if strict_mode is not set).\n* Commandline: --system-versioning-alter-history=value\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Enum\n* Default Value: ERROR\n* Valid Values: ERROR, KEEP\n* Introduced: MariaDB 10.3.4\n\nsystem_versioning_asof\n----------------------\n\n* Description: If set to a specific timestamp value, an implicit FOR\nSYSTEM_TIME AS OF clause will be applied to all queries. This is useful if one\nwants to do many queries for history at the specific point in time. Set it to\nDEFAULT to restore the default behavior. Has no effect on DML, so queries such\nas INSERT .. SELECT and REPLACE .. SELECT need to state AS OF explicitly.\n* Commandline: None\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Varchar\n* Default Value: DEFAULT\n* Introduced: MariaDB 10.3.4\n\nsystem_versioning_innodb_algorithm_simple\n-----------------------------------------\n\n* Description: Never fully implemented and removed in the following release.\n* Commandline: --system-versioning-innodb-algorithm-simple[={0|1}]\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Boolean\n* Default Value: ON\n* Introduced: MariaDB 10.3.4\n* Removed: MariaDB 10.3.5\n\nsystem_versioning_insert_history\n--------------------------------\n\n* Description: Allows direct inserts into ROW_START and ROW_END columns if\nsecure_timestamp allows changing timestamp.\n* Commandline: --system-versioning-insert-history[={0|1}]\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Boolean\n* Default Value: OFF\n* Introduced: MariaDB 10.11.0\n\nLimitations\n-----------\n\n* Versioning clauses can not be applied to generated (virtual and persistent)\ncolumns.\n* mysqldump does not read historical rows from versioned tables, and so\nhistorical data will not be backed up. Also, a restore of the timestamps would\nnot be possible as they cannot be defined by an insert/a user.\n\nURL: https://mariadb.com/kb/en/system-versioned-tables/') WHERE help_topic_id = 808; +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (808,45,'System-Versioned Tables','MariaDB supports temporal data tables in the form of system-versioning tables\n(allowing you to query and operate on historic data, discussed below),\napplication-time periods (allow you to query and operate on a temporal range\nof data), and bitemporal tables (which combine both system-versioning and\napplication-time periods).\n\nSystem-Versioned Tables\n-----------------------\n\nSystem-versioned tables store the history of all changes, not only data which\nis currently valid. This allows data analysis for any point in time, auditing\nof changes and comparison of data from different points in time. Typical uses\ncases are:\n\n* Forensic analysis & legal requirements to store data for N years.\n* Data analytics (retrospective, trends etc.), e.g. to get your staff\ninformation as of one year ago.\n* Point-in-time recovery - recover a table state as of particular point in\ntime.\n\nSystem-versioned tables were first introduced in the SQL:2011 standard.\n\nCreating a System-Versioned Table\n---------------------------------\n\nThe CREATE TABLE syntax has been extended to permit creating a\nsystem-versioned table. To be system-versioned, according to SQL:2011, a table\nmust have two generated columns, a period, and a special table option clause:\n\nCREATE TABLE t(\n x INT,\n start_timestamp TIMESTAMP(6) GENERATED ALWAYS AS ROW START,\n end_timestamp TIMESTAMP(6) GENERATED ALWAYS AS ROW END,\n PERIOD FOR SYSTEM_TIME(start_timestamp, end_timestamp)\n) WITH SYSTEM VERSIONING;\n\nIn MariaDB one can also use a simplified syntax:\n\nCREATE TABLE t (\n x INT\n) WITH SYSTEM VERSIONING;\n\nIn the latter case no extra columns will be created and they won\'t clutter the\noutput of, say, SELECT * FROM t. The versioning information will still be\nstored, and it can be accessed via the pseudo-columns ROW_START and ROW_END:\n\nSELECT x, ROW_START, ROW_END FROM t;\n\nAdding or Removing System Versioning To/From a Table\n----------------------------------------------------\n\nAn existing table can be altered to enable system versioning for it.\n\nCREATE TABLE t(\n x INT\n);\n\nALTER TABLE t ADD SYSTEM VERSIONING;\n\nSHOW CREATE TABLE t\\G\n*************************** 1. row ***************************\n Table: t\nCreate Table: CREATE TABLE `t` (\n `x` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING\n\nSimilarly, system versioning can be removed from a table:\n\nALTER TABLE t DROP SYSTEM VERSIONING;\n\nSHOW CREATE TABLE t\\G\n*************************** 1. row ***************************\n Table: t\nCreate Table: CREATE TABLE `t` (\n `x` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=latin1\n\nOne can also add system versioning with all columns created explicitly:\n\nALTER TABLE t ADD COLUMN ts TIMESTAMP(6) GENERATED ALWAYS AS ROW START,\n ADD COLUMN te TIMESTAMP(6) GENERATED ALWAYS AS ROW END,\n ADD PERIOD FOR SYSTEM_TIME(ts, te),\n ADD SYSTEM VERSIONING;\n\nSHOW CREATE TABLE t\\G\n*************************** 1. row ***************************\n Table: t\nCreate Table: CREATE TABLE `t` (\n `x` int(11) DEFAULT NULL,\n `ts` timestamp(6) GENERATED ALWAYS AS ROW START,\n `te` timestamp(6) GENERATED ALWAYS AS ROW END,\n PERIOD FOR SYSTEM_TIME (`ts`, `te`)\n) ENGINE=InnoDB DEFAULT CHARSET=latin1 WITH SYSTEM VERSIONING\n\nInserting Data\n--------------\n\nWhen data is inserted into a system-versioned table, it is given a row_start\nvalue of the current timestamp, and a row_end value of\nFROM_UNIXTIME(2147483647.999999). The current timestamp can be adjusted by\nsetting the timestamp system variable, for example:\n\nSELECT NOW();\n+---------------------+\n| NOW() |\n+---------------------+\n| 2022-10-24 23:09:38 |\n+---------------------+\n\nINSERT INTO t VALUES(1);\n\nSET @@timestamp = UNIX_TIMESTAMP(\'2033-10-24\');\n\nINSERT INTO t VALUES(2);\n\nSET @@timestamp = default;\n\nINSERT INTO t VALUES(3);\n\nSELECT a,row_start,row_end FROM t;\n+------+----------------------------+----------------------------+\n| a | row_start | row_end |\n+------+----------------------------+----------------------------+\n| 1 | 2022-10-24 23:09:38.951347 | 2038-01-19 05:14:07.999999 |\n| 2 | 2033-10-24 00:00:00.000000 | 2038-01-19 05:14:07.999999 |\n| 3 | 2022-10-24 23:09:38.961857 | 2038-01-19 05:14:07.999999 |\n+------+----------------------------+----------------------------+\n\nQuerying Historical Data\n------------------------\n\nSELECT\n------\n\nTo query the historical data one uses the clause FOR SYSTEM_TIME directly\nafter the table name (before the table alias, if any). SQL:2011 provides three\nsyntactic extensions:\n\n* AS OF is used to see the table as it was at a specific point in time in the\npast:\n\nSELECT * FROM t FOR SYSTEM_TIME AS OF TIMESTAMP\'2016-10-09 08:07:06\';\n\n* BETWEEN start AND end will show all rows that were visible at any point\nbetween two specified points in time. It works inclusively, a row visible\nexactly at start or exactly at end will be shown too.\n\nSELECT * FROM t FOR SYSTEM_TIME BETWEEN (NOW() - INTERVAL 1 YEAR) AND NOW();\n\n* FROM start TO end will also show all rows that were visible at any point\nbetween two specified points in time, including start, but excluding end.\n\nSELECT * FROM t FOR SYSTEM_TIME FROM \'2016-01-01 00:00:00\' TO \'2017-01-01\n00:00:00\';\n\nAdditionally MariaDB implements a non-standard extension:\n\n* ALL will show all rows, historical and current.\n\nSELECT * FROM t FOR SYSTEM_TIME ALL;\n\nIf the FOR SYSTEM_TIME clause is not used, the table will show the current\ndata. This is usually the same as if one had specified FOR SYSTEM_TIME AS OF\nCURRENT_TIMESTAMP, unless one has adjusted the row_start value (until MariaDB\n10.11, only possible by setting the secure_timestamp variable). For example:\n\nCREATE OR REPLACE TABLE t (a int) WITH SYSTEM VERSIONING;\n\nSELECT NOW();\n+---------------------+\n| NOW() |\n+---------------------+\n| 2022-10-24 23:43:37 |\n+---------------------+\n\nINSERT INTO t VALUES (1);\n\nSET @@timestamp = UNIX_TIMESTAMP(\'2033-03-03\');\n\nINSERT INTO t VALUES (2);\n\nDELETE FROM t;\n\nSET @@timestamp = default;\n\nSELECT a, row_start, row_end FROM t FOR SYSTEM_TIME ALL;\n+------+----------------------------+----------------------------+\n| a | row_start | row_end |\n+------+----------------------------+----------------------------+\n| 1 | 2022-10-24 23:43:37.192725 | 2033-03-03 00:00:00.000000 |\n| 2 | 2033-03-03 00:00:00.000000 | 2033-03-03 00:00:00.000000 |\n+------+----------------------------+----------------------------+\n2 rows in set (0.000 sec)\n\nSELECT a, row_start, row_end FROM t FOR SYSTEM_TIME AS OF CURRENT_TIMESTAMP;\n+------+----------------------------+----------------------------+\n| a | row_start | row_end |\n+------+----------------------------+----------------------------+\n| 1 | 2022-10-24 23:43:37.192725 | 2033-03-03 00:00:00.000000 |\n+------+----------------------------+----------------------------+\n1 row in set (0.000 sec)\n\nSELECT a, row_start, row_end FROM t;\nEmpty set (0.001 sec)\n\nViews and Subqueries\n--------------------\n\nWhen a system-versioned tables is used in a view or in a subquery in the from\nclause, FOR SYSTEM_TIME can be used directly in the view or subquery body, or\n(non-standard) applied to the whole view when it\'s being used in a SELECT:\n\nCREATE VIEW v1 AS SELECT * FROM t FOR SYSTEM_TIME AS OF TIMESTAMP\'2016-10-09\n08:07:06\';\n\nOr\n\nCREATE VIEW v1 AS SELECT * FROM t;\nSELECT * FROM v1 FOR SYSTEM_TIME AS OF TIMESTAMP\'2016-10-09 08:07:06\';\n\nUse in Replication and Binary Logs\n----------------------------------\n\nTables that use system-versioning implicitly add the row_end column to the\nPrimary Key. While this is generally not an issue for most use cases, it can\nlead to problems when re-applying write statements from the binary log or in\nreplication environments, where a primary retries an SQL statement on the\nreplica.\n\nSpecifically, these writes include a value on the row_end column containing\nthe timestamp from when the write was initially made. The re-occurrence of the\nPrimary Key with the old system-versioning columns raises an error due to the\nduplication.\n\nTo mitigate this with MariaDB Replication, set the secure_timestamp system\nvariable to YES on the replica. When set, the replica uses its own system\nclock when applying to the row log, meaning that the primary can retry as many\ntimes as needed without causing a conflict. The retries generate new\nhistorical rows with new values for the row_start and row_end columns.\n\nTransaction-Precise History in InnoDB\n-------------------------------------\n\nA point in time when a row was inserted or deleted does not necessarily mean\nthat a change became visible at the same moment. With transactional tables, a\nrow might have been inserted in a long transaction, and became visible hours\nafter it was inserted.\n\nFor some applications — for example, when doing data analytics on one-year-old\ndata — this distinction does not matter much. For others — forensic analysis —\nit might be crucial.\n\nMariaDB supports transaction-precise history (only for the InnoDB storage\nengine) that allows seeing the data exactly as it would\'ve been seen by a new\nconnection doing a SELECT at the specified point in time — rows inserted\nbefore that point, but committed after will not be shown.\n\nTo use transaction-precise history, InnoDB needs to remember not timestamps,\nbut transaction identifier per row. This is done by creating generated columns\nas BIGINT UNSIGNED, not TIMESTAMP(6):\n\nCREATE TABLE t(\n x INT,\n start_trxid BIGINT UNSIGNED GENERATED ALWAYS AS ROW START,\n end_trxid BIGINT UNSIGNED GENERATED ALWAYS AS ROW END,\n PERIOD FOR SYSTEM_TIME(start_trxid, end_trxid)\n) WITH SYSTEM VERSIONING;\n\nThese columns must be specified explicitly, but they can be made INVISIBLE to\navoid cluttering SELECT * output.\n\nWhen one uses transaction-precise history, one can optionally use transaction\nidentifiers in the FOR SYSTEM_TIME clause:\n\nSELECT * FROM t FOR SYSTEM_TIME AS OF TRANSACTION 12345;\n\nThis will show the data, exactly as it was seen by the transaction with the\nidentifier 12345.\n\nStoring the History Separately\n------------------------------\n\nWhen the history is stored together with the current data, it increases the\nsize of the table, so current data queries — table scans and index searches —\nwill take more time, because they will need to skip over historical data. If\nmost queries on that table use only current data, it might make sense to store\nthe history separately, to reduce the overhead from versioning.\n\nThis is done by partitioning the table by SYSTEM_TIME. Because of the\npartition pruning optimization, all current data queries will only access one\npartition, the one that stores current data.\n\nThis example shows how to create such a partitioned table:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME (\n PARTITION p_hist HISTORY,\n PARTITION p_cur CURRENT\n );\n\nIn this example all history will be stored in the partition p_hist while all\ncurrent data will be in the partition p_cur. The table must have exactly one\ncurrent partition and at least one historical partition.\n\nPartitioning by SYSTEM_TIME also supports automatic partition rotation. One\ncan rotate historical partitions by time or by size. This example shows how to\nrotate partitions by size:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME LIMIT 100000 (\n PARTITION p0 HISTORY,\n PARTITION p1 HISTORY,\n PARTITION pcur CURRENT\n );\n\nMariaDB will start writing history rows into partition p0, and when it reaches\na size of 100000 rows, MariaDB will switch to partition p1. There are only two\nhistorical partitions, so when p1 overflows, MariaDB will issue a warning, but\nwill continue writing into it.\n\nSimilarly, one can rotate partitions by time:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 WEEK (\n PARTITION p0 HISTORY,\n PARTITION p1 HISTORY,\n PARTITION p2 HISTORY,\n PARTITION pcur CURRENT\n );\n\nThis means that the history for the first week after the table was created\nwill be stored in p0. The history for the second week — in p1, and all later\nhistory will go into p2. One can see the exact rotation time for each\npartition in the INFORMATION_SCHEMA.PARTITIONS table.\n\nIt is possible to combine partitioning by SYSTEM_TIME and subpartitions:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME\n SUBPARTITION BY KEY (x)\n SUBPARTITIONS 4 (\n PARTITION ph HISTORY,\n PARTITION pc CURRENT\n );\n\nDefault Partitions\n------------------\n\nMariaDB starting with 10.5.0\n----------------------------\nSince partitioning by current and historical data is such a typical usecase,\nfrom MariaDB 10.5, it is possible to use a simplified statement to do so. For\nexample, instead of\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING \n PARTITION BY SYSTEM_TIME (\n PARTITION p0 HISTORY,\n PARTITION pn CURRENT\n);\n\nyou can use\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING \n PARTITION BY SYSTEM_TIME;\n\nYou can also specify the number of partitions, which is useful if you want to\nrotate history by time, for example:\n\nCREATE TABLE t (x INT) WITH SYSTEM VERSIONING \n PARTITION BY SYSTEM_TIME\n INTERVAL 1 MONTH\n PARTITIONS 12;\n\nSpecifying the number of partitions without specifying a rotation condition\nwill result in a warning:\n\nCREATE OR REPLACE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME PARTITIONS 12;\nQuery OK, 0 rows affected, 1 warning (0.518 sec)\n\nWarning (Code 4115): Maybe missing parameters: no rotation condition for\nmultiple HISTORY partitions.\n\nwhile specifying only 1 partition will result in an error:\n\nCREATE OR REPLACE TABLE t (x INT) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME PARTITIONS 1;\nERROR 4128 (HY000): Wrong partitions for `t`: must have at least one HISTORY\nand exactly one last CURRENT\n\nAutomatically Creating Partitions\n---------------------------------\n\nMariaDB starting with 10.9.1\n----------------------------\nFrom MariaDB 10.9.1, the AUTO keyword can be used to automatically create\nhistory partitions.\n\nFor example\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO;\n','','https://mariadb.com/kb/en/system-versioned-tables/'); +update help_topic set description = CONCAT(description, '\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 MONTH\n STARTS \'2021-01-01 00:00:00\' AUTO PARTITIONS 12;\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME LIMIT 1000 AUTO;\n\nOr with explicit partitions:\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO\n (PARTITION p0 HISTORY, PARTITION pn CURRENT);\n\nTo disable or enable auto-creation one can use ALTER TABLE by adding or\nremoving AUTO from the partitioning specification:\n\nCREATE TABLE t1 (x int) WITH SYSTEM VERSIONING\n PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO;\n\n# Disables auto-creation:\nALTER TABLE t1 PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR;\n\n# Enables auto-creation:\nALTER TABLE t1 PARTITION BY SYSTEM_TIME INTERVAL 1 HOUR AUTO;\n\nIf the rest of the partitioning specification is identical to CREATE TABLE, no\nrepartitioning will be done (for details see MDEV-27328).\n\nRemoving Old History\n--------------------\n\nBecause it stores all the history, a system-versioned table might grow very\nlarge over time. There are many options to trim down the space and remove the\nold history.\n\nOne can completely drop the versioning from the table and add it back again,\nthis will delete all the history:\n\nALTER TABLE t DROP SYSTEM VERSIONING;\nALTER TABLE t ADD SYSTEM VERSIONING;\n\nIt might be a rather time-consuming operation, though, as the table will need\nto be rebuilt, possibly twice (depending on the storage engine).\n\nAnother option would be to use partitioning and drop some of historical\npartitions:\n\nALTER TABLE t DROP PARTITION p0;\n\nNote, that one cannot drop a current partition or the only historical\npartition.\n\nAnd the third option; one can use a variant of the DELETE statement to prune\nthe history:\n\nDELETE HISTORY FROM t;\n\nor only old history up to a specific point in time:\n\nDELETE HISTORY FROM t BEFORE SYSTEM_TIME \'2016-10-09 08:07:06\';\n\nor to a specific transaction (with BEFORE SYSTEM_TIME TRANSACTION xxx).\n\nTo protect the integrity of the history, this statement requires a special\nDELETE HISTORY privilege.\n\nCurrently, using the DELETE HISTORY statement with a BEFORE SYSTEM_TIME\ngreater than the ROW_END of the active records (as a TIMESTAMP, this has a\nmaximum value of \'2038-01-19 03:14:07\' UTC) will result in the historical\nrecords being dropped, and the active records being deleted and moved to\nhistory. See MDEV-25468.\n\nPrior to MariaDB 10.4.5, the TRUNCATE TABLE statement drops all historical\nrecords from a system-versioned-table.\n\nFrom MariaDB 10.4.5, historic data is protected from TRUNCATE statements, as\nper the SQL standard, and an Error 4137 is instead raised:\n\nTRUNCATE t;\nERROR 4137 (HY000): System-versioned tables do not support TRUNCATE TABLE\n\nExcluding Columns From Versioning\n---------------------------------\n\nAnother MariaDB extension allows to version only a subset of columns in a\ntable. This is useful, for example, if you have a table with user information\nthat should be versioned, but one column is, let\'s say, a login counter that\nis incremented often and is not interesting to version. Such a column can be\nexcluded from versioning by declaring it WITHOUT VERSIONING\n\nCREATE TABLE t (\n x INT,\n y INT WITHOUT SYSTEM VERSIONING\n) WITH SYSTEM VERSIONING;\n\nA column can also be declared WITH VERSIONING, that will automatically make\nthe table versioned. The statement below is equivalent to the one above:\n\nCREATE TABLE t (\n x INT WITH SYSTEM VERSIONING,\n y INT\n);\n\nChanges in other sections: https://mariadb.com/kb/en/create-table/\nhttps://mariadb.com/kb/en/alter-table/ https://mariadb.com/kb/en/join-syntax/\nhttps://mariadb.com/kb/en/partitioning-types-overview/\nhttps://mariadb.com/kb/en/date-and-time-units/\nhttps://mariadb.com/kb/en/delete/ https://mariadb.com/kb/en/grant/\n\nthey all reference back to this page\n\nAlso, TODO:\n\n* limitations (size, speed, adding history to unique not nullable columns)\n\nSystem Variables\n----------------\n\nThere are a number of system variables related to system-versioned tables:\n\nsystem_versioning_alter_history\n-------------------------------\n\n* Description: SQL:2011 does not allow ALTER TABLE on system-versioned tables.\nWhen this variable is set to ERROR, an attempt to alter a system-versioned\ntable will result in an error. When this variable is set to KEEP, ALTER TABLE\nwill be allowed, but the history will become incorrect — querying historical\ndata will show the new table structure. This mode is still useful, for\nexample, when adding new columns to a table. Note that if historical data\ncontains or would contain nulls, attempting to ALTER these columns to be NOT\nNULL will return an error (or warning if strict_mode is not set).\n* Commandline: --system-versioning-alter-history=value\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Enum\n* Default Value: ERROR\n* Valid Values: ERROR, KEEP\n\nsystem_versioning_asof\n----------------------\n\n* Description: If set to a specific timestamp value, an implicit FOR\nSYSTEM_TIME AS OF clause will be applied to all queries. This is useful if one\nwants to do many queries for history at the specific point in time. Set it to\nDEFAULT to restore the default behavior. Has no effect on DML, so queries such\nas INSERT .. SELECT and REPLACE .. SELECT need to state AS OF explicitly.\n* Commandline: None\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Varchar\n* Default Value: DEFAULT\n\nsystem_versioning_innodb_algorithm_simple\n-----------------------------------------\n\n* Description: Never fully implemented and removed in the following release.\n* Commandline: --system-versioning-innodb-algorithm-simple[={0|1}]\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Boolean\n* Default Value: ON\n* Introduced: MariaDB 10.3.4\n* Removed: MariaDB 10.3.5\n\nsystem_versioning_insert_history\n--------------------------------\n\n* Description: Allows direct inserts into ROW_START and ROW_END columns if\nsecure_timestamp allows changing timestamp.\n* Commandline: --system-versioning-insert-history[={0|1}]\n* Scope: Global, Session\n* Dynamic: Yes\n* Type: Boolean\n* Default Value: OFF\n* Introduced: MariaDB 10.11.0\n\nLimitations\n-----------\n\n* Versioning clauses can not be applied to generated (virtual and persistent)\ncolumns.\n* Before MariaDB 10.11, mariadb-dump did not read historical rows from\nversioned tables, and so historical data would not be backed up. Also, a\nrestore of the timestamps would not be possible as they cannot be defined by\nan insert/a user. From MariaDB 10.11, use the -H or --dump-history options to\ninclude the history.\n\nURL: https://mariadb.com/kb/en/system-versioned-tables/') WHERE help_topic_id = 808; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (809,45,'Application-Time Periods','MariaDB starting with 10.4.3\n----------------------------\nSupport for application-time period-versioning was added in MariaDB 10.4.3.\n\nExtending system-versioned tables, MariaDB 10.4 supports application-time\nperiod tables. Time periods are defined by a range between two temporal\ncolumns. The columns must be of the same temporal data type, i.e. DATE,\nTIMESTAMP or DATETIME (TIME and YEAR are not supported), and of the same width.\n\nUsing time periods implicitly defines the two columns as NOT NULL. It also\nadds a constraint to check whether the first value is less than the second\nvalue. The constraint is invisible to SHOW CREATE TABLE statements. The name\nof this constraint is prefixed by the time period name, to avoid conflict with\nother constraints.\n\nCreating Tables with Time Periods\n---------------------------------\n\nTo create a table with a time period, use a CREATE TABLE statement with the\nPERIOD table option.\n\nCREATE TABLE t1(\n name VARCHAR(50),\n date_1 DATE,\n date_2 DATE,\n PERIOD FOR date_period(date_1, date_2));\n\nThis creates a table with a time_period period and populates the table with\nsome basic temporal values.\n\nExamples are available in the MariaDB Server source code, at\nmysql-test/suite/period/r/create.result.\n\nAdding and Removing Time Periods\n--------------------------------\n\nThe ALTER TABLE statement now supports syntax for adding and removing time\nperiods from a table. To add a period, use the ADD PERIOD clause.\n\nFor example:\n\nCREATE OR REPLACE TABLE rooms (\n room_number INT,\n guest_name VARCHAR(255),\n checkin DATE,\n checkout DATE\n );\n\nALTER TABLE rooms ADD PERIOD FOR p(checkin,checkout);\n\nTo remove a period, use the DROP PERIOD clause:\n\nALTER TABLE rooms DROP PERIOD FOR p;\n\nBoth ADD PERIOD and DROP PERIOD clauses include an option to handle whether\nthe period already exists:\n\nALTER TABLE rooms ADD PERIOD IF NOT EXISTS FOR p(checkin,checkout);\n\nALTER TABLE rooms DROP PERIOD IF EXISTS FOR p;\n\nDeletion by Portion\n-------------------\n\nYou can also remove rows that fall within certain time periods.\n\nWhen MariaDB executes a DELETE FOR PORTION statement, it removes the row:\n\n* When the row period falls completely within the delete period, it removes\nthe row.\n* When the row period overlaps the delete period, it shrinks the row, removing\nthe overlap from the first or second row period value.\n* When the delete period falls completely within the row period, it splits the\nrow into two rows. The first row runs from the starting row period to the\nstarting delete period. The second runs from the ending delete period to the\nending row period.\n\nTo test this, first populate the table with some data to operate on:\n\nCREATE TABLE t1(\n name VARCHAR(50),\n date_1 DATE,\n date_2 DATE,\n PERIOD FOR date_period(date_1, date_2));\n\nINSERT INTO t1 (name, date_1, date_2) VALUES\n (\'a\', \'1999-01-01\', \'2000-01-01\'),\n (\'b\', \'1999-01-01\', \'2018-12-12\'),\n (\'c\', \'1999-01-01\', \'2017-01-01\'),\n (\'d\', \'2017-01-01\', \'2019-01-01\');\n\nSELECT * FROM t1;\n+------+------------+------------+\n| name | date_1 | date_2 |\n+------+------------+------------+\n| a | 1999-01-01 | 2000-01-01 |\n| b | 1999-01-01 | 2018-12-12 |\n| c | 1999-01-01 | 2017-01-01 |\n| d | 2017-01-01 | 2019-01-01 |\n+------+------------+------------+\n\nThen, run the DELETE FOR PORTION statement:\n\nDELETE FROM t1\nFOR PORTION OF date_period\n FROM \'2001-01-01\' TO \'2018-01-01\';\nQuery OK, 3 rows affected (0.028 sec)\n\nSELECT * FROM t1 ORDER BY name;\n+------+------------+------------+\n| name | date_1 | date_2 |\n+------+------------+------------+\n| a | 1999-01-01 | 2000-01-01 |\n| b | 1999-01-01 | 2001-01-01 |\n| b | 2018-01-01 | 2018-12-12 |\n| c | 1999-01-01 | 2001-01-01 |\n| d | 2018-01-01 | 2019-01-01 |\n+------+------------+------------+\n\nHere:\n\n* a is unchanged, as the range falls entirely out of the specified portion to\nbe deleted.\n* b, with values ranging from 1999 to 2018, is split into two rows, 1999 to\n2000 and 2018-01 to 2018-12.\n* c, with values ranging from 1999 to 2017, where only the upper value falls\nwithin the portion to be deleted, has been shrunk to 1999 to 2001.\n* d, with values ranging from 2017 to 2019, where only the lower value falls\nwithin the portion to be deleted, has been shrunk to 2018 to 2019.\n\nThe DELETE FOR PORTION statement has the following restrictions\n\n* The FROM...TO clause must be constant\n* Multi-delete is not supported\n\nIf there are DELETE or INSERT triggers, it works as follows: any matched row\nis deleted, and then one or two rows are inserted. If the record is deleted\ncompletely, nothing is inserted.\n\nUpdating by Portion\n-------------------\n\nThe UPDATE syntax now supports UPDATE FOR PORTION, which modifies rows based\non their occurrence in a range:\n\nTo test it, first populate the table with some data:\n\nTRUNCATE t1;\n\nINSERT INTO t1 (name, date_1, date_2) VALUES\n (\'a\', \'1999-01-01\', \'2000-01-01\'),\n (\'b\', \'1999-01-01\', \'2018-12-12\'),\n (\'c\', \'1999-01-01\', \'2017-01-01\'),\n (\'d\', \'2017-01-01\', \'2019-01-01\');\n\nSELECT * FROM t1;\n+------+------------+------------+\n| name | date_1 | date_2 |\n+------+------------+------------+\n| a | 1999-01-01 | 2000-01-01 |\n| b | 1999-01-01 | 2018-12-12 |\n| c | 1999-01-01 | 2017-01-01 |\n| d | 2017-01-01 | 2019-01-01 |\n+------+------------+------------+\n\nThen run the update:\n\nUPDATE t1 FOR PORTION OF date_period\n FROM \'2000-01-01\' TO \'2018-01-01\'\nSET name = CONCAT(name,\'_original\');\n\nSELECT * FROM t1 ORDER BY name;\n+------------+------------+------------+\n| name | date_1 | date_2 |\n+------------+------------+------------+\n| a | 1999-01-01 | 2000-01-01 |\n| b | 1999-01-01 | 2000-01-01 |\n| b | 2018-01-01 | 2018-12-12 |\n| b_original | 2000-01-01 | 2018-01-01 |\n| c | 1999-01-01 | 2000-01-01 |\n| c_original | 2000-01-01 | 2017-01-01 |\n| d | 2018-01-01 | 2019-01-01 |\n| d_original | 2017-01-01 | 2018-01-01 |\n+------------+------------+------------+\n\n* a is unchanged, as the range falls entirely out of the specified portion to\nbe deleted.\n* b, with values ranging from 1999 to 2018, is split into two rows, 1999 to\n2000 and 2018-01 to 2018-12.\n* c, with values ranging from 1999 to 2017, where only the upper value falls\nwithin the portion to be deleted, has been shrunk to 1999 to 2001.\n* d, with values ranging from 2017 to 2019, where only the lower value falls\nwithin the portion to be deleted, has been shrunk to 2018 to 2019. \n* Original rows affected by the update have \"_original\" appended to the name.\n\nThe UPDATE FOR PORTION statement has the following limitations:\n\n* The operation cannot modify the two temporal columns used by the time period\n* The operation cannot reference period values in the SET expression\n* FROM...TO expressions must be constant\n\nWITHOUT OVERLAPS\n----------------\n\nMariaDB starting with 10.5.3\n----------------------------\nMariaDB 10.5 introduced a new clause, WITHOUT OVERLAPS, which allows one to\ncreate an index specifying that application time periods should not overlap.\n\nAn index constrained by WITHOUT OVERLAPS is required to be either a primary\nkey or a unique index.\n\nTake the following example, an application time period table for a booking\nsystem:\n\nCREATE OR REPLACE TABLE rooms (\n room_number INT,\n guest_name VARCHAR(255),\n checkin DATE,\n checkout DATE,\n PERIOD FOR p(checkin,checkout)\n );\n\nINSERT INTO rooms VALUES \n (1, \'Regina\', \'2020-10-01\', \'2020-10-03\'),\n (2, \'Cochise\', \'2020-10-02\', \'2020-10-05\'),\n (1, \'Nowell\', \'2020-10-03\', \'2020-10-07\'),\n (2, \'Eusebius\', \'2020-10-04\', \'2020-10-06\');\n\nOur system is not intended to permit overlapping bookings, so the fourth\nrecord above should not have been inserted. Using WITHOUT OVERLAPS in a unique\nindex (in this case based on a combination of room number and the application\ntime period) allows us to specify this constraint in the table definition.\n\nCREATE OR REPLACE TABLE rooms (\n room_number INT,\n guest_name VARCHAR(255),\n checkin DATE,\n checkout DATE,\n PERIOD FOR p(checkin,checkout),\n UNIQUE (room_number, p WITHOUT OVERLAPS)\n );\n\nINSERT INTO rooms VALUES \n (1, \'Regina\', \'2020-10-01\', \'2020-10-03\'),\n (2, \'Cochise\', \'2020-10-02\', \'2020-10-05\'),\n (1, \'Nowell\', \'2020-10-03\', \'2020-10-07\'),\n (2, \'Eusebius\', \'2020-10-04\', \'2020-10-06\');\nERROR 1062 (23000): Duplicate entry \'2-2020-10-06-2020-10-04\' for key\n\'room_number\'\n\nFurther Examples\n----------------\n\nThe implicit change from NULL to NOT NULL:\n\nCREATE TABLE `t2` (\n `id` int(11) DEFAULT NULL,\n `d1` datetime DEFAULT NULL,\n `d2` datetime DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=latin1;\n\nALTER TABLE t2 ADD PERIOD FOR p(d1,d2);\n\nSHOW CREATE TABLE t2\\G\n*************************** 1. row ***************************\n Table: t2\nCreate Table: CREATE TABLE `t2` (\n `id` int(11) DEFAULT NULL,\n `d1` datetime NOT NULL,\n `d2` datetime NOT NULL,\n PERIOD FOR `p` (`d1`, `d2`)\n) ENGINE=InnoDB DEFAULT CHARSET=latin1\n\nDue to this constraint, trying to add a time period where null data already\nexists will fail.\n\nCREATE OR REPLACE TABLE `t2` (\n `id` int(11) DEFAULT NULL,\n `d1` datetime DEFAULT NULL,\n `d2` datetime DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=latin1;\n\nINSERT INTO t2(id) VALUES(1);\n\nALTER TABLE t2 ADD PERIOD FOR p(d1,d2);\nERROR 1265 (01000): Data truncated for column \'d1\' at row 1\n\nURL: https://mariadb.com/kb/en/application-time-periods/','','https://mariadb.com/kb/en/application-time-periods/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (810,45,'Bitemporal Tables','MariaDB starting with 10.4.3\n----------------------------\nBitemporal tables are tables that use versioning both at the system and\napplication-time period levels.\n\nUsing Bitemporal Tables\n-----------------------\n\nTo create a bitemporal table, use:\n\nCREATE TABLE test.t3 (\n date_1 DATE,\n date_2 DATE,\n row_start TIMESTAMP(6) AS ROW START INVISIBLE,\n row_end TIMESTAMP(6) AS ROW END INVISIBLE,\n PERIOD FOR application_time(date_1, date_2),\n PERIOD FOR system_time(row_start, row_end))\nWITH SYSTEM VERSIONING;\n\nNote that, while system_time here is also a time period, it cannot be used in\nDELETE FOR PORTION or UPDATE FOR PORTION statements.\n\nDELETE FROM test.t3 \nFOR PORTION OF system_time \n FROM \'2000-01-01\' TO \'2018-01-01\';\nERROR 42000: You have an error in your SQL syntax; check the manual that\ncorresponds \n to your MariaDB server version for the right syntax to use near\n \'of system_time from \'2000-01-01\' to \'2018-01-01\'\' at line 1\n\nURL: https://mariadb.com/kb/en/bitemporal-tables/','','https://mariadb.com/kb/en/bitemporal-tables/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (811,46,'ST_AsGeoJSON','Syntax\n------\n\nST_AsGeoJSON(g[, max_decimals[, options]])\n\nDescription\n-----------\n\nReturns the given geometry g as a GeoJSON element. The optional max_decimals\nlimits the maximum number of decimals displayed.\n\nThe optional options flag can be set to 1 to add a bounding box to the output.\n\nExamples\n--------\n\nSELECT ST_AsGeoJSON(ST_GeomFromText(\'POINT(5.3 7.2)\'));\n+-------------------------------------------------+\n| ST_AsGeoJSON(ST_GeomFromText(\'POINT(5.3 7.2)\')) |\n+-------------------------------------------------+\n| {\"type\": \"Point\", \"coordinates\": [5.3, 7.2]} |\n+-------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/geojson-st_asgeojson/','','https://mariadb.com/kb/en/geojson-st_asgeojson/'); @@ -930,9 +930,9 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (816,48,'Modulo Operator (%)','Syntax\n------\n\nN % M\n\nDescription\n-----------\n\nModulo operator. Returns the remainder of N divided by M. See also MOD.\n\nExamples\n--------\n\nSELECT 1042 % 50;\n+-----------+\n| 1042 % 50 |\n+-----------+\n| 42 |\n+-----------+\n\nURL: https://mariadb.com/kb/en/modulo-operator/','','https://mariadb.com/kb/en/modulo-operator/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (817,48,'Multiplication Operator (*)','Syntax\n------\n\n*\n\nDescription\n-----------\n\nMultiplication operator.\n\nExamples\n--------\n\nSELECT 7*6;\n+-----+\n| 7*6 |\n+-----+\n| 42 |\n+-----+\n\nSELECT 1234567890*9876543210;\n+-----------------------+\n| 1234567890*9876543210 |\n+-----------------------+\n| -6253480962446024716 |\n+-----------------------+\n\nSELECT 18014398509481984*18014398509481984.0;\n+---------------------------------------+\n| 18014398509481984*18014398509481984.0 |\n+---------------------------------------+\n| 324518553658426726783156020576256.0 |\n+---------------------------------------+\n\nSELECT 18014398509481984*18014398509481984;\n+-------------------------------------+\n| 18014398509481984*18014398509481984 |\n+-------------------------------------+\n| 0 |\n+-------------------------------------+\n\nURL: https://mariadb.com/kb/en/multiplication-operator/','','https://mariadb.com/kb/en/multiplication-operator/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (818,48,'Subtraction Operator (-)','Syntax\n------\n\n-\n\nDescription\n-----------\n\nSubtraction. The operator is also used as the unary minus for changing sign.\n\nIf both operands are integers, the result is calculated with BIGINT precision.\nIf either integer is unsigned, the result is also an unsigned integer, unless\nthe NO_UNSIGNED_SUBTRACTION SQL_MODE is enabled, in which case the result is\nalways signed.\n\nFor real or string operands, the operand with the highest precision determines\nthe result precision.\n\nExamples\n--------\n\nSELECT 96-9;\n+------+\n| 96-9 |\n+------+\n| 87 |\n+------+\n\nSELECT 15-17;\n+-------+\n| 15-17 |\n+-------+\n| -2 |\n+-------+\n\nSELECT 3.66 + 1.333;\n+--------------+\n| 3.66 + 1.333 |\n+--------------+\n| 4.993 |\n+--------------+\n\nUnary minus:\n\nSELECT - (3+5);\n+---------+\n| - (3+5) |\n+---------+\n| -8 |\n+---------+\n\nURL: https://mariadb.com/kb/en/subtraction-operator-/','','https://mariadb.com/kb/en/subtraction-operator-/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (819,49,'CHANGE MASTER TO','The terms master and slave have historically been used in replication, but the\nterms terms primary and replica are now preferred. The old terms are used\nstill used in parts of the documentation, and in MariaDB commands, although\nMariaDB 10.5 has begun the process of renaming. The documentation process is\nongoing. See MDEV-18777 to follow progress on this effort.\n\nSyntax\n------\n\nCHANGE MASTER [\'connection_name\'] TO master_def [, master_def] ... \n [FOR CHANNEL \'channel_name\']\n\nmaster_def:\n MASTER_BIND = \'interface_name\'\n | MASTER_HOST = \'host_name\'\n | MASTER_USER = \'user_name\'\n | MASTER_PASSWORD = \'password\'\n | MASTER_PORT = port_num\n | MASTER_CONNECT_RETRY = interval\n | MASTER_HEARTBEAT_PERIOD = interval\n | MASTER_LOG_FILE = \'master_log_name\'\n | MASTER_LOG_POS = master_log_pos\n | RELAY_LOG_FILE = \'relay_log_name\'\n | RELAY_LOG_POS = relay_log_pos\n | MASTER_DELAY = interval\n | MASTER_SSL = {0|1}\n | MASTER_SSL_CA = \'ca_file_name\'\n | MASTER_SSL_CAPATH = \'ca_directory_name\'\n | MASTER_SSL_CERT = \'cert_file_name\'\n | MASTER_SSL_CRL = \'crl_file_name\'\n | MASTER_SSL_CRLPATH = \'crl_directory_name\'\n | MASTER_SSL_KEY = \'key_file_name\'\n | MASTER_SSL_CIPHER = \'cipher_list\'\n | MASTER_SSL_VERIFY_SERVER_CERT = {0|1}\n | MASTER_USE_GTID = {current_pos|slave_pos|no}\n | MASTER_DEMOTE_TO_SLAVE = bool\n | IGNORE_SERVER_IDS = (server_id_list)\n | DO_DOMAIN_IDS = ([N,..])\n | IGNORE_DOMAIN_IDS = ([N,..])\n\nDescription\n-----------\n\nThe CHANGE MASTER statement sets the options that a replica uses to connect to\nand replicate from a primary.\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nto using the channel_name directly after CHANGE MASTER.\n\nMulti-Source Replication\n------------------------\n\nIf you are using multi-source replication, then you need to specify a\nconnection name when you execute CHANGE MASTER. There are two ways to do this:\n\n* Setting the default_master_connection system variable prior to executing\nCHANGE MASTER.\n* Setting the connection_name parameter when executing CHANGE MASTER.\n\ndefault_master_connection\n-------------------------\n\nSET default_master_connection = \'gandalf\';\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nconnection_name\n---------------\n\nSTOP SLAVE \'gandalf\';\nCHANGE MASTER \'gandalf\' TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE \'gandalf\';\n\nOptions\n-------\n\nConnection Options\n------------------\n\nMASTER_USER\n-----------\n\nThe MASTER_USER option for CHANGE MASTER defines the user account that the\nreplica will use to connect to the primary.\n\nThis user account will need the REPLICATION SLAVE privilege (or, from MariaDB\n10.5.1, the REPLICATION REPLICA on the primary.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_USER=\'repl\',\n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nThe maximum length of the MASTER_USER string is 96 characters until MariaDB\n10.5, and 128 characters from MariaDB 10.6.\n\nMASTER_PASSWORD\n---------------\n\nThe MASTER_USER option for CHANGE MASTER defines the password that the replica\nwill use to connect to the primary as the user account defined by the\nMASTER_USER option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nThe maximum length of the MASTER_PASSWORD string is 32 characters.\n\nMASTER_HOST\n-----------\n\nThe MASTER_HOST option for CHANGE MASTER defines the hostname or IP address of\nthe primary.\n\nIf you set the value of the MASTER_HOST option to the empty string, then that\nis not the same as not setting the option\'s value at all. If you set the value\nof the MASTER_HOST option to the empty string, then the CHANGE MASTER command\nwill fail with an error. In MariaDB 5.3 and before, if you set the value of\nthe MASTER_HOST option to the empty string, then the CHANGE MASTER command\nwould succeed, but the subsequent START SLAVE command would fail.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_HOST=\'dbserver1.example.com\',\n MASTER_USER=\'repl\',\n MASTER_PASSWORD=\'new3cret\',\n MASTER_USE_GTID=slave_pos;\nSTART SLAVE;\n\nIf you set the value of the MASTER_HOST option in a CHANGE MASTER command,\nthen the replica assumes that the primary is different from before, even if\nyou set the value of this option to the same value it had previously. In this\nscenario, the replica will consider the old values for the primary\'s binary\nlog file name and position to be invalid for the new primary. As a side\neffect, if you do not explicitly set the values of the MASTER_LOG_FILE and\nMASTER_LOG_POS options in the statement, then the statement will be implicitly\nappended with MASTER_LOG_FILE=\'\' and MASTER_LOG_POS=4. However, if you enable\nGTID mode for replication by setting the MASTER_USE_GTID option to some value\nother than no in the statement, then these values will effectively be ignored\nanyway.\n\nReplicas cannot connect to primaries using Unix socket files or Windows named\npipes. The replica must connect to the primary using TCP/IP.\n\nThe maximum length of the MASTER_HOST string is 60 characters until MariaDB\n10.5, and 255 characters from MariaDB 10.6.\n\nMASTER_PORT\n-----------\n\nThe MASTER_PORT option for CHANGE MASTER defines the TCP/IP port of the\nprimary.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_HOST=\'dbserver1.example.com\',\n MASTER_PORT=3307,\n MASTER_USER=\'repl\',\n MASTER_PASSWORD=\'new3cret\',\n MASTER_USE_GTID=slave_pos;\nSTART SLAVE;\n\nIf you set the value of the MASTER_PORT option in a CHANGE MASTER command,\nthen the replica assumes that the primary is different from before, even if\nyou set the value of this option to the same value it had previously. In this\nscenario, the replica will consider the old values for the primary\'s binary\nlog file name and position to be invalid for the new primary. As a side\neffect, if you do not explicitly set the values of the MASTER_LOG_FILE and\nMASTER_LOG_POS options in the statement, then the statement will be implicitly\nappended with MASTER_LOG_FILE=\'\' and MASTER_LOG_POS=4. However, if you enable\nGTID mode for replication by setting the MASTER_USE_GTID option to some value\nother than no in the statement, then these values will effectively be ignored\nanyway.\n\nReplicas cannot connect to primaries using Unix socket files or Windows named\npipes. The replica must connect to the primary using TCP/IP.\n\nMASTER_CONNECT_RETRY\n--------------------\n\nThe MASTER_CONNECT_RETRY option for CHANGE MASTER defines how many seconds\nthat the replica will wait between connection retries. The default is 60.\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_CONNECT_RETRY=20;\nSTART SLAVE;\n\nThe number of connection attempts is limited by the master_retry_count option.\nIt can be set either on the command-line or in a server option group in an\noption file prior to starting up the server. For example:\n\n[mariadb]\n...\nmaster_retry_count=4294967295\n\nMASTER_BIND\n-----------\n\nThe MASTER_BIND option for CHANGE MASTER is only supported by MySQL 5.6.2 and\nlater and by MySQL NDB Cluster 7.3.1 and later. This option is not supported\nby MariaDB. See MDEV-19248 for more information.\n\nThe MASTER_BIND option for CHANGE MASTER can be used on replicas that have\nmultiple network interfaces to choose which network interface the replica will\nuse to connect to the primary.\n\nMASTER_HEARTBEAT_PERIOD\n-----------------------\n\nThe MASTER_HEARTBEAT_PERIOD option for CHANGE MASTER can be used to set the\ninterval in seconds between replication heartbeats. Whenever the primary\'s\nbinary log is updated with an event, the waiting period for the next heartbeat\nis reset.\n\nThis option\'s interval argument has the following characteristics:\n\n* It is a decimal value with a range of 0 to 4294967 seconds.\n* It has a resolution of hundredths of a second.\n* Its smallest valid non-zero value is 0.001.\n* Its default value is the value of the slave_net_timeout system variable\ndivided by 2.\n* If it\'s set to 0, then heartbeats are disabled.\n\nHeartbeats are sent by the primary only if there are no unsent events in the\nbinary log file for a period longer than the interval.\n\nIf the RESET SLAVE statement is executed, then the heartbeat interval is reset\nto the default.\n\nIf the slave_net_timeout system variable is set to a value that is lower than\nthe current heartbeat interval, then a warning will be issued.\n\nTLS Options\n-----------\n\nThe TLS options are used for providing information about TLS. The options can\nbe set even on replicas that are compiled without TLS support. The TLS options\nare saved to either the default master.info file or the file that is\nconfigured by the master_info_file option, but these TLS options are ignored\nunless the replica supports TLS.\n\nSee Replication with Secure Connections for more information.\n\nMASTER_SSL\n----------\n\nThe MASTER_SSL option for CHANGE MASTER tells the replica whether to force TLS\nfor the connection. The valid values are 0 or 1.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL=1;\nSTART SLAVE;\n\nMASTER_SSL_CA\n-------------\n\nThe MASTER_SSL_CA option for CHANGE MASTER defines a path to a PEM file that\nshould contain one or more X509 certificates for trusted Certificate\nAuthorities (CAs) to use for TLS. This option requires that you use the\nabsolute path, not a relative path. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Authorities (CAs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CA string is 511 characters.\n\nMASTER_SSL_CAPATH\n-----------------\n\nThe MASTER_SSL_CAPATH option for CHANGE MASTER defines a path to a directory\nthat contains one or more PEM files that should each contain one X509\ncertificate for a trusted Certificate Authority (CA) to use for TLS. This\noption requires that you use the absolute path, not a relative path. The\ndirectory specified by this option needs to be run through the openssl rehash\ncommand. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CAPATH=\'/etc/my.cnf.d/certificates/ca/\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Authorities (CAs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CA_PATH string is 511 characters.\n\nMASTER_SSL_CERT\n---------------\n\nThe MASTER_SSL_CERT option for CHANGE MASTER defines a path to the X509\ncertificate file to use for TLS. This option requires that you use the\nabsolute path, not a relative path. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nThe maximum length of MASTER_SSL_CERT string is 511 characters.\n\nMASTER_SSL_CRL\n--------------\n\nThe MASTER_SSL_CRL option for CHANGE MASTER defines a path to a PEM file that\nshould contain one or more revoked X509 certificates to use for TLS. This\noption requires that you use the absolute path, not a relative path.\n\nThis option is only supported if the server was built with OpenSSL. If the\nserver was built with yaSSL, then this option is not supported. See TLS and\nCryptography Libraries Used by MariaDB for more information about which\nlibraries are used on which platforms.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1,\n MASTER_SSL_CRL=\'/etc/my.cnf.d/certificates/crl.pem\';\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Revocation Lists (CRLs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CRL string is 511 characters.\n\nMASTER_SSL_CRLPATH\n------------------\n\nThe MASTER_SSL_CRLPATH option for CHANGE MASTER defines a path to a directory\nthat contains one or more PEM files that should each contain one revoked X509\ncertificate to use for TLS. This option requires that you use the absolute\npath, not a relative path. The directory specified by this variable needs to\nbe run through the openssl rehash command.\n\nThis option is only supported if the server was built with OpenSSL. If the\nserver was built with yaSSL, then this option is not supported. See TLS and\nCryptography Libraries Used by MariaDB for more information about which\nlibraries are used on which platforms.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1,\n MASTER_SSL_CRLPATH=\'/etc/my.cnf.d/certificates/crl/\';\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Revocation Lists (CRLs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CRL_PATH string is 511 characters.\n\nMASTER_SSL_KEY\n--------------\n\nThe MASTER_SSL_KEY option for CHANGE MASTER defines a path to a private key\nfile to use for TLS. This option requires that you use the absolute path, not\na relative path. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nThe maximum length of MASTER_SSL_KEY string is 511 characters.\n\nMASTER_SSL_CIPHER\n-----------------\n\nThe MASTER_SSL_CIPHER option for CHANGE MASTER defines the list of permitted\nciphers or cipher suites to use for TLS. Besides cipher names, if MariaDB was','','https://mariadb.com/kb/en/change-master-to/'); -update help_topic set description = CONCAT(description, '\ncompiled with OpenSSL, this option could be set to \"SSLv3\" or \"TLSv1.2\" to\nallow all SSLv3 or all TLSv1.2 ciphers. Note that the TLSv1.3 ciphers cannot\nbe excluded when using OpenSSL, even by using this option. See Using TLSv1.3\nfor details. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1,\n MASTER_SSL_CIPHER=\'TLSv1.2\';\nSTART SLAVE;\n\nThe maximum length of MASTER_SSL_CIPHER string is 511 characters.\n\nMASTER_SSL_VERIFY_SERVER_CERT\n-----------------------------\n\nThe MASTER_SSL_VERIFY_SERVER_CERT option for CHANGE MASTER enables server\ncertificate verification. This option is disabled by default.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nSee Secure Connections Overview: Server Certificate Verification for more\ninformation.\n\nBinary Log Options\n------------------\n\nThese options are related to the binary log position on the primary.\n\nMASTER_LOG_FILE\n---------------\n\nThe MASTER_LOG_FILE option for CHANGE MASTER can be used along with\nMASTER_LOG_POS to specify the coordinates at which the replica\'s I/O thread\nshould begin reading from the primary\'s binary logs the next time the thread\nstarts.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4;\nSTART SLAVE;\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options cannot be specified if the\nRELAY_LOG_FILE and RELAY_LOG_POS options were also specified.\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options are effectively ignored if you\nenable GTID mode for replication by setting the MASTER_USE_GTID option to some\nvalue other than no in the statement.\n\nMASTER_LOG_POS\n--------------\n\nThe MASTER_LOG_POS option for CHANGE MASTER can be used along with\nMASTER_LOG_FILE to specify the coordinates at which the replica\'s I/O thread\nshould begin reading from the primary\'s binary logs the next time the thread\nstarts.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4;\nSTART SLAVE;\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options cannot be specified if the\nRELAY_LOG_FILE and RELAY_LOG_POS options were also specified.\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options are effectively ignored if you\nenable GTID mode for replication by setting the MASTER_USE_GTID option to some\nvalue other than no in the statement.\n\nRelay Log Options\n-----------------\n\nThese options are related to the relay log position on the replica.\n\nRELAY_LOG_FILE\n--------------\n\nThe RELAY_LOG_FILE option for CHANGE MASTER can be used along with the\nRELAY_LOG_POS option to specify the coordinates at which the replica\'s SQL\nthread should begin reading from the relay log the next time the thread starts.\n\nThe CHANGE MASTER statement usually deletes all relay log files. However, if\nthe RELAY_LOG_FILE and/or RELAY_LOG_POS options are specified, then existing\nrelay log files are kept.\n\nWhen you want to change the relay log position, you only need to stop the\nreplica\'s SQL thread. The replica\'s I/O thread can continue running. The STOP\nSLAVE and START SLAVE statements support the SQL_THREAD option for this\nscenario. For example:\n\nSTOP SLAVE SQL_THREAD;\nCHANGE MASTER TO\n RELAY_LOG_FILE=\'slave-relay-bin.006\',\n RELAY_LOG_POS=4025;\nSTART SLAVE SQL_THREAD;\n\nWhen the value of this option is changed, the metadata about the replica\'s SQL\nthread\'s position in the relay logs will also be changed in the relay-log.info\nfile or the file that is configured by the relay_log_info_file system variable.\n\nThe RELAY_LOG_FILE and RELAY_LOG_POS options cannot be specified if the\nMASTER_LOG_FILE and MASTER_LOG_POS options were also specified.\n\nRELAY_LOG_POS\n-------------\n\nThe RELAY_LOG_POS option for CHANGE MASTER can be used along with the\nRELAY_LOG_FILE option to specify the coordinates at which the replica\'s SQL\nthread should begin reading from the relay log the next time the thread starts.\n\nThe CHANGE MASTER statement usually deletes all relay log files. However, if\nthe RELAY_LOG_FILE and/or RELAY_LOG_POS options are specified, then existing\nrelay log files are kept.\n\nWhen you want to change the relay log position, you only need to stop the\nreplica\'s SQL thread. The replica\'s I/O thread can continue running. The STOP\nSLAVE and START SLAVE statements support the SQL_THREAD option for this\nscenario. For example:\n\nSTOP SLAVE SQL_THREAD;\nCHANGE MASTER TO\n RELAY_LOG_FILE=\'slave-relay-bin.006\',\n RELAY_LOG_POS=4025;\nSTART SLAVE SQL_THREAD;\n\nWhen the value of this option is changed, the metadata about the replica\'s SQL\nthread\'s position in the relay logs will also be changed in the relay-log.info\nfile or the file that is configured by the relay_log_info_file system variable.\n\nThe RELAY_LOG_FILE and RELAY_LOG_POS options cannot be specified if the\nMASTER_LOG_FILE and MASTER_LOG_POS options were also specified.\n\nGTID Options\n------------\n\nMASTER_USE_GTID\n---------------\n\nThe MASTER_USE_GTID option for CHANGE MASTER can be used to configure the\nreplica to use the global transaction ID (GTID) when connecting to a primary.\nThe possible values are:\n\n* current_pos - Replicate in GTID mode and use gtid_current_pos as the\nposition to start downloading transactions from the primary. Deprecated from\nMariaDB 10.10. Using to transition to primary can break the replication state\nif the replica executes local transactions due to actively updating\ngtid_current_pos with gtid_binlog_pos and gtid_slave_pos. Use the new, safe,\nMASTER_DEMOTE_TO_SLAVE= option instead.\n* slave_pos - Replicate in GTID mode and use gtid_slave_pos as the position to\nstart downloading transactions from the primary. From MariaDB 10.5.1,\nreplica_pos is an alias for slave_pos.\n* no - Don\'t replicate in GTID mode.\n\nMASTER_DEMOTE_TO_SLAVE\n----------------------\n\nMariaDB starting with 10.10\n---------------------------\nUsed to transition a primary to become a replica. Replaces the old\nMASTER_USE_GTID=current_pos with a safe alternative by forcing users to set\nUsing_Gtid=Slave_Pos and merging gtid_binlog_pos into gtid_slave_pos once at\nCHANGE MASTER TO time. If gtid_slave_pos is more recent than gtid_binlog_pos\n(as in the case of chain replication), the replication state should be\npreserved.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_USE_GTID = current_pos;\nSTART SLAVE;\n\nOr:\n\nSTOP SLAVE;\nSET GLOBAL gtid_slave_pos=\'0-1-153\';\nCHANGE MASTER TO\n MASTER_USE_GTID = slave_pos;\nSTART SLAVE;\n\nReplication Filter Options\n--------------------------\n\nAlso see Replication filters.\n\nIGNORE_SERVER_IDS\n-----------------\n\nThe IGNORE_SERVER_IDS option for CHANGE MASTER can be used to configure a\nreplica to ignore binary log events that originated from certain servers.\nFiltered binary log events will not get logged to the replica’s relay log, and\nthey will not be applied by the replica.\n\nThe option\'s value can be specified by providing a comma-separated list of\nserver_id values. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_SERVER_IDS = (3,5);\nSTART SLAVE;\n\nIf you would like to clear a previously set list, then you can set the value\nto an empty list. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_SERVER_IDS = ();\nSTART SLAVE;\n\nDO_DOMAIN_IDS\n-------------\n\nThe DO_DOMAIN_IDS option for CHANGE MASTER can be used to configure a replica\nto only apply binary log events if the transaction\'s GTID is in a specific\ngtid_domain_id value. Filtered binary log events will not get logged to the\nreplica’s relay log, and they will not be applied by the replica.\n\nThe option\'s value can be specified by providing a comma-separated list of\ngtid_domain_id values. Duplicate values are automatically ignored. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n DO_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nIf you would like to clear a previously set list, then you can set the value\nto an empty list. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n DO_DOMAIN_IDS = ();\nSTART SLAVE;\n\nThe DO_DOMAIN_IDS option and the IGNORE_DOMAIN_IDS option cannot both be set\nto non-empty values at the same time. If you want to set the DO_DOMAIN_IDS\noption, and the IGNORE_DOMAIN_IDS option was previously set, then you need to\nclear the value of the IGNORE_DOMAIN_IDS option. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_DOMAIN_IDS = (),\n DO_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nThe DO_DOMAIN_IDS option can only be specified if the replica is replicating\nin GTID mode. Therefore, the MASTER_USE_GTID option must also be set to some\nvalue other than no in order to use this option.\n\nIGNORE_DOMAIN_IDS\n-----------------\n\nThe IGNORE_DOMAIN_IDS option for CHANGE MASTER can be used to configure a\nreplica to ignore binary log events if the transaction\'s GTID is in a specific\ngtid_domain_id value. Filtered binary log events will not get logged to the\nreplica’s relay log, and they will not be applied by the replica.\n\nThe option\'s value can be specified by providing a comma-separated list of\ngtid_domain_id values. Duplicate values are automatically ignored. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nIf you would like to clear a previously set list, then you can set the value\nto an empty list. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_DOMAIN_IDS = ();\nSTART SLAVE;\n\nThe DO_DOMAIN_IDS option and the IGNORE_DOMAIN_IDS option cannot both be set\nto non-empty values at the same time. If you want to set the IGNORE_DOMAIN_IDS\noption, and the DO_DOMAIN_IDS option was previously set, then you need to\nclear the value of the DO_DOMAIN_IDS option. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n DO_DOMAIN_IDS = (),\n IGNORE_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nThe IGNORE_DOMAIN_IDS option can only be specified if the replica is\nreplicating in GTID mode. Therefore, the MASTER_USE_GTID option must also be\nset to some value other than no in order to use this option.\n\nDelayed Replication Options\n---------------------------\n\nMASTER_DELAY\n------------\n\nThe MASTER_DELAY option for CHANGE MASTER can be used to enable delayed\nreplication. This option specifies the time in seconds (at least) that a\nreplica should lag behind the primary up to a maximum value of 2147483647, or\nabout 68 years. Before executing an event, the replica will first wait, if\nnecessary, until the given time has passed since the event was created on the\nprimary. The result is that the replica will reflect the state of the primary\nsome time back in the past. The default is zero, no delay.\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_DELAY=3600;\nSTART SLAVE;\n\nChanging Option Values\n----------------------\n\nIf you don\'t specify a given option when executing the CHANGE MASTER\nstatement, then the option keeps its old value in most cases. Most of the\ntime, there is no need to specify the options that do not need to change. For\nexample, if the password for the user account that the replica uses to connect\nto its primary has changed, but no other options need to change, then you can\njust change the MASTER_PASSWORD option by executing the following commands:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nThere are some cases where options are implicitly reset, such as when the\nMASTER_HOST and MASTER_PORT options are changed.\n\nOption Persistence\n------------------\n\nThe values of the MASTER_LOG_FILE and MASTER_LOG_POS options (i.e. the binary\nlog position on the primary) and most other options are written to either the\ndefault master.info file or the file that is configured by the\nmaster_info_file option. The replica\'s I/O thread keeps this binary log\nposition updated as it downloads events only when MASTER_USE_GTID option is\nset to NO. Otherwise the file is not updated on a per event basis.\n\nThe master_info_file option can be set either on the command-line or in a\nserver option group in an option file prior to starting up the server. For\nexample:\n\n[mariadb]\n...\nmaster_info_file=/mariadb/myserver1-master.info\n\nThe values of the RELAY_LOG_FILE and RELAY_LOG_POS options (i.e. the relay log\nposition) are written to either the default relay-log.info file or the file\nthat is configured by the relay_log_info_file system variable. The replica\'s\nSQL thread keeps this relay log position updated as it applies events.\n\nThe relay_log_info_file system variable can be set either on the command-line\nor in a server option group in an option file prior to starting up the server.\nFor example:\n\n[mariadb]\n...\nrelay_log_info_file=/mariadb/myserver1-relay-log.info\n\nGTID Persistence\n----------------\n\nIf the replica is replicating binary log events that contain GTIDs, then the\nreplica\'s SQL thread will write every GTID that it applies to the\nmysql.gtid_slave_pos table. This GTID can be inspected and modified through\nthe gtid_slave_pos system variable.\n\nIf the replica has the log_slave_updates system variable enabled and if the\nreplica has the binary log enabled, then every write by the replica\'s SQL\nthread will also go into the replica\'s binary log. This means that GTIDs of\nreplicated transactions would be reflected in the value of the gtid_binlog_pos\nsystem variable.\n\nCreating a Replica from a Backup\n--------------------------------\n\nThe CHANGE MASTER statement is useful for setting up a replica when you have a\nbackup of the primary and you also have the binary log position or GTID\nposition corresponding to the backup.\n\nAfter restoring the backup on the replica, you could execute something like\nthis to use the binary log position:\n\nCHANGE MASTER TO\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4;\nSTART SLAVE;\n\nOr you could execute something like this to use the GTID position:\n\nSET GLOBAL gtid_slave_pos=\'0-1-153\';\nCHANGE MASTER TO\n MASTER_USE_GTID=slave_pos;\nSTART SLAVE;\n\nSee Setting up a Replication Slave with Mariabackup for more information on\nhow to do this with Mariabackup.\n\nExample') WHERE help_topic_id = 819; -update help_topic set description = CONCAT(description, '\n-------\n\nThe following example changes the primary and primary\'s binary log\ncoordinates. This is used when you want to set up the replica to replicate the\nprimary:\n\nCHANGE MASTER TO\n MASTER_HOST=\'master2.mycompany.com\',\n MASTER_USER=\'replication\',\n MASTER_PASSWORD=\'bigs3cret\',\n MASTER_PORT=3306,\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4,\n MASTER_CONNECT_RETRY=10;\nSTART SLAVE;\n\nURL: https://mariadb.com/kb/en/change-master-to/') WHERE help_topic_id = 819; +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (819,49,'CHANGE MASTER TO','The terms master and slave have historically been used in replication, but the\nterms terms primary and replica are now preferred. The old terms are used\nstill used in parts of the documentation, and in MariaDB commands, although\nMariaDB 10.5 has begun the process of renaming. The documentation process is\nongoing. See MDEV-18777 to follow progress on this effort.\n\nSyntax\n------\n\nCHANGE MASTER [\'connection_name\'] TO master_def [, master_def] ... \n [FOR CHANNEL \'channel_name\']\n\nmaster_def:\n MASTER_BIND = \'interface_name\'\n | MASTER_HOST = \'host_name\'\n | MASTER_USER = \'user_name\'\n | MASTER_PASSWORD = \'password\'\n | MASTER_PORT = port_num\n | MASTER_CONNECT_RETRY = interval\n | MASTER_HEARTBEAT_PERIOD = interval\n | MASTER_LOG_FILE = \'master_log_name\'\n | MASTER_LOG_POS = master_log_pos\n | RELAY_LOG_FILE = \'relay_log_name\'\n | RELAY_LOG_POS = relay_log_pos\n | MASTER_DELAY = interval\n | MASTER_SSL = {0|1}\n | MASTER_SSL_CA = \'ca_file_name\'\n | MASTER_SSL_CAPATH = \'ca_directory_name\'\n | MASTER_SSL_CERT = \'cert_file_name\'\n | MASTER_SSL_CRL = \'crl_file_name\'\n | MASTER_SSL_CRLPATH = \'crl_directory_name\'\n | MASTER_SSL_KEY = \'key_file_name\'\n | MASTER_SSL_CIPHER = \'cipher_list\'\n | MASTER_SSL_VERIFY_SERVER_CERT = {0|1}\n | MASTER_USE_GTID = {current_pos|slave_pos|no}\n | MASTER_DEMOTE_TO_SLAVE = bool\n | IGNORE_SERVER_IDS = (server_id_list)\n | DO_DOMAIN_IDS = ([N,..])\n | IGNORE_DOMAIN_IDS = ([N,..])\n\nDescription\n-----------\n\nThe CHANGE MASTER statement sets the options that a replica uses to connect to\nand replicate from a primary.\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nto using the channel_name directly after CHANGE MASTER.\n\nMulti-Source Replication\n------------------------\n\nIf you are using multi-source replication, then you need to specify a\nconnection name when you execute CHANGE MASTER. There are two ways to do this:\n\n* Setting the default_master_connection system variable prior to executing\nCHANGE MASTER.\n* Setting the connection_name parameter when executing CHANGE MASTER.\n\ndefault_master_connection\n-------------------------\n\nSET default_master_connection = \'gandalf\';\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nconnection_name\n---------------\n\nSTOP SLAVE \'gandalf\';\nCHANGE MASTER \'gandalf\' TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE \'gandalf\';\n\nOptions\n-------\n\nConnection Options\n------------------\n\nMASTER_USER\n-----------\n\nThe MASTER_USER option for CHANGE MASTER defines the user account that the\nreplica will use to connect to the primary.\n\nThis user account will need the REPLICATION SLAVE privilege (or, from MariaDB\n10.5.1, the REPLICATION REPLICA on the primary.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_USER=\'repl\',\n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nThe maximum length of the MASTER_USER string is 96 characters until MariaDB\n10.5, and 128 characters from MariaDB 10.6.\n\nMASTER_PASSWORD\n---------------\n\nThe MASTER_USER option for CHANGE MASTER defines the password that the replica\nwill use to connect to the primary as the user account defined by the\nMASTER_USER option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nThe maximum length of the MASTER_PASSWORD string is 32 characters. The\neffective maximum length of the string depends on how many bytes are used per\ncharacter and can be up to 96 characters.\n\nDue to MDEV-29994, the password can be silently truncated to 41 characters\nwhen MariaDB is restarted. For this reason it is recommended to use a password\nthat is shorter than this.\n\nMASTER_HOST\n-----------\n\nThe MASTER_HOST option for CHANGE MASTER defines the hostname or IP address of\nthe primary.\n\nIf you set the value of the MASTER_HOST option to the empty string, then that\nis not the same as not setting the option\'s value at all. If you set the value\nof the MASTER_HOST option to the empty string, then the CHANGE MASTER command\nwill fail with an error. In MariaDB 5.3 and before, if you set the value of\nthe MASTER_HOST option to the empty string, then the CHANGE MASTER command\nwould succeed, but the subsequent START SLAVE command would fail.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_HOST=\'dbserver1.example.com\',\n MASTER_USER=\'repl\',\n MASTER_PASSWORD=\'new3cret\',\n MASTER_USE_GTID=slave_pos;\nSTART SLAVE;\n\nIf you set the value of the MASTER_HOST option in a CHANGE MASTER command,\nthen the replica assumes that the primary is different from before, even if\nyou set the value of this option to the same value it had previously. In this\nscenario, the replica will consider the old values for the primary\'s binary\nlog file name and position to be invalid for the new primary. As a side\neffect, if you do not explicitly set the values of the MASTER_LOG_FILE and\nMASTER_LOG_POS options in the statement, then the statement will be implicitly\nappended with MASTER_LOG_FILE=\'\' and MASTER_LOG_POS=4. However, if you enable\nGTID mode for replication by setting the MASTER_USE_GTID option to some value\nother than no in the statement, then these values will effectively be ignored\nanyway.\n\nReplicas cannot connect to primaries using Unix socket files or Windows named\npipes. The replica must connect to the primary using TCP/IP.\n\nThe maximum length of the MASTER_HOST string is 60 characters until MariaDB\n10.5, and 255 characters from MariaDB 10.6.\n\nMASTER_PORT\n-----------\n\nThe MASTER_PORT option for CHANGE MASTER defines the TCP/IP port of the\nprimary.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_HOST=\'dbserver1.example.com\',\n MASTER_PORT=3307,\n MASTER_USER=\'repl\',\n MASTER_PASSWORD=\'new3cret\',\n MASTER_USE_GTID=slave_pos;\nSTART SLAVE;\n\nIf you set the value of the MASTER_PORT option in a CHANGE MASTER command,\nthen the replica assumes that the primary is different from before, even if\nyou set the value of this option to the same value it had previously. In this\nscenario, the replica will consider the old values for the primary\'s binary\nlog file name and position to be invalid for the new primary. As a side\neffect, if you do not explicitly set the values of the MASTER_LOG_FILE and\nMASTER_LOG_POS options in the statement, then the statement will be implicitly\nappended with MASTER_LOG_FILE=\'\' and MASTER_LOG_POS=4. However, if you enable\nGTID mode for replication by setting the MASTER_USE_GTID option to some value\nother than no in the statement, then these values will effectively be ignored\nanyway.\n\nReplicas cannot connect to primaries using Unix socket files or Windows named\npipes. The replica must connect to the primary using TCP/IP.\n\nMASTER_CONNECT_RETRY\n--------------------\n\nThe MASTER_CONNECT_RETRY option for CHANGE MASTER defines how many seconds\nthat the replica will wait between connection retries. The default is 60.\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_CONNECT_RETRY=20;\nSTART SLAVE;\n\nThe number of connection attempts is limited by the master_retry_count option.\nIt can be set either on the command-line or in a server option group in an\noption file prior to starting up the server. For example:\n\n[mariadb]\n...\nmaster_retry_count=4294967295\n\nMASTER_BIND\n-----------\n\nThe MASTER_BIND option for CHANGE MASTER is only supported by MySQL 5.6.2 and\nlater and by MySQL NDB Cluster 7.3.1 and later. This option is not supported\nby MariaDB. See MDEV-19248 for more information.\n\nThe MASTER_BIND option for CHANGE MASTER can be used on replicas that have\nmultiple network interfaces to choose which network interface the replica will\nuse to connect to the primary.\n\nMASTER_HEARTBEAT_PERIOD\n-----------------------\n\nThe MASTER_HEARTBEAT_PERIOD option for CHANGE MASTER can be used to set the\ninterval in seconds between replication heartbeats. Whenever the primary\'s\nbinary log is updated with an event, the waiting period for the next heartbeat\nis reset.\n\nThis option\'s interval argument has the following characteristics:\n\n* It is a decimal value with a range of 0 to 4294967 seconds.\n* It has a resolution of hundredths of a second.\n* Its smallest valid non-zero value is 0.001.\n* Its default value is the value of the slave_net_timeout system variable\ndivided by 2.\n* If it\'s set to 0, then heartbeats are disabled.\n\nHeartbeats are sent by the primary only if there are no unsent events in the\nbinary log file for a period longer than the interval.\n\nIf the RESET SLAVE statement is executed, then the heartbeat interval is reset\nto the default.\n\nIf the slave_net_timeout system variable is set to a value that is lower than\nthe current heartbeat interval, then a warning will be issued.\n\nTLS Options\n-----------\n\nThe TLS options are used for providing information about TLS. The options can\nbe set even on replicas that are compiled without TLS support. The TLS options\nare saved to either the default master.info file or the file that is\nconfigured by the master_info_file option, but these TLS options are ignored\nunless the replica supports TLS.\n\nSee Replication with Secure Connections for more information.\n\nMASTER_SSL\n----------\n\nThe MASTER_SSL option for CHANGE MASTER tells the replica whether to force TLS\nfor the connection. The valid values are 0 or 1.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL=1;\nSTART SLAVE;\n\nMASTER_SSL_CA\n-------------\n\nThe MASTER_SSL_CA option for CHANGE MASTER defines a path to a PEM file that\nshould contain one or more X509 certificates for trusted Certificate\nAuthorities (CAs) to use for TLS. This option requires that you use the\nabsolute path, not a relative path. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Authorities (CAs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CA string is 511 characters.\n\nMASTER_SSL_CAPATH\n-----------------\n\nThe MASTER_SSL_CAPATH option for CHANGE MASTER defines a path to a directory\nthat contains one or more PEM files that should each contain one X509\ncertificate for a trusted Certificate Authority (CA) to use for TLS. This\noption requires that you use the absolute path, not a relative path. The\ndirectory specified by this option needs to be run through the openssl rehash\ncommand. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CAPATH=\'/etc/my.cnf.d/certificates/ca/\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Authorities (CAs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CA_PATH string is 511 characters.\n\nMASTER_SSL_CERT\n---------------\n\nThe MASTER_SSL_CERT option for CHANGE MASTER defines a path to the X509\ncertificate file to use for TLS. This option requires that you use the\nabsolute path, not a relative path. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nThe maximum length of MASTER_SSL_CERT string is 511 characters.\n\nMASTER_SSL_CRL\n--------------\n\nThe MASTER_SSL_CRL option for CHANGE MASTER defines a path to a PEM file that\nshould contain one or more revoked X509 certificates to use for TLS. This\noption requires that you use the absolute path, not a relative path.\n\nThis option is only supported if the server was built with OpenSSL. If the\nserver was built with yaSSL, then this option is not supported. See TLS and\nCryptography Libraries Used by MariaDB for more information about which\nlibraries are used on which platforms.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1,\n MASTER_SSL_CRL=\'/etc/my.cnf.d/certificates/crl.pem\';\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Revocation Lists (CRLs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CRL string is 511 characters.\n\nMASTER_SSL_CRLPATH\n------------------\n\nThe MASTER_SSL_CRLPATH option for CHANGE MASTER defines a path to a directory\nthat contains one or more PEM files that should each contain one revoked X509\ncertificate to use for TLS. This option requires that you use the absolute\npath, not a relative path. The directory specified by this variable needs to\nbe run through the openssl rehash command.\n\nThis option is only supported if the server was built with OpenSSL. If the\nserver was built with yaSSL, then this option is not supported. See TLS and\nCryptography Libraries Used by MariaDB for more information about which\nlibraries are used on which platforms.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1,\n MASTER_SSL_CRLPATH=\'/etc/my.cnf.d/certificates/crl/\';\nSTART SLAVE;\n\nSee Secure Connections Overview: Certificate Revocation Lists (CRLs) for more\ninformation.\n\nThe maximum length of MASTER_SSL_CRL_PATH string is 511 characters.\n\nMASTER_SSL_KEY\n--------------\n\nThe MASTER_SSL_KEY option for CHANGE MASTER defines a path to a private key\nfile to use for TLS. This option requires that you use the absolute path, not\na relative path. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;','','https://mariadb.com/kb/en/change-master-to/'); +update help_topic set description = CONCAT(description, '\nSTART SLAVE;\n\nThe maximum length of MASTER_SSL_KEY string is 511 characters.\n\nMASTER_SSL_CIPHER\n-----------------\n\nThe MASTER_SSL_CIPHER option for CHANGE MASTER defines the list of permitted\nciphers or cipher suites to use for TLS. Besides cipher names, if MariaDB was\ncompiled with OpenSSL, this option could be set to \"SSLv3\" or \"TLSv1.2\" to\nallow all SSLv3 or all TLSv1.2 ciphers. Note that the TLSv1.3 ciphers cannot\nbe excluded when using OpenSSL, even by using this option. See Using TLSv1.3\nfor details. This option implies the MASTER_SSL option.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1,\n MASTER_SSL_CIPHER=\'TLSv1.2\';\nSTART SLAVE;\n\nThe maximum length of MASTER_SSL_CIPHER string is 511 characters.\n\nMASTER_SSL_VERIFY_SERVER_CERT\n-----------------------------\n\nThe MASTER_SSL_VERIFY_SERVER_CERT option for CHANGE MASTER enables server\ncertificate verification. This option is disabled by default.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_SSL_CERT=\'/etc/my.cnf.d/certificates/server-cert.pem\',\n MASTER_SSL_KEY=\'/etc/my.cnf.d/certificates/server-key.pem\',\n MASTER_SSL_CA=\'/etc/my.cnf.d/certificates/ca.pem\',\n MASTER_SSL_VERIFY_SERVER_CERT=1;\nSTART SLAVE;\n\nSee Secure Connections Overview: Server Certificate Verification for more\ninformation.\n\nBinary Log Options\n------------------\n\nThese options are related to the binary log position on the primary.\n\nMASTER_LOG_FILE\n---------------\n\nThe MASTER_LOG_FILE option for CHANGE MASTER can be used along with\nMASTER_LOG_POS to specify the coordinates at which the replica\'s I/O thread\nshould begin reading from the primary\'s binary logs the next time the thread\nstarts.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4;\nSTART SLAVE;\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options cannot be specified if the\nRELAY_LOG_FILE and RELAY_LOG_POS options were also specified.\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options are effectively ignored if you\nenable GTID mode for replication by setting the MASTER_USE_GTID option to some\nvalue other than no in the statement.\n\nMASTER_LOG_POS\n--------------\n\nThe MASTER_LOG_POS option for CHANGE MASTER can be used along with\nMASTER_LOG_FILE to specify the coordinates at which the replica\'s I/O thread\nshould begin reading from the primary\'s binary logs the next time the thread\nstarts.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4;\nSTART SLAVE;\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options cannot be specified if the\nRELAY_LOG_FILE and RELAY_LOG_POS options were also specified.\n\nThe MASTER_LOG_FILE and MASTER_LOG_POS options are effectively ignored if you\nenable GTID mode for replication by setting the MASTER_USE_GTID option to some\nvalue other than no in the statement.\n\nRelay Log Options\n-----------------\n\nThese options are related to the relay log position on the replica.\n\nRELAY_LOG_FILE\n--------------\n\nThe RELAY_LOG_FILE option for CHANGE MASTER can be used along with the\nRELAY_LOG_POS option to specify the coordinates at which the replica\'s SQL\nthread should begin reading from the relay log the next time the thread starts.\n\nThe CHANGE MASTER statement usually deletes all relay log files. However, if\nthe RELAY_LOG_FILE and/or RELAY_LOG_POS options are specified, then existing\nrelay log files are kept.\n\nWhen you want to change the relay log position, you only need to stop the\nreplica\'s SQL thread. The replica\'s I/O thread can continue running. The STOP\nSLAVE and START SLAVE statements support the SQL_THREAD option for this\nscenario. For example:\n\nSTOP SLAVE SQL_THREAD;\nCHANGE MASTER TO\n RELAY_LOG_FILE=\'slave-relay-bin.006\',\n RELAY_LOG_POS=4025;\nSTART SLAVE SQL_THREAD;\n\nWhen the value of this option is changed, the metadata about the replica\'s SQL\nthread\'s position in the relay logs will also be changed in the relay-log.info\nfile or the file that is configured by the relay_log_info_file system variable.\n\nThe RELAY_LOG_FILE and RELAY_LOG_POS options cannot be specified if the\nMASTER_LOG_FILE and MASTER_LOG_POS options were also specified.\n\nRELAY_LOG_POS\n-------------\n\nThe RELAY_LOG_POS option for CHANGE MASTER can be used along with the\nRELAY_LOG_FILE option to specify the coordinates at which the replica\'s SQL\nthread should begin reading from the relay log the next time the thread starts.\n\nThe CHANGE MASTER statement usually deletes all relay log files. However, if\nthe RELAY_LOG_FILE and/or RELAY_LOG_POS options are specified, then existing\nrelay log files are kept.\n\nWhen you want to change the relay log position, you only need to stop the\nreplica\'s SQL thread. The replica\'s I/O thread can continue running. The STOP\nSLAVE and START SLAVE statements support the SQL_THREAD option for this\nscenario. For example:\n\nSTOP SLAVE SQL_THREAD;\nCHANGE MASTER TO\n RELAY_LOG_FILE=\'slave-relay-bin.006\',\n RELAY_LOG_POS=4025;\nSTART SLAVE SQL_THREAD;\n\nWhen the value of this option is changed, the metadata about the replica\'s SQL\nthread\'s position in the relay logs will also be changed in the relay-log.info\nfile or the file that is configured by the relay_log_info_file system variable.\n\nThe RELAY_LOG_FILE and RELAY_LOG_POS options cannot be specified if the\nMASTER_LOG_FILE and MASTER_LOG_POS options were also specified.\n\nGTID Options\n------------\n\nMASTER_USE_GTID\n---------------\n\nThe MASTER_USE_GTID option for CHANGE MASTER can be used to configure the\nreplica to use the global transaction ID (GTID) when connecting to a primary.\nThe possible values are:\n\n* current_pos - Replicate in GTID mode and use gtid_current_pos as the\nposition to start downloading transactions from the primary. Deprecated from\nMariaDB 10.10. Using to transition to primary can break the replication state\nif the replica executes local transactions due to actively updating\ngtid_current_pos with gtid_binlog_pos and gtid_slave_pos. Use the new, safe,\nMASTER_DEMOTE_TO_SLAVE= option instead.\n* slave_pos - Replicate in GTID mode and use gtid_slave_pos as the position to\nstart downloading transactions from the primary. From MariaDB 10.5.1,\nreplica_pos is an alias for slave_pos.\n* no - Don\'t replicate in GTID mode.\n\nMASTER_DEMOTE_TO_SLAVE\n----------------------\n\nMariaDB starting with 10.10\n---------------------------\nUsed to transition a primary to become a replica. Replaces the old\nMASTER_USE_GTID=current_pos with a safe alternative by forcing users to set\nUsing_Gtid=Slave_Pos and merging gtid_binlog_pos into gtid_slave_pos once at\nCHANGE MASTER TO time. If gtid_slave_pos is more recent than gtid_binlog_pos\n(as in the case of chain replication), the replication state should be\npreserved.\n\nFor example:\n\nSTOP SLAVE;\nCHANGE MASTER TO\n MASTER_USE_GTID = current_pos;\nSTART SLAVE;\n\nOr:\n\nSTOP SLAVE;\nSET GLOBAL gtid_slave_pos=\'0-1-153\';\nCHANGE MASTER TO\n MASTER_USE_GTID = slave_pos;\nSTART SLAVE;\n\nReplication Filter Options\n--------------------------\n\nAlso see Replication filters.\n\nIGNORE_SERVER_IDS\n-----------------\n\nThe IGNORE_SERVER_IDS option for CHANGE MASTER can be used to configure a\nreplica to ignore binary log events that originated from certain servers.\nFiltered binary log events will not get logged to the replica’s relay log, and\nthey will not be applied by the replica.\n\nThe option\'s value can be specified by providing a comma-separated list of\nserver_id values. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_SERVER_IDS = (3,5);\nSTART SLAVE;\n\nIf you would like to clear a previously set list, then you can set the value\nto an empty list. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_SERVER_IDS = ();\nSTART SLAVE;\n\nDO_DOMAIN_IDS\n-------------\n\nThe DO_DOMAIN_IDS option for CHANGE MASTER can be used to configure a replica\nto only apply binary log events if the transaction\'s GTID is in a specific\ngtid_domain_id value. Filtered binary log events will not get logged to the\nreplica’s relay log, and they will not be applied by the replica.\n\nThe option\'s value can be specified by providing a comma-separated list of\ngtid_domain_id values. Duplicate values are automatically ignored. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n DO_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nIf you would like to clear a previously set list, then you can set the value\nto an empty list. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n DO_DOMAIN_IDS = ();\nSTART SLAVE;\n\nThe DO_DOMAIN_IDS option and the IGNORE_DOMAIN_IDS option cannot both be set\nto non-empty values at the same time. If you want to set the DO_DOMAIN_IDS\noption, and the IGNORE_DOMAIN_IDS option was previously set, then you need to\nclear the value of the IGNORE_DOMAIN_IDS option. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_DOMAIN_IDS = (),\n DO_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nThe DO_DOMAIN_IDS option can only be specified if the replica is replicating\nin GTID mode. Therefore, the MASTER_USE_GTID option must also be set to some\nvalue other than no in order to use this option.\n\nIGNORE_DOMAIN_IDS\n-----------------\n\nThe IGNORE_DOMAIN_IDS option for CHANGE MASTER can be used to configure a\nreplica to ignore binary log events if the transaction\'s GTID is in a specific\ngtid_domain_id value. Filtered binary log events will not get logged to the\nreplica’s relay log, and they will not be applied by the replica.\n\nThe option\'s value can be specified by providing a comma-separated list of\ngtid_domain_id values. Duplicate values are automatically ignored. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nIf you would like to clear a previously set list, then you can set the value\nto an empty list. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n IGNORE_DOMAIN_IDS = ();\nSTART SLAVE;\n\nThe DO_DOMAIN_IDS option and the IGNORE_DOMAIN_IDS option cannot both be set\nto non-empty values at the same time. If you want to set the IGNORE_DOMAIN_IDS\noption, and the DO_DOMAIN_IDS option was previously set, then you need to\nclear the value of the DO_DOMAIN_IDS option. For example:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n DO_DOMAIN_IDS = (),\n IGNORE_DOMAIN_IDS = (1,2);\nSTART SLAVE;\n\nThe IGNORE_DOMAIN_IDS option can only be specified if the replica is\nreplicating in GTID mode. Therefore, the MASTER_USE_GTID option must also be\nset to some value other than no in order to use this option.\n\nDelayed Replication Options\n---------------------------\n\nMASTER_DELAY\n------------\n\nThe MASTER_DELAY option for CHANGE MASTER can be used to enable delayed\nreplication. This option specifies the time in seconds (at least) that a\nreplica should lag behind the primary up to a maximum value of 2147483647, or\nabout 68 years. Before executing an event, the replica will first wait, if\nnecessary, until the given time has passed since the event was created on the\nprimary. The result is that the replica will reflect the state of the primary\nsome time back in the past. The default is zero, no delay.\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_DELAY=3600;\nSTART SLAVE;\n\nChanging Option Values\n----------------------\n\nIf you don\'t specify a given option when executing the CHANGE MASTER\nstatement, then the option keeps its old value in most cases. Most of the\ntime, there is no need to specify the options that do not need to change. For\nexample, if the password for the user account that the replica uses to connect\nto its primary has changed, but no other options need to change, then you can\njust change the MASTER_PASSWORD option by executing the following commands:\n\nSTOP SLAVE;\nCHANGE MASTER TO \n MASTER_PASSWORD=\'new3cret\';\nSTART SLAVE;\n\nThere are some cases where options are implicitly reset, such as when the\nMASTER_HOST and MASTER_PORT options are changed.\n\nOption Persistence\n------------------\n\nThe values of the MASTER_LOG_FILE and MASTER_LOG_POS options (i.e. the binary\nlog position on the primary) and most other options are written to either the\ndefault master.info file or the file that is configured by the\nmaster_info_file option. The replica\'s I/O thread keeps this binary log\nposition updated as it downloads events only when MASTER_USE_GTID option is\nset to NO. Otherwise the file is not updated on a per event basis.\n\nThe master_info_file option can be set either on the command-line or in a\nserver option group in an option file prior to starting up the server. For\nexample:\n\n[mariadb]\n...\nmaster_info_file=/mariadb/myserver1-master.info\n\nThe values of the RELAY_LOG_FILE and RELAY_LOG_POS options (i.e. the relay log\nposition) are written to either the default relay-log.info file or the file\nthat is configured by the relay_log_info_file system variable. The replica\'s\nSQL thread keeps this relay log position updated as it applies events.\n\nThe relay_log_info_file system variable can be set either on the command-line\nor in a server option group in an option file prior to starting up the server.\nFor example:\n\n[mariadb]\n...\nrelay_log_info_file=/mariadb/myserver1-relay-log.info\n\nGTID Persistence\n----------------\n\nIf the replica is replicating binary log events that contain GTIDs, then the\nreplica\'s SQL thread will write every GTID that it applies to the\nmysql.gtid_slave_pos table. This GTID can be inspected and modified through\nthe gtid_slave_pos system variable.\n\nIf the replica has the log_slave_updates system variable enabled and if the\nreplica has the binary log enabled, then every write by the replica\'s SQL\nthread will also go into the replica\'s binary log. This means that GTIDs of\nreplicated transactions would be reflected in the value of the gtid_binlog_pos\nsystem variable.\n\nCreating a Replica from a Backup\n--------------------------------\n\nThe CHANGE MASTER statement is useful for setting up a replica when you have a\nbackup of the primary and you also have the binary log position or GTID\nposition corresponding to the backup.\n\nAfter restoring the backup on the replica, you could execute something like\nthis to use the binary log position:\n\nCHANGE MASTER TO\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4;\nSTART SLAVE;\n') WHERE help_topic_id = 819; +update help_topic set description = CONCAT(description, '\nOr you could execute something like this to use the GTID position:\n\nSET GLOBAL gtid_slave_pos=\'0-1-153\';\nCHANGE MASTER TO\n MASTER_USE_GTID=slave_pos;\nSTART SLAVE;\n\nSee Setting up a Replication Slave with Mariabackup for more information on\nhow to do this with Mariabackup.\n\nExample\n-------\n\nThe following example changes the primary and primary\'s binary log\ncoordinates. This is used when you want to set up the replica to replicate the\nprimary:\n\nCHANGE MASTER TO\n MASTER_HOST=\'master2.mycompany.com\',\n MASTER_USER=\'replication\',\n MASTER_PASSWORD=\'bigs3cret\',\n MASTER_PORT=3306,\n MASTER_LOG_FILE=\'master2-bin.001\',\n MASTER_LOG_POS=4,\n MASTER_CONNECT_RETRY=10;\nSTART SLAVE;\n\nURL: https://mariadb.com/kb/en/change-master-to/') WHERE help_topic_id = 819; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (820,49,'START SLAVE','The terms master and slave have historically been used in replication, but the\nterms terms primary and replica are now preferred. The old terms are used\nstill used in parts of the documentation, and in MariaDB commands, although\nMariaDB 10.5 has begun the process of renaming. The documentation process is\nongoing. See MDEV-18777 to follow progress on this effort.\n\nSyntax\n------\n\nSTART SLAVE [\"connection_name\"] [thread_type [, thread_type] ... ] [FOR\nCHANNEL \"connection_name\"]\nSTART SLAVE [\"connection_name\"] [SQL_THREAD] UNTIL \n MASTER_LOG_FILE = \'log_name\', MASTER_LOG_POS = log_pos [FOR CHANNEL\n\"connection_name\"]\nSTART SLAVE [\"connection_name\"] [SQL_THREAD] UNTIL\n RELAY_LOG_FILE = \'log_name\', RELAY_LOG_POS = log_pos [FOR CHANNEL\n\"connection_name\"]\nSTART SLAVE [\"connection_name\"] [SQL_THREAD] UNTIL\n MASTER_GTID_POS = [FOR CHANNEL \"connection_name\"]\nSTART ALL SLAVES [thread_type [, thread_type]]\n\nSTART REPLICA [\"connection_name\"] [thread_type [, thread_type] ... ] -- from\n10.5.1\nSTART REPLICA [\"connection_name\"] [SQL_THREAD] UNTIL \n MASTER_LOG_FILE = \'log_name\', MASTER_LOG_POS = log_pos -- from 10.5.1\nSTART REPLICA [\"connection_name\"] [SQL_THREAD] UNTIL\n RELAY_LOG_FILE = \'log_name\', RELAY_LOG_POS = log_pos -- from 10.5.1\nSTART REPLICA [\"connection_name\"] [SQL_THREAD] UNTIL\n MASTER_GTID_POS = -- from 10.5.1\nSTART ALL REPLICAS [thread_type [, thread_type]] -- from 10.5.1\n\nthread_type: IO_THREAD | SQL_THREAD\n\nDescription\n-----------\n\nSTART SLAVE (START REPLICA from MariaDB 10.5.1) with no thread_type options\nstarts both of the replica threads (see replication). The I/O thread reads\nevents from the primary server and stores them in the relay log. The SQL\nthread reads events from the relay log and executes them. START SLAVE requires\nthe SUPER privilege, or, from MariaDB 10.5.2, the REPLICATION SLAVE ADMIN\nprivilege.\n\nIf START SLAVE succeeds in starting the replica threads, it returns without\nany error. However, even in that case, it might be that the replica threads\nstart and then later stop (for example, because they do not manage to connect\nto the primary or read its binary log, or some other problem). START SLAVE\ndoes not warn you about this. You must check the replica\'s error log for error\nmessages generated by the replica threads, or check that they are running\nsatisfactorily with SHOW SLAVE STATUS (SHOW REPLICA STATUS from MariaDB\n10.5.1).\n\nSTART SLAVE UNTIL\n-----------------\n\nSTART SLAVE UNTIL refers to the SQL_THREAD replica position at which the\nSQL_THREAD replication will halt. If SQL_THREAD isn\'t specified both threads\nare started.\n\nSTART SLAVE UNTIL master_gtid_pos=xxx is also supported. See Global\nTransaction ID/START SLAVE UNTIL master_gtid_pos=xxx for more details.\n\nconnection_name\n---------------\n\nIf there is only one nameless primary, or the default primary (as specified by\nthe default_master_connection system variable) is intended, connection_name\ncan be omitted. If provided, the START SLAVE statement will apply to the\nspecified primary. connection_name is case-insensitive.\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nas using the channel_name directly after START SLAVE.\n\nSTART ALL SLAVES\n----------------\n\nSTART ALL SLAVES starts all configured replicas (replicas with master_host not\nempty) that were not started before. It will give a note for all started\nconnections. You can check the notes with SHOW WARNINGS.\n\nSTART REPLICA\n-------------\n\nMariaDB starting with 10.5.1\n----------------------------\nSTART REPLICA is an alias for START SLAVE from MariaDB 10.5.1.\n\nURL: https://mariadb.com/kb/en/start-replica/','','https://mariadb.com/kb/en/start-replica/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (821,49,'STOP SLAVE','The terms master and slave have historically been used in replication, but the\nterms terms primary and replica are now preferred. The old terms are used\nstill used in parts of the documentation, and in MariaDB commands, although\nMariaDB 10.5 has begun the process of renaming. The documentation process is\nongoing. See MDEV-18777 to follow progress on this effort.\n\nSyntax\n------\n\nSTOP SLAVE [\"connection_name\"] [thread_type [, thread_type] ... ] [FOR CHANNEL\n\"connection_name\"]\n\nSTOP ALL SLAVES [thread_type [, thread_type]]\n\nSTOP REPLICA [\"connection_name\"] [thread_type [, thread_type] ... ] -- from\n10.5.1\n\nSTOP ALL REPLICAS [thread_type [, thread_type]] -- from 10.5.1\n\nthread_type: IO_THREAD | SQL_THREAD\n\nDescription\n-----------\n\nStops the replica threads. STOP SLAVE requires the SUPER privilege, or, from\nMariaDB 10.5.2, the REPLICATION SLAVE ADMIN privilege.\n\nLike START SLAVE, this statement may be used with the IO_THREAD and SQL_THREAD\noptions to name the thread or threads to be stopped. In almost all cases, one\nnever need to use the thread_type options.\n\nSTOP SLAVE waits until any current replication event group affecting one or\nmore non-transactional tables has finished executing (if there is any such\nreplication group), or until the user issues a KILL QUERY or KILL CONNECTION\nstatement.\n\nNote that STOP SLAVE doesn\'t delete the connection permanently. Next time you\nexecute START SLAVE or the MariaDB server restarts, the replica connection is\nrestored with it\'s original arguments. If you want to delete a connection, you\nshould execute RESET SLAVE.\n\nSTOP ALL SLAVES\n---------------\n\nSTOP ALL SLAVES stops all your running replicas. It will give you a note for\nevery stopped connection. You can check the notes with SHOW WARNINGS.\n\nconnection_name\n---------------\n\nThe connection_name option is used for multi-source replication.\n\nIf there is only one nameless master, or the default master (as specified by\nthe default_master_connection system variable) is intended, connection_name\ncan be omitted. If provided, the STOP SLAVE statement will apply to the\nspecified master. connection_name is case-insensitive.\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nas using the channel_name directly after STOP SLAVE.\n\nSTOP REPLICA\n------------\n\nMariaDB starting with 10.5.1\n----------------------------\nSTOP REPLICA is an alias for STOP SLAVE from MariaDB 10.5.1.\n\nURL: https://mariadb.com/kb/en/stop-replica/','','https://mariadb.com/kb/en/stop-replica/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (822,49,'RESET REPLICA/SLAVE','The terms master and slave have historically been used in replication, but the\nterms terms primary and replica are now preferred. The old terms are used\nstill used in parts of the documentation, and in MariaDB commands, although\nMariaDB 10.5 has begun the process of renaming. The documentation process is\nongoing. See MDEV-18777 to follow progress on this effort.\n\nSyntax\n------\n\nRESET REPLICA [\"connection_name\"] [ALL] [FOR CHANNEL \"connection_name\"] --\nfrom MariaDB 10.5.1 \nRESET SLAVE [\"connection_name\"] [ALL] [FOR CHANNEL \"connection_name\"]\n\nDescription\n-----------\n\nRESET REPLICA/SLAVE makes the replica forget its replication position in the\nmaster\'s binary log. This statement is meant to be used for a clean start. It\ndeletes the master.info and relay-log.info files, all the relay log files, and\nstarts a new relay log file. To use RESET REPLICA/SLAVE, the replica threads\nmust be stopped (use STOP REPLICA/SLAVE if necessary).\n\nNote: All relay log files are deleted, even if they have not been completely\nexecuted by the slave SQL thread. (This is a condition likely to exist on a\nreplication slave if you have issued a STOP REPLICA/SLAVE statement or if the\nslave is highly loaded.)\n\nNote: RESET REPLICA does not reset the global gtid_slave_pos variable. This\nmeans that a replica server configured with CHANGE MASTER TO\nMASTER_USE_GTID=slave_pos will not receive events with GTIDs occurring before\nthe state saved in gtid_slave_pos. If the intent is to reprocess these events,\ngtid_slave_pos must be manually reset, e.g. by executing set global\ngtid_slave_pos=\"\".\n\nConnection information stored in the master.info file is immediately reset\nusing any values specified in the corresponding startup options. This\ninformation includes values such as master host, master port, master user, and\nmaster password. If the replica SQL thread was in the middle of replicating\ntemporary tables when it was stopped, and RESET REPLICA/SLAVE is issued, these\nreplicated temporary tables are deleted on the slave.\n\nThe ALL also resets the PORT, HOST, USER and PASSWORD parameters for the\nslave. If you are using a connection name, it will permanently delete it and\nit will not show up anymore in SHOW ALL REPLICAS/SLAVE STATUS.\n\nconnection_name\n---------------\n\nThe connection_name option is used for multi-source replication.\n\nIf there is only one nameless primary, or the default primary (as specified by\nthe default_master_connection system variable) is intended, connection_name\ncan be omitted. If provided, the RESET REPLICA/SLAVE statement will apply to\nthe specified primary. connection_name is case-insensitive.\n\nMariaDB starting with 10.7.0\n----------------------------\nThe FOR CHANNEL keyword was added for MySQL compatibility. This is identical\nas using the channel_name directly after RESET REPLICA.\n\nRESET REPLICA\n-------------\n\nMariaDB starting with 10.5.1\n----------------------------\nRESET REPLICA is an alias for RESET SLAVE from MariaDB 10.5.1.\n\nURL: https://mariadb.com/kb/en/reset-replica/','','https://mariadb.com/kb/en/reset-replica/'); -- cgit v1.2.1 From a5b30158d4d8718f8a21b53ac21e3e33aa11b85e Mon Sep 17 00:00:00 2001 From: Ian Gilfillan Date: Mon, 23 Jan 2023 13:06:39 +0200 Subject: Update 10.8 HELP tables --- scripts/fill_help_tables.sql | 1885 +++++++++++++++++++++--------------------- 1 file changed, 943 insertions(+), 942 deletions(-) diff --git a/scripts/fill_help_tables.sql b/scripts/fill_help_tables.sql index a3bdc17a045..0454f4d2f05 100644 --- a/scripts/fill_help_tables.sql +++ b/scripts/fill_help_tables.sql @@ -84,8 +84,8 @@ insert into help_category (help_category_id,name,parent_category_id,url) values insert into help_category (help_category_id,name,parent_category_id,url) values (49,'Replication',1,''); insert into help_category (help_category_id,name,parent_category_id,url) values (50,'Prepared Statements',1,''); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (1,9,'HELP_DATE','Help Contents generated from the MariaDB Knowledge Base on 22 October 2022.','',''); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (2,9,'HELP_VERSION','Help Contents generated for MariaDB 10.7 from the MariaDB Knowledge Base on 22 October 2022.','',''); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (1,9,'HELP_DATE','Help Contents generated from the MariaDB Knowledge Base on 23 January 2023.','',''); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (2,9,'HELP_VERSION','Help Contents generated for MariaDB 10.8 from the MariaDB Knowledge Base on 23 January 2023.','',''); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (3,2,'AREA','A synonym for ST_AREA.\n\nURL: https://mariadb.com/kb/en/polygon-properties-area/','','https://mariadb.com/kb/en/polygon-properties-area/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (4,2,'CENTROID','A synonym for ST_CENTROID.\n\nURL: https://mariadb.com/kb/en/centroid/','','https://mariadb.com/kb/en/centroid/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (5,2,'ExteriorRing','A synonym for ST_ExteriorRing.\n\nURL: https://mariadb.com/kb/en/polygon-properties-exteriorring/','','https://mariadb.com/kb/en/polygon-properties-exteriorring/'); @@ -137,810 +137,811 @@ insert into help_topic (help_topic_id,help_category_id,name,description,example, insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (51,4,'COS','Syntax\n------\n\nCOS(X)\n\nDescription\n-----------\n\nReturns the cosine of X, where X is given in radians.\n\nExamples\n--------\n\nSELECT COS(PI());\n+-----------+\n| COS(PI()) |\n+-----------+\n| -1 |\n+-----------+\n\nURL: https://mariadb.com/kb/en/cos/','','https://mariadb.com/kb/en/cos/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (52,4,'COT','Syntax\n------\n\nCOT(X)\n\nDescription\n-----------\n\nReturns the cotangent of X.\n\nExamples\n--------\n\nSELECT COT(42);\n+--------------------+\n| COT(42) |\n+--------------------+\n| 0.4364167060752729 |\n+--------------------+\n\nSELECT COT(12);\n+---------------------+\n| COT(12) |\n+---------------------+\n| -1.5726734063976893 |\n+---------------------+\n\nSELECT COT(0);\nERROR 1690 (22003): DOUBLE value is out of range in \'cot(0)\'\n\nURL: https://mariadb.com/kb/en/cot/','','https://mariadb.com/kb/en/cot/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (53,4,'CRC32','Syntax\n------\n\n<= MariaDB 10.7\n\nCRC32(expr)\n\nFrom MariaDB 10.8\n\nCRC32([par,]expr)\n\nDescription\n-----------\n\nComputes a cyclic redundancy check (CRC) value and returns a 32-bit unsigned\nvalue. The result is NULL if the argument is NULL. The argument is expected to\nbe a string and (if possible) is treated as one if it is not.\n\nUses the ISO 3309 polynomial that used by zlib and many others. MariaDB 10.8\nintroduced the CRC32C() function, which uses the alternate Castagnoli\npolynomia.\n\nMariaDB starting with 10.8\n--------------------------\nOften, CRC is computed in pieces. To facilitate this, MariaDB 10.8.0\nintroduced an optional parameter: CRC32(\'MariaDB\')=CRC32(CRC32(\'Maria\'),\'DB\').\n\nExamples\n--------\n\nSELECT CRC32(\'MariaDB\');\n+------------------+\n| CRC32(\'MariaDB\') |\n+------------------+\n| 4227209140 |\n+------------------+\n\nSELECT CRC32(\'mariadb\');\n+------------------+\n| CRC32(\'mariadb\') |\n+------------------+\n| 2594253378 |\n+------------------+\n\nFrom MariaDB 10.8.0\n\nSELECT CRC32(CRC32(\'Maria\'),\'DB\');\n+----------------------------+\n| CRC32(CRC32(\'Maria\'),\'DB\') |\n+----------------------------+\n| 4227209140 |\n+----------------------------+\n\nURL: https://mariadb.com/kb/en/crc32/','','https://mariadb.com/kb/en/crc32/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (54,4,'DEGREES','Syntax\n------\n\nDEGREES(X)\n\nDescription\n-----------\n\nReturns the argument X, converted from radians to degrees.\n\nThis is the converse of the RADIANS() function.\n\nExamples\n--------\n\nSELECT DEGREES(PI());\n+---------------+\n| DEGREES(PI()) |\n+---------------+\n| 180 |\n+---------------+\n\nSELECT DEGREES(PI() / 2);\n+-------------------+\n| DEGREES(PI() / 2) |\n+-------------------+\n| 90 |\n+-------------------+\n\nSELECT DEGREES(45);\n+-----------------+\n| DEGREES(45) |\n+-----------------+\n| 2578.3100780887 |\n+-----------------+\n\nURL: https://mariadb.com/kb/en/degrees/','','https://mariadb.com/kb/en/degrees/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (55,4,'EXP','Syntax\n------\n\nEXP(X)\n\nDescription\n-----------\n\nReturns the value of e (the base of natural logarithms) raised to the power of\nX. The inverse of this function is LOG() (using a single argument only) or\nLN().\n\nIf X is NULL, this function returns NULL.\n\nExamples\n--------\n\nSELECT EXP(2);\n+------------------+\n| EXP(2) |\n+------------------+\n| 7.38905609893065 |\n+------------------+\n\nSELECT EXP(-2);\n+--------------------+\n| EXP(-2) |\n+--------------------+\n| 0.1353352832366127 |\n+--------------------+\n\nSELECT EXP(0);\n+--------+\n| EXP(0) |\n+--------+\n| 1 |\n+--------+\n\nSELECT EXP(NULL);\n+-----------+\n| EXP(NULL) |\n+-----------+\n| NULL |\n+-----------+\n\nURL: https://mariadb.com/kb/en/exp/','','https://mariadb.com/kb/en/exp/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (56,4,'FLOOR','Syntax\n------\n\nFLOOR(X)\n\nDescription\n-----------\n\nReturns the largest integer value not greater than X.\n\nExamples\n--------\n\nSELECT FLOOR(1.23);\n+-------------+\n| FLOOR(1.23) |\n+-------------+\n| 1 |\n+-------------+\n\nSELECT FLOOR(-1.23);\n+--------------+\n| FLOOR(-1.23) |\n+--------------+\n| -2 |\n+--------------+\n\nURL: https://mariadb.com/kb/en/floor/','','https://mariadb.com/kb/en/floor/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (57,4,'LN','Syntax\n------\n\nLN(X)\n\nDescription\n-----------\n\nReturns the natural logarithm of X; that is, the base-e logarithm of X. If X\nis less than or equal to 0, or NULL, then NULL is returned.\n\nThe inverse of this function is EXP().\n\nExamples\n--------\n\nSELECT LN(2);\n+-------------------+\n| LN(2) |\n+-------------------+\n| 0.693147180559945 |\n+-------------------+\n\nSELECT LN(-2);\n+--------+\n| LN(-2) |\n+--------+\n| NULL |\n+--------+\n\nURL: https://mariadb.com/kb/en/ln/','','https://mariadb.com/kb/en/ln/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (58,4,'LOG','Syntax\n------\n\nLOG(X), LOG(B,X)\n\nDescription\n-----------\n\nIf called with one parameter, this function returns the natural logarithm of\nX. If X is less than or equal to 0, then NULL is returned.\n\nIf called with two parameters, it returns the logarithm of X to the base B. If\nB is <= 1 or X <= 0, the function returns NULL.\n\nIf any argument is NULL, the function returns NULL.\n\nThe inverse of this function (when called with a single argument) is the EXP()\nfunction.\n\nExamples\n--------\n\nLOG(X):\n\nSELECT LOG(2);\n+-------------------+\n| LOG(2) |\n+-------------------+\n| 0.693147180559945 |\n+-------------------+\n\nSELECT LOG(-2);\n+---------+\n| LOG(-2) |\n+---------+\n| NULL |\n+---------+\n\nLOG(B,X)\n\nSELECT LOG(2,16);\n+-----------+\n| LOG(2,16) |\n+-----------+\n| 4 |\n+-----------+\n\nSELECT LOG(3,27);\n+-----------+\n| LOG(3,27) |\n+-----------+\n| 3 |\n+-----------+\n\nSELECT LOG(3,1);\n+----------+\n| LOG(3,1) |\n+----------+\n| 0 |\n+----------+\n\nSELECT LOG(3,0);\n+----------+\n| LOG(3,0) |\n+----------+\n| NULL |\n+----------+\n\nURL: https://mariadb.com/kb/en/log/','','https://mariadb.com/kb/en/log/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (59,4,'LOG10','Syntax\n------\n\nLOG10(X)\n\nDescription\n-----------\n\nReturns the base-10 logarithm of X.\n\nExamples\n--------\n\nSELECT LOG10(2);\n+-------------------+\n| LOG10(2) |\n+-------------------+\n| 0.301029995663981 |\n+-------------------+\n\nSELECT LOG10(100);\n+------------+\n| LOG10(100) |\n+------------+\n| 2 |\n+------------+\n\nSELECT LOG10(-100);\n+-------------+\n| LOG10(-100) |\n+-------------+\n| NULL |\n+-------------+\n\nURL: https://mariadb.com/kb/en/log10/','','https://mariadb.com/kb/en/log10/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (60,4,'LOG2','Syntax\n------\n\nLOG2(X)\n\nDescription\n-----------\n\nReturns the base-2 logarithm of X.\n\nExamples\n--------\n\nSELECT LOG2(4398046511104);\n+---------------------+\n| LOG2(4398046511104) |\n+---------------------+\n| 42 |\n+---------------------+\n\nSELECT LOG2(65536);\n+-------------+\n| LOG2(65536) |\n+-------------+\n| 16 |\n+-------------+\n\nSELECT LOG2(-100);\n+------------+\n| LOG2(-100) |\n+------------+\n| NULL |\n+------------+\n\nURL: https://mariadb.com/kb/en/log2/','','https://mariadb.com/kb/en/log2/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (61,4,'MOD','Syntax\n------\n\nMOD(N,M), N % M, N MOD M\n\nDescription\n-----------\n\nModulo operation. Returns the remainder of N divided by M. See also Modulo\nOperator.\n\nIf the ERROR_ON_DIVISION_BY_ZERO SQL_MODE is used, any number modulus zero\nproduces an error. Otherwise, it returns NULL.\n\nThe integer part of a division can be obtained using DIV.\n\nExamples\n--------\n\nSELECT 1042 % 50;\n+-----------+\n| 1042 % 50 |\n+-----------+\n| 42 |\n+-----------+\n\nSELECT MOD(234, 10);\n+--------------+\n| MOD(234, 10) |\n+--------------+\n| 4 |\n+--------------+\n\nSELECT 253 % 7;\n+---------+\n| 253 % 7 |\n+---------+\n| 1 |\n+---------+\n\nSELECT MOD(29,9);\n+-----------+\n| MOD(29,9) |\n+-----------+\n| 2 |\n+-----------+\n\nSELECT 29 MOD 9;\n+----------+\n| 29 MOD 9 |\n+----------+\n| 2 |\n+----------+\n\nURL: https://mariadb.com/kb/en/mod/','','https://mariadb.com/kb/en/mod/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (62,4,'OCT','Syntax\n------\n\nOCT(N)\n\nDescription\n-----------\n\nReturns a string representation of the octal value of N, where N is a longlong\n(BIGINT) number. This is equivalent to CONV(N,10,8). Returns NULL if N is NULL.\n\nExamples\n--------\n\nSELECT OCT(34);\n+---------+\n| OCT(34) |\n+---------+\n| 42 |\n+---------+\n\nSELECT OCT(12);\n+---------+\n| OCT(12) |\n+---------+\n| 14 |\n+---------+\n\nURL: https://mariadb.com/kb/en/oct/','','https://mariadb.com/kb/en/oct/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (63,4,'PI','Syntax\n------\n\nPI()\n\nDescription\n-----------\n\nReturns the value of π (pi). The default number of decimal places displayed is\nsix, but MariaDB uses the full double-precision value internally.\n\nExamples\n--------\n\nSELECT PI();\n+----------+\n| PI() |\n+----------+\n| 3.141593 |\n+----------+\n\nSELECT PI()+0.0000000000000000000000;\n+-------------------------------+\n| PI()+0.0000000000000000000000 |\n+-------------------------------+\n| 3.1415926535897931159980 |\n+-------------------------------+\n\nURL: https://mariadb.com/kb/en/pi/','','https://mariadb.com/kb/en/pi/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (64,4,'POW','Syntax\n------\n\nPOW(X,Y)\n\nDescription\n-----------\n\nReturns the value of X raised to the power of Y.\n\nPOWER() is a synonym.\n\nExamples\n--------\n\nSELECT POW(2,3);\n+----------+\n| POW(2,3) |\n+----------+\n| 8 |\n+----------+\n\nSELECT POW(2,-2);\n+-----------+\n| POW(2,-2) |\n+-----------+\n| 0.25 |\n+-----------+\n\nURL: https://mariadb.com/kb/en/pow/','','https://mariadb.com/kb/en/pow/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (65,4,'POWER','Syntax\n------\n\nPOWER(X,Y)\n\nDescription\n-----------\n\nThis is a synonym for POW(), which returns the value of X raised to the power\nof Y.\n\nURL: https://mariadb.com/kb/en/power/','','https://mariadb.com/kb/en/power/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (66,4,'RADIANS','Syntax\n------\n\nRADIANS(X)\n\nDescription\n-----------\n\nReturns the argument X, converted from degrees to radians. Note that π radians\nequals 180 degrees.\n\nThis is the converse of the DEGREES() function.\n\nExamples\n--------\n\nSELECT RADIANS(45);\n+-------------------+\n| RADIANS(45) |\n+-------------------+\n| 0.785398163397448 |\n+-------------------+\n\nSELECT RADIANS(90);\n+-----------------+\n| RADIANS(90) |\n+-----------------+\n| 1.5707963267949 |\n+-----------------+\n\nSELECT RADIANS(PI());\n+--------------------+\n| RADIANS(PI()) |\n+--------------------+\n| 0.0548311355616075 |\n+--------------------+\n\nSELECT RADIANS(180);\n+------------------+\n| RADIANS(180) |\n+------------------+\n| 3.14159265358979 |\n+------------------+\n\nURL: https://mariadb.com/kb/en/radians/','','https://mariadb.com/kb/en/radians/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (67,4,'RAND','Syntax\n------\n\nRAND(), RAND(N)\n\nDescription\n-----------\n\nReturns a random DOUBLE precision floating point value v in the range 0 <= v <\n1.0. If a constant integer argument N is specified, it is used as the seed\nvalue, which produces a repeatable sequence of column values. In the example\nbelow, note that the sequences of values produced by RAND(3) is the same both\nplaces where it occurs.\n\nIn a WHERE clause, RAND() is evaluated each time the WHERE is executed.\n\nStatements using the RAND() function are not safe for statement-based\nreplication.\n\nPractical uses\n--------------\n\nThe expression to get a random integer from a given range is the following:\n\nFLOOR(min_value + RAND() * (max_value - min_value +1))\n\nRAND() is often used to read random rows from a table, as follows:\n\nSELECT * FROM my_table ORDER BY RAND() LIMIT 10;\n\nNote, however, that this technique should never be used on a large table as it\nwill be extremely slow. MariaDB will read all rows in the table, generate a\nrandom value for each of them, order them, and finally will apply the LIMIT\nclause.\n\nExamples\n--------\n\nCREATE TABLE t (i INT);\n\nINSERT INTO t VALUES(1),(2),(3);\n\nSELECT i, RAND() FROM t;\n+------+-------------------+\n| i | RAND() |\n+------+-------------------+\n| 1 | 0.255651095188829 |\n| 2 | 0.833920199269355 |\n| 3 | 0.40264774151393 |\n+------+-------------------+\n\nSELECT i, RAND(3) FROM t;\n+------+-------------------+\n| i | RAND(3) |\n+------+-------------------+\n| 1 | 0.90576975597606 |\n| 2 | 0.373079058130345 |\n| 3 | 0.148086053457191 |\n+------+-------------------+\n\nSELECT i, RAND() FROM t;\n+------+-------------------+\n| i | RAND() |\n+------+-------------------+\n| 1 | 0.511478140495232 |\n| 2 | 0.349447508668012 |\n| 3 | 0.212803152588013 |\n+------+-------------------+\n\nUsing the same seed, the same sequence will be returned:\n\nSELECT i, RAND(3) FROM t;\n+------+-------------------+\n| i | RAND(3) |\n+------+-------------------+\n| 1 | 0.90576975597606 |\n| 2 | 0.373079058130345 |\n| 3 | 0.148086053457191 |\n+------+-------------------+\n\nGenerating a random number from 5 to 15:\n\nSELECT FLOOR(5 + (RAND() * 11));\n\nURL: https://mariadb.com/kb/en/rand/','','https://mariadb.com/kb/en/rand/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (68,4,'ROUND','Syntax\n------\n\nROUND(X), ROUND(X,D)\n\nDescription\n-----------\n\nRounds the argument X to D decimal places. The rounding algorithm depends on\nthe data type of X. D defaults to 0 if not specified. D can be negative to\ncause D digits left of the decimal point of the value X to become zero.\n\nExamples\n--------\n\nSELECT ROUND(-1.23);\n+--------------+\n| ROUND(-1.23) |\n+--------------+\n| -1 |\n+--------------+\n\nSELECT ROUND(-1.58);\n+--------------+\n| ROUND(-1.58) |\n+--------------+\n| -2 |\n+--------------+\n\nSELECT ROUND(1.58); \n+-------------+\n| ROUND(1.58) |\n+-------------+\n| 2 |\n+-------------+\n\nSELECT ROUND(1.298, 1);\n+-----------------+\n| ROUND(1.298, 1) |\n+-----------------+\n| 1.3 |\n+-----------------+\n\nSELECT ROUND(1.298, 0);\n+-----------------+\n| ROUND(1.298, 0) |\n+-----------------+\n| 1 |\n+-----------------+\n\nSELECT ROUND(23.298, -1);\n+-------------------+\n| ROUND(23.298, -1) |\n+-------------------+\n| 20 |\n+-------------------+\n\nURL: https://mariadb.com/kb/en/round/','','https://mariadb.com/kb/en/round/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (69,4,'SIGN','Syntax\n------\n\nSIGN(X)\n\nDescription\n-----------\n\nReturns the sign of the argument as -1, 0, or 1, depending on whether X is\nnegative, zero, or positive.\n\nExamples\n--------\n\nSELECT SIGN(-32);\n+-----------+\n| SIGN(-32) |\n+-----------+\n| -1 |\n+-----------+\n\nSELECT SIGN(0);\n+---------+\n| SIGN(0) |\n+---------+\n| 0 |\n+---------+\n\nSELECT SIGN(234);\n+-----------+\n| SIGN(234) |\n+-----------+\n| 1 |\n+-----------+\n\nURL: https://mariadb.com/kb/en/sign/','','https://mariadb.com/kb/en/sign/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (70,4,'SIN','Syntax\n------\n\nSIN(X)\n\nDescription\n-----------\n\nReturns the sine of X, where X is given in radians.\n\nExamples\n--------\n\nSELECT SIN(1.5707963267948966);\n+-------------------------+\n| SIN(1.5707963267948966) |\n+-------------------------+\n| 1 |\n+-------------------------+\n\nSELECT SIN(PI());\n+----------------------+\n| SIN(PI()) |\n+----------------------+\n| 1.22460635382238e-16 |\n+----------------------+\n\nSELECT ROUND(SIN(PI()));\n+------------------+\n| ROUND(SIN(PI())) |\n+------------------+\n| 0 |\n+------------------+\n\nURL: https://mariadb.com/kb/en/sin/','','https://mariadb.com/kb/en/sin/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (71,4,'SQRT','Syntax\n------\n\nSQRT(X)\n\nDescription\n-----------\n\nReturns the square root of X. If X is negative, NULL is returned.\n\nExamples\n--------\n\nSELECT SQRT(4);\n+---------+\n| SQRT(4) |\n+---------+\n| 2 |\n+---------+\n\nSELECT SQRT(20);\n+------------------+\n| SQRT(20) |\n+------------------+\n| 4.47213595499958 |\n+------------------+\n\nSELECT SQRT(-16);\n+-----------+\n| SQRT(-16) |\n+-----------+\n| NULL |\n+-----------+\n\nSELECT SQRT(1764);\n+------------+\n| SQRT(1764) |\n+------------+\n| 42 |\n+------------+\n\nURL: https://mariadb.com/kb/en/sqrt/','','https://mariadb.com/kb/en/sqrt/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (72,4,'TAN','Syntax\n------\n\nTAN(X)\n\nDescription\n-----------\n\nReturns the tangent of X, where X is given in radians.\n\nExamples\n--------\n\nSELECT TAN(0.7853981633974483);\n+-------------------------+\n| TAN(0.7853981633974483) |\n+-------------------------+\n| 0.9999999999999999 |\n+-------------------------+\n\nSELECT TAN(PI());\n+-----------------------+\n| TAN(PI()) |\n+-----------------------+\n| -1.22460635382238e-16 |\n+-----------------------+\n\nSELECT TAN(PI()+1);\n+-----------------+\n| TAN(PI()+1) |\n+-----------------+\n| 1.5574077246549 |\n+-----------------+\n\nSELECT TAN(RADIANS(PI()));\n+--------------------+\n| TAN(RADIANS(PI())) |\n+--------------------+\n| 0.0548861508080033 |\n+--------------------+\n\nURL: https://mariadb.com/kb/en/tan/','','https://mariadb.com/kb/en/tan/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (73,4,'TRUNCATE','This page documents the TRUNCATE function. See TRUNCATE TABLE for the DDL\nstatement.\n\nSyntax\n------\n\nTRUNCATE(X,D)\n\nDescription\n-----------\n\nReturns the number X, truncated to D decimal places. If D is 0, the result has\nno decimal point or fractional part. D can be negative to cause D digits left\nof the decimal point of the value X to become zero.\n\nExamples\n--------\n\nSELECT TRUNCATE(1.223,1);\n+-------------------+\n| TRUNCATE(1.223,1) |\n+-------------------+\n| 1.2 |\n+-------------------+\n\nSELECT TRUNCATE(1.999,1);\n+-------------------+\n| TRUNCATE(1.999,1) |\n+-------------------+\n| 1.9 |\n+-------------------+\n\nSELECT TRUNCATE(1.999,0); \n+-------------------+\n| TRUNCATE(1.999,0) |\n+-------------------+\n| 1 |\n+-------------------+\n\nSELECT TRUNCATE(-1.999,1);\n+--------------------+\n| TRUNCATE(-1.999,1) |\n+--------------------+\n| -1.9 |\n+--------------------+\n\nSELECT TRUNCATE(122,-2);\n+------------------+\n| TRUNCATE(122,-2) |\n+------------------+\n| 100 |\n+------------------+\n\nSELECT TRUNCATE(10.28*100,0);\n+-----------------------+\n| TRUNCATE(10.28*100,0) |\n+-----------------------+\n| 1028 |\n+-----------------------+\n\nURL: https://mariadb.com/kb/en/truncate/','','https://mariadb.com/kb/en/truncate/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (74,5,'INSTALL PLUGIN','Syntax\n------\n\nINSTALL PLUGIN [IF NOT EXISTS] plugin_name SONAME \'plugin_library\'\n\nDescription\n-----------\n\nThis statement installs an individual plugin from the specified library. To\ninstall the whole library (which could be required), use INSTALL SONAME. See\nalso Installing a Plugin.\n\nplugin_name is the name of the plugin as defined in the plugin declaration\nstructure contained in the library file. Plugin names are not case sensitive.\nFor maximal compatibility, plugin names should be limited to ASCII letters,\ndigits, and underscore, because they are used in C source files, shell command\nlines, M4 and Bourne shell scripts, and SQL environments.\n\nplugin_library is the name of the shared library that contains the plugin\ncode. The file name extension can be omitted (which makes the statement look\nthe same on all architectures).\n\nThe shared library must be located in the plugin directory (that is, the\ndirectory named by the plugin_dir system variable). The library must be in the\nplugin directory itself, not in a subdirectory. By default, plugin_dir is\nplugin directory under the directory named by the pkglibdir configuration\nvariable, but it can be changed by setting the value of plugin_dir at server\nstartup. For example, set its value in a my.cnf file:\n\n[mysqld]\nplugin_dir=/path/to/plugin/directory\nIf the value of plugin_dir is a relative path name, it is taken to be relative\nto the MySQL base directory (the value of the basedir system variable).\n\nINSTALL PLUGIN adds a line to the mysql.plugin table that describes the\nplugin. This table contains the plugin name and library file name.\n\nINSTALL PLUGIN causes the server to read option (my.cnf) files just as during\nserver startup. This enables the plugin to pick up any relevant options from\nthose files. It is possible to add plugin options to an option file even\nbefore loading a plugin (if the loose prefix is used). It is also possible to\nuninstall a plugin, edit my.cnf, and install the plugin again. Restarting the\nplugin this way enables it to the new option values without a server restart.\n\nINSTALL PLUGIN also loads and initializes the plugin code to make the plugin\navailable for use. A plugin is initialized by executing its initialization\nfunction, which handles any setup that the plugin must perform before it can\nbe used.\n\nTo use INSTALL PLUGIN, you must have the INSERT privilege for the mysql.plugin\ntable.\n\nAt server startup, the server loads and initializes any plugin that is listed\nin the mysql.plugin table. This means that a plugin is installed with INSTALL\nPLUGIN only once, not every time the server starts. Plugin loading at startup\ndoes not occur if the server is started with the --skip-grant-tables option.\n\nWhen the server shuts down, it executes the de-initialization function for\neach plugin that is loaded so that the plugin has a chance to perform any\nfinal cleanup.\n\nIf you need to load plugins for a single server startup when the\n--skip-grant-tables option is given (which tells the server not to read system\ntables), use the --plugin-load mysqld option.\n\nMariaDB starting with 10.4.0\n----------------------------\n\nIF NOT EXISTS\n-------------\n\nWhen the IF NOT EXISTS clause is used, MariaDB will return a note instead of\nan error if the specified plugin already exists. See SHOW WARNINGS.\n\nExamples\n--------\n\nINSTALL PLUGIN sphinx SONAME \'ha_sphinx.so\';\n\nThe extension can also be omitted:\n\nINSTALL PLUGIN innodb SONAME \'ha_xtradb\';\n\nFrom MariaDB 10.4.0:\n\nINSTALL PLUGIN IF NOT EXISTS example SONAME \'ha_example\';\nQuery OK, 0 rows affected (0.104 sec)\n\nINSTALL PLUGIN IF NOT EXISTS example SONAME \'ha_example\';\nQuery OK, 0 rows affected, 1 warning (0.000 sec)\n\nSHOW WARNINGS;\n+-------+------+------------------------------------+\n| Level | Code | Message |\n+-------+------+------------------------------------+\n| Note | 1968 | Plugin \'example\' already installed |\n+-------+------+------------------------------------+\n\nURL: https://mariadb.com/kb/en/install-plugin/','','https://mariadb.com/kb/en/install-plugin/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (75,5,'UNINSTALL PLUGIN','Syntax\n------\n\nUNINSTALL PLUGIN [IF EXISTS] plugin_name\n\nDescription\n-----------\n\nThis statement removes a single installed plugin. To uninstall the whole\nlibrary which contains the plugin, use UNINSTALL SONAME. You cannot uninstall\na plugin if any table that uses it is open.\n\nplugin_name must be the name of some plugin that is listed in the mysql.plugin\ntable. The server executes the plugin\'s deinitialization function and removes\nthe row for the plugin from the mysql.plugin table, so that subsequent server\nrestarts will not load and initialize the plugin. UNINSTALL PLUGIN does not\nremove the plugin\'s shared library file.\n\nTo use UNINSTALL PLUGIN, you must have the DELETE privilege for the\nmysql.plugin table.\n\nMariaDB starting with 10.4.0\n----------------------------\n\nIF EXISTS\n---------\n\nIf the IF EXISTS clause is used, MariaDB will return a note instead of an\nerror if the plugin does not exist. See SHOW WARNINGS.\n\nExamples\n--------\n\nUNINSTALL PLUGIN example;\n\nFrom MariaDB 10.4.0:\n\nUNINSTALL PLUGIN IF EXISTS example;\nQuery OK, 0 rows affected (0.099 sec)\n\nUNINSTALL PLUGIN IF EXISTS example;\nQuery OK, 0 rows affected, 1 warning (0.000 sec)\n\nSHOW WARNINGS;\n+-------+------+-------------------------------+\n| Level | Code | Message |\n+-------+------+-------------------------------+\n| Note | 1305 | PLUGIN example does not exist |\n+-------+------+-------------------------------+\n\nURL: https://mariadb.com/kb/en/uninstall-plugin/','','https://mariadb.com/kb/en/uninstall-plugin/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (76,5,'INSTALL SONAME','Syntax\n------\n\nINSTALL SONAME \'plugin_library\'\n\nDescription\n-----------\n\nThis statement is a variant of INSTALL PLUGIN. It installs all plugins from a\ngiven plugin_library. See INSTALL PLUGIN for details.\n\nplugin_library is the name of the shared library that contains the plugin\ncode. The file name extension (for example, libmyplugin.so or libmyplugin.dll)\ncan be omitted (which makes the statement look the same on all architectures).\n\nThe shared library must be located in the plugin directory (that is, the\ndirectory named by the plugin_dir system variable). The library must be in the\nplugin directory itself, not in a subdirectory. By default, plugin_dir is\nplugin directory under the directory named by the pkglibdir configuration\nvariable, but it can be changed by setting the value of plugin_dir at server\nstartup. For example, set its value in a my.cnf file:\n\n[mysqld]\nplugin_dir=/path/to/plugin/directory\nIf the value of plugin_dir is a relative path name, it is taken to be relative\nto the MySQL base directory (the value of the basedir system variable).\n\nINSTALL SONAME adds one or more lines to the mysql.plugin table that describes\nthe plugin. This table contains the plugin name and library file name.\n\nINSTALL SONAME causes the server to read option (my.cnf) files just as during\nserver startup. This enables the plugin to pick up any relevant options from\nthose files. It is possible to add plugin options to an option file even\nbefore loading a plugin (if the loose prefix is used). It is also possible to\nuninstall a plugin, edit my.cnf, and install the plugin again. Restarting the\nplugin this way enables it to the new option values without a server restart.\n\nINSTALL SONAME also loads and initializes the plugin code to make the plugin\navailable for use. A plugin is initialized by executing its initialization\nfunction, which handles any setup that the plugin must perform before it can\nbe used.\n\nTo use INSTALL SONAME, you must have the INSERT privilege for the mysql.plugin\ntable.\n\nAt server startup, the server loads and initializes any plugin that is listed\nin the mysql.plugin table. This means that a plugin is installed with INSTALL\nSONAME only once, not every time the server starts. Plugin loading at startup\ndoes not occur if the server is started with the --skip-grant-tables option.\n\nWhen the server shuts down, it executes the de-initialization function for\neach plugin that is loaded so that the plugin has a chance to perform any\nfinal cleanup.\n\nIf you need to load plugins for a single server startup when the\n--skip-grant-tables option is given (which tells the server not to read system\ntables), use the --plugin-load mysqld option.\n\nIf you need to install only one plugin from a library, use the INSTALL PLUGIN\nstatement.\n\nExamples\n--------\n\nTo load the XtraDB storage engine and all of its information_schema tables\nwith one statement, use\n\nINSTALL SONAME \'ha_xtradb\';\n\nThis statement can be used instead of INSTALL PLUGIN even when the library\ncontains only one plugin:\n\nINSTALL SONAME \'ha_sequence\';\n\nURL: https://mariadb.com/kb/en/install-soname/','','https://mariadb.com/kb/en/install-soname/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (77,5,'UNINSTALL SONAME','Syntax\n------\n\nUNINSTALL SONAME [IF EXISTS] \'plugin_library\'\n\nDescription\n-----------\n\nThis statement is a variant of UNINSTALL PLUGIN statement, that removes all\nplugins belonging to a specified plugin_library. See UNINSTALL PLUGIN for\ndetails.\n\nplugin_library is the name of the shared library that contains the plugin\ncode. The file name extension (for example, libmyplugin.so or libmyplugin.dll)\ncan be omitted (which makes the statement look the same on all architectures).\n\nTo use UNINSTALL SONAME, you must have the DELETE privilege for the\nmysql.plugin table.\n\nMariaDB starting with 10.4.0\n----------------------------\n\nIF EXISTS\n---------\n\nIf the IF EXISTS clause is used, MariaDB will return a note instead of an\nerror if the plugin library does not exist. See SHOW WARNINGS.\n\nExamples\n--------\n\nTo uninstall the XtraDB plugin and all of its information_schema tables with\none statement, use\n\nUNINSTALL SONAME \'ha_xtradb\';\n\nFrom MariaDB 10.4.0:\n\nUNINSTALL SONAME IF EXISTS \'ha_example\';\nQuery OK, 0 rows affected (0.099 sec)\n\nUNINSTALL SONAME IF EXISTS \'ha_example\';\nQuery OK, 0 rows affected, 1 warning (0.000 sec)\n\nSHOW WARNINGS;\n+-------+------+-------------------------------------+\n| Level | Code | Message |\n+-------+------+-------------------------------------+\n| Note | 1305 | SONAME ha_example.so does not exist |\n+-------+------+-------------------------------------+\n\nURL: https://mariadb.com/kb/en/uninstall-soname/','','https://mariadb.com/kb/en/uninstall-soname/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (78,5,'Plugin Overview','Plugins are server components that enhance MariaDB in some way. These can be\nanything from new storage engines, plugins for enhancing full-text parsing, or\neven small enhancements, such as a plugin to get a timestamp as an integer.\n\nQuerying Plugin Information\n---------------------------\n\nThere are a number of ways to see which plugins are currently active.\n\nA server almost always has a large number of active plugins, because the\nserver contains a large number of built-in plugins, which are active by\ndefault and cannot be uninstalled.\n\nQuerying Plugin Information with SHOW PLUGINS\n---------------------------------------------\n\nThe SHOW PLUGINS statement can be used to query information about all active\nplugins.\n\nFor example:\n\nSHOW PLUGINS\\G;\n********************** 1. row **********************\n Name: binlog\n Status: ACTIVE\n Type: STORAGE ENGINE\nLibrary: NULL\nLicense: GPL\n********************** 2. row **********************\n Name: mysql_native_password\n Status: ACTIVE\n Type: AUTHENTICATION\nLibrary: NULL\nLicense: GPL\n********************** 3. row **********************\n Name: mysql_old_password\n Status: ACTIVE\n Type: AUTHENTICATION\nLibrary: NULL\nLicense: GPL\n...\n\nIf a plugin\'s Library column has a NULL value, then the plugin is built-in,\nand it cannot be uninstalled.\n\nQuerying Plugin Information with information_schema.PLUGINS\n-----------------------------------------------------------\n\nThe information_schema.PLUGINS table can be queried to get more detailed\ninformation about plugins.\n\nFor example:\n\nSELECT * FROM information_schema.PLUGINS\\G\n...\n*************************** 6. row ***************************\n PLUGIN_NAME: CSV\n PLUGIN_VERSION: 1.0\n PLUGIN_STATUS: ACTIVE\n PLUGIN_TYPE: STORAGE ENGINE\n PLUGIN_TYPE_VERSION: 100003.0\n PLUGIN_LIBRARY: NULL\nPLUGIN_LIBRARY_VERSION: NULL\n PLUGIN_AUTHOR: Brian Aker, MySQL AB\n PLUGIN_DESCRIPTION: CSV storage engine\n PLUGIN_LICENSE: GPL\n LOAD_OPTION: FORCE\n PLUGIN_MATURITY: Stable\n PLUGIN_AUTH_VERSION: 1.0\n*************************** 7. row ***************************\n PLUGIN_NAME: MEMORY\n PLUGIN_VERSION: 1.0\n PLUGIN_STATUS: ACTIVE\n PLUGIN_TYPE: STORAGE ENGINE\n PLUGIN_TYPE_VERSION: 100003.0\n PLUGIN_LIBRARY: NULL\nPLUGIN_LIBRARY_VERSION: NULL\n PLUGIN_AUTHOR: MySQL AB\n PLUGIN_DESCRIPTION: Hash based, stored in memory, useful for temporary\ntables\n PLUGIN_LICENSE: GPL\n LOAD_OPTION: FORCE\n PLUGIN_MATURITY: Stable\n PLUGIN_AUTH_VERSION: 1.0\n...\n\nIf a plugin\'s PLUGIN_LIBRARY column has the NULL value, then the plugin is\nbuilt-in, and it cannot be uninstalled.\n\nQuerying Plugin Information with mysql.plugin\n---------------------------------------------\n\nThe mysql.plugin table can be queried to get information about installed\nplugins.\n\nThis table only contains information about plugins that have been installed\nvia the following methods:\n\n* The INSTALL SONAME statement.\n* The INSTALL PLUGIN statement.\n* The mysql_plugin utility.\n\nThis table does not contain information about:\n\n* Built-in plugins.\n* Plugins loaded with the --plugin-load-add option.\n* Plugins loaded with the --plugin-load option.\n\nThis table only contains enough information to reload the plugin when the\nserver is restarted, which means it only contains the plugin name and the\nplugin library.\n\nFor example:\n\nSELECT * FROM mysql.plugin;\n\n+------+------------+\n| name | dl |\n+------+------------+\n| PBXT | libpbxt.so |\n+------+------------+\n\nInstalling a Plugin\n-------------------\n\nThere are three primary ways to install a plugin:\n\n* A plugin can be installed dynamically with an SQL statement.\n* A plugin can be installed with a mysqld option, but it requires a server\nrestart.\n* A plugin can be installed with the mysql_plugin utility, while the server is\ncompletely offline.\n\nWhen you are installing a plugin, you also have to ensure that:\n\n* The server\'s plugin directory is properly configured, and the plugin\'s\nlibrary is in the plugin directory.\n* The server\'s minimum plugin maturity is properly configured, and the plugin\nis mature enough to be installed.\n\nInstalling a Plugin Dynamically\n-------------------------------\n\nA plugin can be installed dynamically by executing either the INSTALL SONAME\nor the INSTALL PLUGIN statement.\n\nIf a plugin is installed with one of these statements, then a record will be\nadded to the mysql.plugins table for the plugin. This means that the plugin\nwill automatically be loaded every time the server restarts, unless\nspecifically uninstalled or deactivated.\n\nInstalling a Plugin with INSTALL SONAME\n---------------------------------------\n\nYou can install a plugin dynamically by executing the INSTALL SONAME\nstatement. INSTALL SONAME installs all plugins from the given plugin library.\nThis could be required for some plugin libraries.\n\nFor example, to install all plugins in the server_audit plugin library (which\nis currently only the server_audit audit plugin), you could execute the\nfollowing:\n\nINSTALL SONAME \'server_audit\';\n\nInstalling a Plugin with INSTALL PLUGIN\n---------------------------------------\n\nYou can install a plugin dynamically by executing the INSTALL PLUGIN\nstatement. INSTALL PLUGIN installs a single plugin from the given plugin\nlibrary.\n\nFor example, to install the server_audit audit plugin from the server_audit\nplugin library, you could execute the following:\n\nINSTALL PLUGIN server_audit SONAME \'server_audit\';\n\nInstalling a Plugin with Plugin Load Options\n--------------------------------------------\n\nA plugin can be installed with a mysqld option by providing either the\n--plugin-load-add or the --plugin-load option.\n\nIf a plugin is installed with one of these options, then a record will not be\nadded to the mysql.plugins table for the plugin. This means that if the server\nis restarted without the same option set, then the plugin will not\nautomatically be loaded.\n\nInstalling a Plugin with --plugin-load-add\n------------------------------------------\n\nYou can install a plugin with the --plugin-load-add option by specifying the\noption as a command-line argument to mysqld or by specifying the option in a\nrelevant server option group in an option file.\n\nThe --plugin-load-add option uses the following format:\n\n* Plugins can be specified in the format name=library, where name is the\nplugin name and library is the plugin library. This format installs a single\nplugin from the given plugin library.\n* Plugins can also be specified in the format library, where library is the\nplugin library. This format installs all plugins from the given plugin library.\n* Multiple plugins can be specified by separating them with semicolons.\n\nFor example, to install all plugins in the server_audit plugin library (which\nis currently only the server_audit audit plugin) and also the ed25519\nauthentication plugin from the auth_ed25519 plugin library, you could set the\noption to the following values on the command-line:\n\n$ mysqld --user=mysql --plugin-load-add=\'server_audit\'\n--plugin-load-add=\'ed25519=auth_ed25519\'\n\nYou could also set the option to the same values in an option file:\n\n[mariadb]\n...\nplugin_load_add = server_audit\nplugin_load_add = ed25519=auth_ed25519\n\nSpecial care must be taken when specifying both the --plugin-load option and\nthe --plugin-load-add option together. The --plugin-load option resets the\nplugin load list, and this can cause unexpected problems if you are not aware.\nThe --plugin-load-add option does not reset the plugin load list, so it is\nmuch safer to use. See Specifying Multiple Plugin Load Options for more\ninformation.\n\nInstalling a Plugin with --plugin-load\n--------------------------------------\n\nYou can install a plugin with the --plugin-load option by specifying the\noption as a command-line argument to mysqld or by specifying the option in a\nrelevant server option group in an option file.\n\nThe --plugin-load option uses the following format:\n\n* Plugins can be specified in the format name=library, where name is the\nplugin name and library is the plugin library. This format installs a single\nplugin from the given plugin library.\n* Plugins can also be specified in the format library, where library is the\nplugin library. This format installs all plugins from the given plugin library.\n* Multiple plugins can be specified by separating them with semicolons.\n\nFor example, to install all plugins in the server_audit plugin library (which\nis currently only the server_audit audit plugin) and also the ed25519\nauthentication plugin from the auth_ed25519 plugin library, you could set the\noption to the following values on the command-line:\n\n$ mysqld --user=mysql --plugin-load=\'server_audit;ed25519=auth_ed25519\'\n\nYou could also set the option to the same values in an option file:\n\n[mariadb]\n...\nplugin_load = server_audit;ed25519=auth_ed25519\n\nSpecial care must be taken when specifying the --plugin-load option multiple\ntimes, or when specifying both the --plugin-load option and the\n--plugin-load-add option together. The --plugin-load option resets the plugin\nload list, and this can cause unexpected problems if you are not aware. The\n--plugin-load-add option does not reset the plugin load list, so it is much\nsafer to use. See Specifying Multiple Plugin Load Options for more information.\n\nSpecifying Multiple Plugin Load Options\n---------------------------------------\n\nSpecial care must be taken when specifying the --plugin-load option multiple\ntimes, or when specifying both the --plugin-load option and the\n--plugin-load-add option. The --plugin-load option resets the plugin load\nlist, and this can cause unexpected problems if you are not aware. The\n--plugin-load-add option does not reset the plugin load list, so it is much\nsafer to use.\n\nThis can have the following consequences:\n\n* If the --plugin-load option is specified multiple times, then only the last\ninstance will have any effect. For example, in the following case, the first\ninstance of the option is reset:\n\n[mariadb]\n...\nplugin_load = server_audit\nplugin_load = ed25519=auth_ed25519\n\n* If the --plugin-load option is specified after the --plugin-load-add option,\nthen it will also reset the changes made by that option. For example, in the\nfollowing case, the --plugin-load-add option does not do anything, because the\nsubsequent --plugin-load option resets the plugin load list:\n\n[mariadb]\n...\nplugin_load_add = server_audit\nplugin_load = ed25519=auth_ed25519\n\n* In contrast, if the --plugin-load option is specified before the\n--plugin-load-add option, then it will work fine, because the\n--plugin-load-add option does not reset the plugin load list. For example, in\nthe following case, both plugins are properly loaded:\n\n[mariadb]\n...\nplugin_load = server_audit\nplugin_load_add = ed25519=auth_ed25519\n\nInstalling a Plugin with mysql_plugin\n-------------------------------------\n\nA plugin can be installed with the mysql_plugin utility if the server is\ncompletely offline.\n\nThe syntax is:\n\nmysql_plugin [options] ENABLE|DISABLE\n\nFor example, to install the server_audit audit plugin, you could execute the\nfollowing:\n\nmysql_plugin server_audit ENABLE\n\nIf a plugin is installed with this utility, then a record will be added to the\nmysql.plugins table for the plugin. This means that the plugin will\nautomatically be loaded every time the server restarts, unless specifically\nuninstalled or deactivated.\n\nConfiguring the Plugin Directory\n--------------------------------\n\nWhen a plugin is being installed, the server looks for the plugin\'s library in\nthe server\'s plugin directory. This directory is configured by the plugin_dir\nsystem variable. This can be specified as a command-line argument to mysqld or\nit can be specified in a relevant server option group in an option file. For\nexample:\n\n[mariadb]\n...\nplugin_dir = /usr/lib64/mysql/plugin\n\nConfiguring the Minimum Plugin Maturity\n---------------------------------------\n\nWhen a plugin is being installed, the server compares the plugin\'s maturity\nlevel against the server\'s minimum allowed plugin maturity. This can help\nprevent users from using unstable plugins on production servers. This minimum\nplugin maturity is configured by the plugin_maturity system variable. This can\nbe specified as a command-line argument to mysqld or it can be specified in a\nrelevant server option group in an option file. For example:\n\n[mariadb]\n...\nplugin_maturity = stable\n\nConfiguring Plugin Activation at Server Startup\n-----------------------------------------------\n\nA plugin will be loaded by default when the server starts if:\n\n* The plugin was installed with the INSTALL SONAME statement.\n* The plugin was installed with the INSTALL PLUGIN statement.\n* The plugin was installed with the mysql_plugin utility.\n* The server is configured to load the plugin with the --plugin-load-add\noption.\n* The server is configured to load the plugin with the --plugin-load option.\n\nThis behavior can be changed with special options that take the form\n--plugin-name. For example, for the server_audit audit plugin, the special\noption is called --server-audit.\n\nThe possible values for these special options are:\n\n+---------------------------------------+------------------------------------+\n| Option Value | Description |\n+---------------------------------------+------------------------------------+\n| OFF | Disables the plugin without |\n| | removing it from the |\n| | mysql.plugins table. |\n+---------------------------------------+------------------------------------+\n| ON | Enables the plugin. If the plugin |\n| | cannot be initialized, then the |\n| | server will still continue |\n| | starting up, but the plugin will |\n| | be disabled. |\n+---------------------------------------+------------------------------------+\n| FORCE | Enables the plugin. If the plugin |','','https://mariadb.com/kb/en/plugin-overview/'); -update help_topic set description = CONCAT(description, '\n| | cannot be initialized, then the |\n| | server will fail to start with an |\n| | error. |\n+---------------------------------------+------------------------------------+\n| FORCE_PLUS_PERMANENT | Enables the plugin. If the plugin |\n| | cannot be initialized, then the |\n| | server will fail to start with an |\n| | error. In addition, the plugin |\n| | cannot be uninstalled with |\n| | UNINSTALL SONAME or UNINSTALL |\n| | PLUGIN while the server is |\n| | running. |\n+---------------------------------------+------------------------------------+\n\nA plugin\'s status can be found by looking at the PLUGIN_STATUS column of the\ninformation_schema.PLUGINS table.\n\nUninstalling Plugins\n--------------------\n\nPlugins that are found in the mysql.plugin table, that is those that were\ninstalled with INSTALL SONAME, INSTALL PLUGIN or mysql_plugin can be\nuninstalled in one of two ways:\n\n* The UNINSTALL SONAME or the UNINSTALL PLUGIN statement while the server is\nrunning\n* With mysql_plugin while the server is offline.\n\nPlugins that were enabled as a --plugin-load option do not need to be\nuninstalled. If --plugin-load is omitted the next time the server starts, or\nthe plugin is not listed as one of the --plugin-load entries, the plugin will\nnot be loaded.\n\nUNINSTALL PLUGIN uninstalls a single installed plugin, while UNINSTALL SONAME\nuninstalls all plugins belonging to a given library.\n\nURL: https://mariadb.com/kb/en/plugin-overview/') WHERE help_topic_id = 78; -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (79,6,'MBR Definition','Description\n-----------\n\nThe MBR (Minimum Bounding Rectangle), or Envelope is the bounding geometry,\nformed by the minimum and maximum (X,Y) coordinates:\n\nExamples\n--------\n\n((MINX MINY, MAXX MINY, MAXX MAXY, MINX MAXY, MINX MINY))\n\nURL: https://mariadb.com/kb/en/mbr-definition/','','https://mariadb.com/kb/en/mbr-definition/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (80,6,'MBRContains','Syntax\n------\n\nMBRContains(g1,g2)\n\nDescription\n-----------\n\nReturns 1 or 0 to indicate whether the Minimum Bounding Rectangle of g1\ncontains the Minimum Bounding Rectangle of g2. This tests the opposite\nrelationship as MBRWithin().\n\nExamples\n--------\n\nSET @g1 = GeomFromText(\'Polygon((0 0,0 3,3 3,3 0,0 0))\');\n\nSET @g2 = GeomFromText(\'Point(1 1)\');\n\nSELECT MBRContains(@g1,@g2), MBRContains(@g2,@g1);\n+----------------------+----------------------+\n| MBRContains(@g1,@g2) | MBRContains(@g2,@g1) |\n+----------------------+----------------------+\n| 1 | 0 |\n+----------------------+----------------------+\n\nURL: https://mariadb.com/kb/en/mbrcontains/','','https://mariadb.com/kb/en/mbrcontains/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (81,6,'MBRDisjoint','Syntax\n------\n\nMBRDisjoint(g1,g2)\n\nDescription\n-----------\n\nReturns 1 or 0 to indicate whether the Minimum Bounding Rectangles of the two\ngeometries g1 and g2 are disjoint. Two geometries are disjoint if they do not\nintersect, that is touch or overlap.\n\nExamples\n--------\n\nSET @g1 = GeomFromText(\'Polygon((0 0,0 3,3 3,3 0,0 0))\');\nSET @g2 = GeomFromText(\'Polygon((4 4,4 7,7 7,7 4,4 4))\');\nSELECTmbrdisjoint(@g1,@g2);\n+----------------------+\n| mbrdisjoint(@g1,@g2) |\n+----------------------+\n| 1 |\n+----------------------+\n\nSET @g1 = GeomFromText(\'Polygon((0 0,0 3,3 3,3 0,0 0))\');\nSET @g2 = GeomFromText(\'Polygon((3 3,3 6,6 6,6 3,3 3))\');\nSELECT mbrdisjoint(@g1,@g2);\n+----------------------+\n| mbrdisjoint(@g1,@g2) |\n+----------------------+\n| 0 |\n+----------------------+\n\nURL: https://mariadb.com/kb/en/mbrdisjoint/','','https://mariadb.com/kb/en/mbrdisjoint/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (82,6,'MBREqual','Syntax\n------\n\nMBREqual(g1,g2)\n\nDescription\n-----------\n\nReturns 1 or 0 to indicate whether the Minimum Bounding Rectangles of the two\ngeometries g1 and g2 are the same.\n\nExamples\n--------\n\nSET @g1=GEOMFROMTEXT(\'LINESTRING(0 0, 1 2)\');\nSET @g2=GEOMFROMTEXT(\'POLYGON((0 0, 0 2, 1 2, 1 0, 0 0))\');\nSELECT MbrEqual(@g1,@g2);\n+-------------------+\n| MbrEqual(@g1,@g2) |\n+-------------------+\n| 1 |\n+-------------------+\n\nSET @g1=GEOMFROMTEXT(\'LINESTRING(0 0, 1 3)\');\nSET @g2=GEOMFROMTEXT(\'POLYGON((0 0, 0 2, 1 4, 1 0, 0 0))\');\nSELECT MbrEqual(@g1,@g2);\n+-------------------+\n| MbrEqual(@g1,@g2) |\n+-------------------+\n| 0 |\n+-------------------+\n\nURL: https://mariadb.com/kb/en/mbrequal/','','https://mariadb.com/kb/en/mbrequal/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (83,6,'MBRIntersects','Syntax\n------\n\nMBRIntersects(g1,g2)\n\nDescription\n-----------\n\nReturns 1 or 0 to indicate whether the Minimum Bounding Rectangles of the two\ngeometries g1 and g2 intersect.\n\nExamples\n--------\n\nSET @g1 = GeomFromText(\'Polygon((0 0,0 3,3 3,3 0,0 0))\');\nSET @g2 = GeomFromText(\'Polygon((3 3,3 6,6 6,6 3,3 3))\');\nSELECT mbrintersects(@g1,@g2);\n+------------------------+\n| mbrintersects(@g1,@g2) |\n+------------------------+\n| 1 |\n+------------------------+\n\nSET @g1 = GeomFromText(\'Polygon((0 0,0 3,3 3,3 0,0 0))\');\nSET @g2 = GeomFromText(\'Polygon((4 4,4 7,7 7,7 4,4 4))\');\nSELECT mbrintersects(@g1,@g2);\n+------------------------+\n| mbrintersects(@g1,@g2) |\n+------------------------+\n| 0 |\n+------------------------+\n\nURL: https://mariadb.com/kb/en/mbrintersects/','','https://mariadb.com/kb/en/mbrintersects/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (84,6,'MBROverlaps','Syntax\n------\n\nMBROverlaps(g1,g2)\n\nDescription\n-----------\n\nReturns 1 or 0 to indicate whether the Minimum Bounding Rectangles of the two\ngeometries g1 and g2 overlap. The term spatially overlaps is used if two\ngeometries intersect and their intersection results in a geometry of the same\ndimension but not equal to either of the given geometries.\n\nExamples\n--------\n\nSET @g1 = GeomFromText(\'Polygon((0 0,0 3,3 3,3 0,0 0))\');\nSET @g2 = GeomFromText(\'Polygon((4 4,4 7,7 7,7 4,4 4))\');\nSELECT mbroverlaps(@g1,@g2);\n+----------------------+\n| mbroverlaps(@g1,@g2) |\n+----------------------+\n| 0 |\n+----------------------+\n\nSET @g1 = GeomFromText(\'Polygon((0 0,0 3,3 3,3 0,0 0))\');\nSET @g2 = GeomFromText(\'Polygon((3 3,3 6,6 6,6 3,3 3))\');\nSELECT mbroverlaps(@g1,@g2);\n+----------------------+\n| mbroverlaps(@g1,@g2) |\n+----------------------+\n| 0 |\n+----------------------+\n\nSET @g1 = GeomFromText(\'Polygon((0 0,0 4,4 4,4 0,0 0))\');\nSET @g2 = GeomFromText(\'Polygon((3 3,3 6,6 6,6 3,3 3))\');\nSELECT mbroverlaps(@g1,@g2);\n+----------------------+\n| mbroverlaps(@g1,@g2) |\n+----------------------+\n| 1 |\n+----------------------+\n\nURL: https://mariadb.com/kb/en/mbroverlaps/','','https://mariadb.com/kb/en/mbroverlaps/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (85,6,'MBRTouches','Syntax\n------\n\nMBRTouches(g1,g2)\n\nDescription\n-----------\n\nReturns 1 or 0 to indicate whether the Minimum Bounding Rectangles of the two\ngeometries g1 and g2 touch. Two geometries spatially touch if the interiors of\nthe geometries do not intersect, but the boundary of one of the geometries\nintersects either the boundary or the interior of the other.\n\nExamples\n--------\n\nSET @g1 = GeomFromText(\'Polygon((0 0,0 3,3 3,3 0,0 0))\');\nSET @g2 = GeomFromText(\'Polygon((4 4,4 7,7 7,7 4,4 4))\');\nSELECT mbrtouches(@g1,@g2);\n+---------------------+\n| mbrtouches(@g1,@g2) |\n+---------------------+\n| 0 |\n+---------------------+\n\nSET @g1 = GeomFromText(\'Polygon((0 0,0 3,3 3,3 0,0 0))\');\nSET @g2 = GeomFromText(\'Polygon((3 3,3 6,6 6,6 3,3 3))\');\nSELECT mbrtouches(@g1,@g2);\n+---------------------+\n| mbrtouches(@g1,@g2) |\n+---------------------+\n| 1 |\n+---------------------+\n\nSET @g1 = GeomFromText(\'Polygon((0 0,0 4,4 4,4 0,0 0))\');\nSET @g2 = GeomFromText(\'Polygon((3 3,3 6,6 6,6 3,3 3))\');\nSELECT mbrtouches(@g1,@g2);\n+---------------------+\n| mbrtouches(@g1,@g2) |\n+---------------------+\n| 0 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/mbrtouches/','','https://mariadb.com/kb/en/mbrtouches/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (86,6,'MBRWithin','Syntax\n------\n\nMBRWithin(g1,g2)\n\nDescription\n-----------\n\nReturns 1 or 0 to indicate whether the Minimum Bounding Rectangle of g1 is\nwithin the Minimum Bounding Rectangle of g2. This tests the opposite\nrelationship as MBRContains().\n\nExamples\n--------\n\nSET @g1 = GeomFromText(\'Polygon((0 0,0 3,3 3,3 0,0 0))\');\nSET @g2 = GeomFromText(\'Polygon((0 0,0 5,5 5,5 0,0 0))\');\nSELECT MBRWithin(@g1,@g2), MBRWithin(@g2,@g1);\n+--------------------+--------------------+\n| MBRWithin(@g1,@g2) | MBRWithin(@g2,@g1) |\n+--------------------+--------------------+\n| 1 | 0 |\n+--------------------+--------------------+\n\nURL: https://mariadb.com/kb/en/mbrwithin/','','https://mariadb.com/kb/en/mbrwithin/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (87,7,'CASE OPERATOR','Syntax\n------\n\nCASE value WHEN [compare_value] THEN result [WHEN [compare_value] THEN\nresult ...] [ELSE result] END\n\nCASE WHEN [condition] THEN result [WHEN [condition] THEN result ...]\n[ELSE result] END\n\nDescription\n-----------\n\nThe first version returns the result where value=compare_value. The second\nversion returns the result for the first condition that is true. If there was\nno matching result value, the result after ELSE is returned, or NULL if there\nis no ELSE part.\n\nThere is also a CASE statement, which differs from the CASE operator described\nhere.\n\nExamples\n--------\n\nSELECT CASE 1 WHEN 1 THEN \'one\' WHEN 2 THEN \'two\' ELSE \'more\' END;\n+------------------------------------------------------------+\n| CASE 1 WHEN 1 THEN \'one\' WHEN 2 THEN \'two\' ELSE \'more\' END |\n+------------------------------------------------------------+\n| one |\n+------------------------------------------------------------+\n\nSELECT CASE WHEN 1>0 THEN \'true\' ELSE \'false\' END;\n+--------------------------------------------+\n| CASE WHEN 1>0 THEN \'true\' ELSE \'false\' END |\n+--------------------------------------------+\n| true |\n+--------------------------------------------+\n\nSELECT CASE BINARY \'B\' WHEN \'a\' THEN 1 WHEN \'b\' THEN 2 END;\n+-----------------------------------------------------+\n| CASE BINARY \'B\' WHEN \'a\' THEN 1 WHEN \'b\' THEN 2 END |\n+-----------------------------------------------------+\n| NULL |\n+-----------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/case-operator/','','https://mariadb.com/kb/en/case-operator/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (88,7,'IF Function','Syntax\n------\n\nIF(expr1,expr2,expr3)\n\nDescription\n-----------\n\nIf expr1 is TRUE (expr1 <> 0 and expr1 <> NULL) then IF() returns expr2;\notherwise it returns expr3. IF() returns a numeric or string value, depending\non the context in which it is used.\n\nNote: There is also an IF statement which differs from the IF() function\ndescribed here.\n\nExamples\n--------\n\nSELECT IF(1>2,2,3);\n+-------------+\n| IF(1>2,2,3) |\n+-------------+\n| 3 |\n+-------------+\n\nSELECT IF(1<2,\'yes\',\'no\');\n+--------------------+\n| IF(1<2,\'yes\',\'no\') |\n+--------------------+\n| yes |\n+--------------------+\n\nSELECT IF(STRCMP(\'test\',\'test1\'),\'no\',\'yes\');\n+---------------------------------------+\n| IF(STRCMP(\'test\',\'test1\'),\'no\',\'yes\') |\n+---------------------------------------+\n| no |\n+---------------------------------------+\n\nURL: https://mariadb.com/kb/en/if-function/','','https://mariadb.com/kb/en/if-function/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (89,7,'IFNULL','Syntax\n------\n\nIFNULL(expr1,expr2)\nNVL(expr1,expr2)\n\nDescription\n-----------\n\nIf expr1 is not NULL, IFNULL() returns expr1; otherwise it returns expr2.\nIFNULL() returns a numeric or string value, depending on the context in which\nit is used.\n\nFrom MariaDB 10.3, NVL() is an alias for IFNULL().\n\nExamples\n--------\n\nSELECT IFNULL(1,0); \n+-------------+\n| IFNULL(1,0) |\n+-------------+\n| 1 |\n+-------------+\n\nSELECT IFNULL(NULL,10);\n+-----------------+\n| IFNULL(NULL,10) |\n+-----------------+\n| 10 |\n+-----------------+\n\nSELECT IFNULL(1/0,10);\n+----------------+\n| IFNULL(1/0,10) |\n+----------------+\n| 10.0000 |\n+----------------+\n\nSELECT IFNULL(1/0,\'yes\');\n+-------------------+\n| IFNULL(1/0,\'yes\') |\n+-------------------+\n| yes |\n+-------------------+\n\nURL: https://mariadb.com/kb/en/ifnull/','','https://mariadb.com/kb/en/ifnull/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (90,7,'NULLIF','Syntax\n------\n\nNULLIF(expr1,expr2)\n\nDescription\n-----------\n\nReturns NULL if expr1 = expr2 is true, otherwise returns expr1. This is the\nsame as CASE WHEN expr1 = expr2 THEN NULL ELSE expr1 END.\n\nExamples\n--------\n\nSELECT NULLIF(1,1);\n+-------------+\n| NULLIF(1,1) |\n+-------------+\n| NULL |\n+-------------+\n\nSELECT NULLIF(1,2);\n+-------------+\n| NULLIF(1,2) |\n+-------------+\n| 1 |\n+-------------+\n\nURL: https://mariadb.com/kb/en/nullif/','','https://mariadb.com/kb/en/nullif/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (91,7,'NVL','MariaDB starting with 10.3\n--------------------------\nFrom MariaDB 10.3, NVL is a synonym for IFNULL.\n\nURL: https://mariadb.com/kb/en/nvl/','','https://mariadb.com/kb/en/nvl/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (92,7,'NVL2','MariaDB starting with 10.3\n--------------------------\nThe NLV2 function was introduced in MariaDB 10.3.0.\n\nSyntax\n------\n\nNVL2(expr1,expr2,expr3)\n\nDescription\n-----------\n\nThe NVL2 function returns a value based on whether a specified expression is\nNULL or not. If expr1 is not NULL, then NVL2 returns expr2. If expr1 is NULL,\nthen NVL2 returns expr3.\n\nExamples\n--------\n\nSELECT NVL2(NULL,1,2);\n+----------------+\n| NVL2(NULL,1,2) |\n+----------------+\n| 2 |\n+----------------+\n\nSELECT NVL2(\'x\',1,2);\n+---------------+\n| NVL2(\'x\',1,2) |\n+---------------+\n| 1 |\n+---------------+\n\nURL: https://mariadb.com/kb/en/nvl2/','','https://mariadb.com/kb/en/nvl2/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (93,8,'SET TRANSACTION','Syntax\n------\n\nSET [GLOBAL | SESSION] TRANSACTION\n transaction_property [, transaction_property] ...\n\ntransaction_property:\n ISOLATION LEVEL level\n | READ WRITE\n | READ ONLY\n\nlevel:\n REPEATABLE READ\n | READ COMMITTED\n | READ UNCOMMITTED\n | SERIALIZABLE\n\nDescription\n-----------\n\nThis statement sets the transaction isolation level or the transaction access\nmode globally, for the current session, or for the next transaction:\n\n* With the GLOBAL keyword, the statement sets the default\n transaction level globally for all subsequent sessions. Existing sessions are\n unaffected.\n* With the SESSION keyword, the statement sets the default\n transaction level for all subsequent transactions performed within the\n current session.\n* Without any SESSION or GLOBAL keyword,\n the statement sets the isolation level for the next (not started) transaction\n performed within the current session.\n\nA change to the global default isolation level requires the SUPER privilege.\nAny session is free to change its session isolation level (even in the middle\nof a transaction), or the isolation level for its next transaction.\n\nIsolation Level\n---------------\n\nTo set the global default isolation level at server startup, use the\n--transaction-isolation=level option on the command line or in an option file.\nValues of level for this option use dashes rather than spaces, so the\nallowable values are READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, or\nSERIALIZABLE. For example, to set the default isolation level to REPEATABLE\nREAD, use these lines in the [mysqld] section of an option file:\n\n[mysqld]\ntransaction-isolation = REPEATABLE-READ\nTo determine the global and session transaction isolation levels at runtime,\ncheck the value of the tx_isolation system variable:\n\nSELECT @@GLOBAL.tx_isolation, @@tx_isolation;\n\nInnoDB supports each of the translation isolation levels described here using\ndifferent locking strategies. The default level is REPEATABLE READ. For\nadditional information about InnoDB record-level locks and how it uses them to\nexecute various types of statements, see InnoDB Lock Modes, and\nhttp://dev.mysql.com/doc/refman/en/innodb-locks-set.html.\n\nIsolation Levels\n----------------\n\nThe following sections describe how MariaDB supports the different transaction\nlevels.\n\nREAD UNCOMMITTED\n----------------\n\nSELECT statements are performed in a non-locking fashion, but a possible\nearlier version of a row might be used. Thus, using this isolation level, such\nreads are not consistent. This is also called a \"dirty read.\" Otherwise, this\nisolation level works like READ COMMITTED.\n\nREAD COMMITTED\n--------------\n\nA somewhat Oracle-like isolation level with respect to consistent\n(non-locking) reads: Each consistent read, even within the same transaction,\nsets and reads its own fresh snapshot. See\nhttp://dev.mysql.com/doc/refman/en/innodb-consistent-read.html.\n\nFor locking reads (SELECT with FOR UPDATE or LOCK IN SHARE MODE), InnoDB locks\nonly index records, not the gaps before them, and thus allows the free\ninsertion of new records next to locked records. For UPDATE and DELETE\nstatements, locking depends on whether the statement uses a unique index with\na unique search condition (such as WHERE id = 100), or a range-type search\ncondition (such as WHERE id > 100). For a unique index with a unique search\ncondition, InnoDB locks only the index record found, not the gap before it.\nFor range-type searches, InnoDB locks the index range scanned, using gap locks\nor next-key (gap plus index-record) locks to block insertions by other\nsessions into the gaps covered by the range. This is necessary because\n\"phantom rows\" must be blocked for MySQL replication and recovery to work.\n\nNote: If the READ COMMITTED isolation level is used or the\ninnodb_locks_unsafe_for_binlog system variable is enabled, there is no InnoDB\ngap locking except for foreign-key constraint checking and duplicate-key\nchecking. Also, record locks for non-matching rows are released after MariaDB\nhas evaluated the WHERE condition.If you use READ COMMITTED or enable\ninnodb_locks_unsafe_for_binlog, you must use row-based binary logging.\n\nREPEATABLE READ\n---------------\n\nThis is the default isolation level for InnoDB. For consistent reads, there is\nan important difference from the READ COMMITTED isolation level: All\nconsistent reads within the same transaction read the snapshot established by\nthe first read. This convention means that if you issue several plain\n(non-locking) SELECT statements within the same transaction, these SELECT\nstatements are consistent also with respect to each other. See\nhttp://dev.mysql.com/doc/refman/en/innodb-consistent-read.html.\n\nFor locking reads (SELECT with FOR UPDATE or LOCK IN SHARE MODE), UPDATE, and\nDELETE statements, locking depends on whether the statement uses a unique\nindex with a unique search condition, or a range-type search condition. For a\nunique index with a unique search condition, InnoDB locks only the index\nrecord found, not the gap before it. For other search conditions, InnoDB locks\nthe index range scanned, using gap locks or next-key (gap plus index-record)\nlocks to block insertions by other sessions into the gaps covered by the range.\n\nThis is the minimum isolation level for non-distributed XA transactions.\n\nSERIALIZABLE\n------------\n\nThis level is like REPEATABLE READ, but InnoDB implicitly converts all plain\nSELECT statements to SELECT ... LOCK IN SHARE MODE if autocommit is disabled.\nIf autocommit is enabled, the SELECT is its own transaction. It therefore is\nknown to be read only and can be serialized if performed as a consistent\n(non-locking) read and need not block for other transactions. (This means that\nto force a plain SELECT to block if other transactions have modified the\nselected rows, you should disable autocommit.)\n\nDistributed XA transactions should always use this isolation level.\n\nAccess Mode\n-----------\n\nThe access mode specifies whether the transaction is allowed to write data or\nnot. By default, transactions are in READ WRITE mode (see the tx_read_only\nsystem variable). READ ONLY mode allows the storage engine to apply\noptimizations that cannot be used for transactions which write data. The only\nexception to this rule is that read only transactions can perform DDL\nstatements on temporary tables.\n\nIt is not permitted to specify both READ WRITE and READ ONLY in the same\nstatement.\n\nREAD WRITE and READ ONLY can also be specified in the START TRANSACTION\nstatement, in which case the specified mode is only valid for one transaction.\n\nExamples\n--------\n\nSET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;\n\nAttempting to set the isolation level within an existing transaction without\nspecifying GLOBAL or SESSION.\n\nSTART TRANSACTION;\n\nSET TRANSACTION ISOLATION LEVEL SERIALIZABLE;\nERROR 1568 (25001): Transaction characteristics can\'t be changed while a\ntransaction is in progress\n\nURL: https://mariadb.com/kb/en/set-transaction/','','https://mariadb.com/kb/en/set-transaction/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (94,8,'START TRANSACTION','Syntax\n------\n\nSTART TRANSACTION [transaction_property [, transaction_property] ...] | BEGIN\n[WORK]\nCOMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE]\nROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE]\nSET autocommit = {0 | 1}\n\ntransaction_property:\n WITH CONSISTENT SNAPSHOT\n | READ WRITE\n | READ ONLY\n\nDescription\n-----------\n\nThe START TRANSACTION or BEGIN statement begins a new transaction. COMMIT\ncommits the current transaction, making its changes permanent. ROLLBACK rolls\nback the current transaction, canceling its changes. The SET autocommit\nstatement disables or enables the default autocommit mode for the current\nsession.\n\nSTART TRANSACTION and SET autocommit = 1 implicitly commit the current\ntransaction, if any.\n\nThe optional WORK keyword is supported for COMMIT and ROLLBACK, as are the\nCHAIN and RELEASE clauses. CHAIN and RELEASE can be used for additional\ncontrol over transaction completion. The value of the completion_type system\nvariable determines the default completion behavior.\n\nThe AND CHAIN clause causes a new transaction to begin as soon as the current\none ends, and the new transaction has the same isolation level as the\njust-terminated transaction. The RELEASE clause causes the server to\ndisconnect the current client session after terminating the current\ntransaction. Including the NO keyword suppresses CHAIN or RELEASE completion,\nwhich can be useful if the completion_type system variable is set to cause\nchaining or release completion by default.\n\nAccess Mode\n-----------\n\nThe access mode specifies whether the transaction is allowed to write data or\nnot. By default, transactions are in READ WRITE mode (see the tx_read_only\nsystem variable). READ ONLY mode allows the storage engine to apply\noptimizations that cannot be used for transactions which write data. The only\nexception to this rule is that read only transactions can perform DDL\nstatements on temporary tables.\n\nIt is not permitted to specify both READ WRITE and READ ONLY in the same\nstatement.\n\nREAD WRITE and READ ONLY can also be specified in the SET TRANSACTION\nstatement, in which case the specified mode is valid for all sessions, or for\nall subsequent transaction used by the current session.\n\nautocommit\n----------\n\nBy default, MariaDB runs with autocommit mode enabled. This means that as soon\nas you execute a statement that updates (modifies) a table, MariaDB stores the\nupdate on disk to make it permanent. To disable autocommit mode, use the\nfollowing statement:\n\nSET autocommit=0;\n\nAfter disabling autocommit mode by setting the autocommit variable to zero,\nchanges to transaction-safe tables (such as those for InnoDB or NDBCLUSTER)\nare not made permanent immediately. You must use COMMIT to store your changes\nto disk or ROLLBACK to ignore the changes.\n\nTo disable autocommit mode for a single series of statements, use the START\nTRANSACTION statement.\n\nDDL Statements\n--------------\n\nDDL statements (CREATE, ALTER, DROP) and administrative statements (FLUSH,\nRESET, OPTIMIZE, ANALYZE, CHECK, REPAIR, CACHE INDEX), transaction management\nstatements (BEGIN, START TRANSACTION) and LOAD DATA INFILE, cause an implicit\nCOMMIT and start a new transaction. An exception to this rule are the DDL that\noperate on temporary tables: you can CREATE, ALTER and DROP them without\ncausing any COMMIT, but those actions cannot be rolled back. This means that\nif you call ROLLBACK, the temporary tables you created in the transaction will\nremain, while the rest of the transaction will be rolled back.\n\nTransactions cannot be used in Stored Functions or Triggers. In Stored\nProcedures and Events BEGIN is not allowed, so you should use START\nTRANSACTION instead.\n\nA transaction acquires a metadata lock on every table it accesses to prevent\nother connections from altering their structure. The lock is released at the\nend of the transaction. This happens even with non-transactional storage\nengines (like MEMORY or CONNECT), so it makes sense to use transactions with\nnon-transactional tables.\n\nin_transaction\n--------------\n\nThe in_transaction system variable is a session-only, read-only variable that\nreturns 1 inside a transaction, and 0 if not in a transaction.\n\nWITH CONSISTENT SNAPSHOT\n------------------------\n\nThe WITH CONSISTENT SNAPSHOT option starts a consistent read for storage\nengines such as InnoDB that can do so, the same as if a START TRANSACTION\nfollowed by a SELECT from any InnoDB table was issued.\n\nSee Enhancements for START TRANSACTION WITH CONSISTENT SNAPSHOT.\n\nExamples\n--------\n\nSTART TRANSACTION;\nSELECT @A:=SUM(salary) FROM table1 WHERE type=1;\nUPDATE table2 SET summary=@A WHERE type=1;\nCOMMIT;\n\nURL: https://mariadb.com/kb/en/start-transaction/','','https://mariadb.com/kb/en/start-transaction/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (95,8,'COMMIT','The COMMIT statement ends a transaction, saving any changes to the data so\nthat they become visible to subsequent transactions. Also, unlocks metadata\nchanged by current transaction. If autocommit is set to 1, an implicit commit\nis performed after each statement. Otherwise, all transactions which don\'t end\nwith an explicit COMMIT are implicitly rollbacked and the changes are lost.\nThe ROLLBACK statement can be used to do this explicitly.\n\nThe required syntax for the COMMIT statement is as follows:\n\nCOMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE]\n\nCOMMIT is the more important transaction terminator, as well as the more\ninteresting one. The basic form of the COMMIT statement is simply the keyword\nCOMMIT (the keyword WORK is simply noise and can be omitted without changing\nthe effect).\n\nThe optional AND CHAIN clause is a convenience for initiating a new\ntransaction as soon as the old transaction terminates. If AND CHAIN is\nspecified, then there is effectively nothing between the old and new\ntransactions, although they remain separate. The characteristics of the new\ntransaction will be the same as the characteristics of the old one — that is,\nthe new transaction will have the same access mode, isolation level and\ndiagnostics area size (we\'ll discuss all of these shortly) as the transaction\njust terminated.\n\nRELEASE tells the server to disconnect the client immediately after the\ncurrent transaction.\n\nThere are NO RELEASE and AND NO CHAIN options. By default, commits do not\nRELEASE or CHAIN, but it\'s possible to change this default behavior with the\ncompletion_type server system variable. In this case, the AND NO CHAIN and NO\nRELEASE options override the server default.\n\nURL: https://mariadb.com/kb/en/commit/','','https://mariadb.com/kb/en/commit/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (96,8,'ROLLBACK','The ROLLBACK statement rolls back (ends) a transaction, destroying any changes\nto SQL-data so that they never become visible to subsequent transactions. The\nrequired syntax for the ROLLBACK statement is as follows.\n\nROLLBACK [ WORK ] [ AND [ NO ] CHAIN ] \n[ TO [ SAVEPOINT ] { | } ]\n\nThe ROLLBACK statement will either end a transaction, destroying all data\nchanges that happened during any of the transaction, or it will just destroy\nany data changes that happened since you established a savepoint. The basic\nform of the ROLLBACK statement is just the keyword ROLLBACK (the keyword WORK\nis simply noise and can be omitted without changing the effect).\n\nThe optional AND CHAIN clause is a convenience for initiating a new\ntransaction as soon as the old transaction terminates. If AND CHAIN is\nspecified, then there is effectively nothing between the old and new\ntransactions, although they remain separate. The characteristics of the new\ntransaction will be the same as the characteristics of the old one — that is,\nthe new transaction will have the same access mode, isolation level and\ndiagnostics area size (we\'ll discuss all of these shortly) as the transaction\njust terminated. The AND NO CHAIN option just tells your DBMS to end the\ntransaction — that is, these four SQL statements are equivalent:\n\nROLLBACK; \nROLLBACK WORK; \nROLLBACK AND NO CHAIN; \nROLLBACK WORK AND NO CHAIN;\n\nAll of them end a transaction without saving any transaction characteristics.\nThe only other options, the equivalent statements:\n\nROLLBACK AND CHAIN;\nROLLBACK WORK AND CHAIN;\n\nboth tell your DBMS to end a transaction, but to save that transaction\'s\ncharacteristics for the next transaction.\n\nROLLBACK is much simpler than COMMIT: it may involve no more than a few\ndeletions (of Cursors, locks, prepared SQL statements and log-file entries).\nIt\'s usually assumed that ROLLBACK can\'t fail, although such a thing is\nconceivable (for example, an encompassing transaction might reject an attempt\nto ROLLBACK because it\'s lining up for a COMMIT).\n\nROLLBACK cancels all effects of a transaction. It does not cancel effects on\nobjects outside the DBMS\'s control (for example the values in host program\nvariables or the settings made by some SQL/CLI function calls). But in\ngeneral, it is a convenient statement for those situations when you say \"oops,\nthis isn\'t working\" or when you simply don\'t care whether your temporary work\nbecomes permanent or not.\n\nHere is a moot question. If all you\'ve been doing is SELECTs, so that there\nhave been no data changes, should you end the transaction with ROLLBACK or\nCOMMIT? It shouldn\'t really matter because both ROLLBACK and COMMIT do the\nsame transaction-terminating job. However, the popular conception is that\nROLLBACK implies failure, so after a successful series of SELECT statements\nthe convention is to end the transaction with COMMIT rather than ROLLBACK.\n\nMariaDB (and most other DBMSs) supports rollback of SQL-data change\nstatements, but not of SQL-Schema statements. This means that if you use any\nof CREATE, ALTER, DROP, GRANT, REVOKE, you are implicitly committing at\nexecution time.\n\nINSERT INTO Table_2 VALUES(5); \nDROP TABLE Table_3 CASCADE; \nROLLBACK;\n\nThe result will be that both the INSERT and the DROP will go through as\nseparate transactions so the ROLLBACK will have no effect.\n\nURL: https://mariadb.com/kb/en/rollback/','','https://mariadb.com/kb/en/rollback/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (97,8,'LOCK TABLES','Syntax\n------\n\nLOCK TABLE[S]\n tbl_name [[AS] alias] lock_type\n [, tbl_name [[AS] alias] lock_type] ...\n [WAIT n|NOWAIT]\n\nlock_type:\n READ [LOCAL]\n | [LOW_PRIORITY] WRITE\n | WRITE CONCURRENT\n\nUNLOCK TABLES\n\nDescription\n-----------\n\nThe lock_type can be one of:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| READ | Read lock, no writes allowed |\n+---------------------------+------------------------------------------------+\n| READ LOCAL | Read lock, but allow concurrent inserts |\n+---------------------------+------------------------------------------------+\n| WRITE | Exclusive write lock. No other connections |\n| | can read or write to this table |\n+---------------------------+------------------------------------------------+\n| LOW_PRIORITY WRITE | Exclusive write lock, but allow new read |\n| | locks on the table until we get the write |\n| | lock. |\n+---------------------------+------------------------------------------------+\n| WRITE CONCURRENT | Exclusive write lock, but allow READ LOCAL |\n| | locks to the table. |\n+---------------------------+------------------------------------------------+\n\nMariaDB enables client sessions to acquire table locks explicitly for the\npurpose of cooperating with other sessions for access to tables, or to prevent\nother sessions from modifying tables during periods when a session requires\nexclusive access to them. A session can acquire or release locks only for\nitself. One session cannot acquire locks for another session or release locks\nheld by another session.\n\nLocks may be used to emulate transactions or to get more speed when updating\ntables.\n\nLOCK TABLES explicitly acquires table locks for the current client session.\nTable locks can be acquired for base tables or views. To use LOCK TABLES, you\nmust have the LOCK TABLES privilege, and the SELECT privilege for each object\nto be locked. See GRANT\n\nFor view locking, LOCK TABLES adds all base tables used in the view to the set\nof tables to be locked and locks them automatically. If you lock a table\nexplicitly with LOCK TABLES, any tables used in triggers are also locked\nimplicitly, as described in Triggers and Implicit Locks.\n\nUNLOCK TABLES explicitly releases any table locks held by the current session.\n\nMariaDB starting with 10.3.0\n----------------------------\n\nWAIT/NOWAIT\n-----------\n\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nLimitations\n-----------\n\n* LOCK TABLES doesn\'t work when using Galera cluster. You may experience\ncrashes or locks when used with Galera.\n* LOCK TABLES works on XtraDB/InnoDB tables only if the innodb_table_locks\nsystem variable is set to 1 (the default) and autocommit is set to 0 (1 is\ndefault). Please note that no error message will be returned on LOCK TABLES\nwith innodb_table_locks = 0.\n* LOCK TABLES implicitly commits the active transaction, if any. Also,\nstarting a transaction always releases all table locks acquired with LOCK\nTABLES. This means that there is no way to have table locks and an active\ntransaction at the same time. The only exceptions are the transactions in\nautocommit mode. To preserve the data integrity between transactional and\nnon-transactional tables, the GET_LOCK() function can be used.\n* When using LOCK TABLES on a TEMPORARY table, it will always be locked with a\nWRITE lock.\n* While a connection holds an explicit read lock on a table, it cannot modify\nit. If you try, the following error will be produced:\n\nERROR 1099 (HY000): Table \'tab_name\' was locked with a READ lock and can\'t be\nupdated\n\n* While a connection holds an explicit lock on a table, it cannot access a\nnon-locked table. If you try, the following error will be produced:\n\nERROR 1100 (HY000): Table \'tab_name\' was not locked with LOCK TABLES\n\n* While a connection holds an explicit lock on a table, it cannot issue the\nfollowing: INSERT DELAYED, CREATE TABLE, CREATE TABLE ... LIKE, and DDL\nstatements involving stored programs and views (except for triggers). If you\ntry, the following error will be produced:\n\nERROR 1192 (HY000): Can\'t execute the given command because you have active\nlocked tables or an active transaction\n\n* LOCK TABLES can not be used in stored routines - if you try, the following\nerror will be produced on creation. This restriction was removed in MariaDB\n10.6.2:\n\nERROR 1314 (0A000): LOCK is not allowed in stored procedures\n\nURL: https://mariadb.com/kb/en/lock-tables/','','https://mariadb.com/kb/en/lock-tables/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (98,8,'SAVEPOINT','Syntax\n------\n\nSAVEPOINT identifier\nROLLBACK [WORK] TO [SAVEPOINT] identifier\nRELEASE SAVEPOINT identifier\n\nDescription\n-----------\n\nInnoDB supports the SQL statements SAVEPOINT, ROLLBACK TO SAVEPOINT, RELEASE\nSAVEPOINT and the optional WORK keyword for ROLLBACK.\n\nEach savepoint must have a legal MariaDB identifier. A savepoint is a named\nsub-transaction.\n\nNormally ROLLBACK undoes the changes performed by the whole transaction. When\nused with the TO clause, it undoes the changes performed after the specified\nsavepoint, and erases all subsequent savepoints. However, all locks that have\nbeen acquired after the save point will survive. RELEASE SAVEPOINT does not\nrollback or commit any changes, but removes the specified savepoint.\n\nWhen the execution of a trigger or a stored function begins, it is not\npossible to use statements which reference a savepoint which was defined from\nout of that stored program.\n\nWhen a COMMIT (including implicit commits) or a ROLLBACK statement (with no TO\nclause) is performed, they act on the whole transaction, and all savepoints\nare removed.\n\nErrors\n------\n\nIf COMMIT or ROLLBACK is issued and no transaction was started, no error is\nreported.\n\nIf SAVEPOINT is issued and no transaction was started, no error is reported\nbut no savepoint is created. When ROLLBACK TO SAVEPOINT or RELEASE SAVEPOINT\nis called for a savepoint that does not exist, an error like this is issued:\n\nERROR 1305 (42000): SAVEPOINT svp_name does not exist\n\nURL: https://mariadb.com/kb/en/savepoint/','','https://mariadb.com/kb/en/savepoint/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (99,8,'Metadata Locking','MariaDB supports metadata locking. This means that when a transaction\n(including XA transactions) uses a table, it locks its metadata until the end\nof transaction. Non-transactional tables are also locked, as well as views and\nobjects which are related to locked tables/views (stored functions, triggers,\netc). When a connection tries to use a DDL statement (like an ALTER TABLE)\nwhich modifies a table that is locked, that connection is queued, and has to\nwait until it\'s unlocked. Using savepoints and performing a partial rollback\ndoes not release metadata locks.\n\nLOCK TABLES ... WRITE are also queued. Some wrong statements which produce an\nerror may not need to wait for the lock to be freed.\n\nThe metadata lock\'s timeout is determined by the value of the\nlock_wait_timeout server system variable (in seconds). However, note that its\ndefault value is 31536000 (1 year, MariaDB <= 10.2.3), or 86400 (1 day,\nMariaDB >= 10.2.4). If this timeout is exceeded, the following error is\nreturned:\n\nERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction\n\nIf the metadata_lock_info plugin is installed, the Information Schema\nmetadata_lock_info table stores information about existing metadata locks.\n\nMariaDB starting with 10.5.2\n----------------------------\nFrom MariaDB 10.5, the Performance Schema metadata_locks table contains\nmetadata lock information.\n\nExample\n-------\n\nLet\'s use the following MEMORY (non-transactional) table:\n\nCREATE TABLE t (a INT) ENGINE = MEMORY;\n\nConnection 1 starts a transaction, and INSERTs a row into t:\n\nSTART TRANSACTION;\n\nINSERT INTO t SET a=1;\n\nt\'s metadata is now locked by connection 1. Connection 2 tries to alter t, but\nhas to wait:\n\nALTER TABLE t ADD COLUMN b INT;\n\nConnection 2\'s prompt is blocked now.\n\nNow connection 1 ends the transaction:\n\nCOMMIT;\n\n...and connection 2 finally gets the output of its command:\n\nQuery OK, 1 row affected (35.23 sec)\nRecords: 1 Duplicates: 0 Warnings: 0\n\nURL: https://mariadb.com/kb/en/metadata-locking/','','https://mariadb.com/kb/en/metadata-locking/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (100,8,'Transaction Timeouts','MariaDB has always had the wait_timeout and interactive_timeout settings,\nwhich close connections after a certain period of inactivity.\n\nHowever, these are by default set to a long wait period. In situations where\ntransactions may be started, but not committed or rolled back, more granular\ncontrol and a shorter timeout may be desirable so as to avoid locks being held\nfor too long.\n\nMariaDB 10.3 introduced three new variables to handle this situation.\n\n* idle_transaction_timeout (all transactions)\n* idle_write_transaction_timeout (write transactions - called\nidle_readwrite_transaction_timeout until MariaDB 10.3.2)\n* idle_readonly_transaction_timeout (read transactions)\n\nThese accept a time in seconds to time out, by closing the connection,\ntransactions that are idle for longer than this period. By default all are set\nto zero, or no timeout.\n\nidle_transaction_timeout affects all transactions,\nidle_write_transaction_timeout affects write transactions only and\nidle_readonly_transaction_timeout affects read transactions only. The latter\ntwo variables work independently. However, if either is set along with\nidle_transaction_timeout, the settings for idle_write_transaction_timeout or\nidle_readonly_transaction_timeout will take precedence.\n\nExamples\n--------\n\nSET SESSION idle_transaction_timeout=2;\nBEGIN;\nSELECT * FROM t;\nEmpty set (0.000 sec)\n## wait 3 seconds\nSELECT * FROM t;\nERROR 2006 (HY000): MySQL server has gone away\n\nSET SESSION idle_write_transaction_timeout=2;\nBEGIN;\nSELECT * FROM t;\nEmpty set (0.000 sec)\n## wait 3 seconds\nSELECT * FROM t;\nEmpty set (0.000 sec)\nINSERT INTO t VALUES(1);\n## wait 3 seconds\nSELECT * FROM t;\nERROR 2006 (HY000): MySQL server has gone away\n\nSET SESSION idle_transaction_timeout=2, SESSION\nidle_readonly_transaction_timeout=10;\nBEGIN;\nSELECT * FROM t;\nEmpty set (0.000 sec)\n ## wait 3 seconds\nSELECT * FROM t;\nEmpty set (0.000 sec)\n## wait 11 seconds\nSELECT * FROM t;\nERROR 2006 (HY000): MySQL server has gone away\n\nURL: https://mariadb.com/kb/en/transaction-timeouts/','','https://mariadb.com/kb/en/transaction-timeouts/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (101,8,'UNLOCK TABLES','Syntax\n------\n\nUNLOCK TABLES\n\nDescription\n-----------\n\nUNLOCK TABLES explicitly releases any table locks held by the current session.\nSee LOCK TABLES for more information.\n\nIn addition to releasing table locks acquired by the LOCK TABLES statement,\nthe UNLOCK TABLES statement also releases the global read lock acquired by the\nFLUSH TABLES WITH READ LOCK statement. The FLUSH TABLES WITH READ LOCK\nstatement is very useful for performing backups. See FLUSH for more\ninformation about FLUSH TABLES WITH READ LOCK.\n\nURL: https://mariadb.com/kb/en/transactions-unlock-tables/','','https://mariadb.com/kb/en/transactions-unlock-tables/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (102,8,'WAIT and NOWAIT','MariaDB starting with 10.3.0\n----------------------------\nMariaDB 10.3.0 introduced extended syntax so that it is possible to set\ninnodb_lock_wait_timeout and lock_wait_timeout for the following statements:\n\nSyntax\n------\n\nALTER TABLE tbl_name [WAIT n|NOWAIT] ...\nCREATE ... INDEX ON tbl_name (index_col_name, ...) [WAIT n|NOWAIT] ...\nDROP INDEX ... [WAIT n|NOWAIT]\nDROP TABLE tbl_name [WAIT n|NOWAIT] ...\nLOCK TABLE ... [WAIT n|NOWAIT]\nOPTIMIZE TABLE tbl_name [WAIT n|NOWAIT]\nRENAME TABLE tbl_name [WAIT n|NOWAIT] ...\nSELECT ... FOR UPDATE [WAIT n|NOWAIT]\nSELECT ... LOCK IN SHARE MODE [WAIT n|NOWAIT]\nTRUNCATE TABLE tbl_name [WAIT n|NOWAIT]\n\nDescription\n-----------\n\nThe lock wait timeout can be explicitly set in the statement by using either\nWAIT n (to set the wait in seconds) or NOWAIT, in which case the statement\nwill immediately fail if the lock cannot be obtained. WAIT 0 is equivalent to\nNOWAIT.\n\nURL: https://mariadb.com/kb/en/wait-and-nowait/','','https://mariadb.com/kb/en/wait-and-nowait/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (103,8,'XA Transactions','Overview\n--------\n\nThe MariaDB XA implementation is based on the X/Open CAE document Distributed\nTransaction Processing: The XA Specification. This document is published by\nThe Open Group and available at\nhttp://www.opengroup.org/public/pubs/catalog/c193.htm.\n\nXA transactions are designed to allow distributed transactions, where a\ntransaction manager (the application) controls a transaction which involves\nmultiple resources. Such resources are usually DBMSs, but could be resources\nof any type. The whole set of required transactional operations is called a\nglobal transaction. Each subset of operations which involve a single resource\nis called a local transaction. XA used a 2-phases commit (2PC). With the first\ncommit, the transaction manager tells each resource to prepare an effective\ncommit, and waits for a confirm message. The changes are not still made\neffective at this point. If any of the resources encountered an error, the\ntransaction manager will rollback the global transaction. If all resources\ncommunicate that the first commit is successful, the transaction manager can\nrequire a second commit, which makes the changes effective.\n\nIn MariaDB, XA transactions can only be used with storage engines that support\nthem. At least InnoDB, TokuDB, SPIDER and MyRocks support them. For InnoDB,\nuntil MariaDB 10.2, XA transactions can be disabled by setting the\ninnodb_support_xa server system variable to 0. From MariaDB 10.3, XA\ntransactions are always supported.\n\nLike regular transactions, XA transactions create metadata locks on accessed\ntables.\n\nXA transactions require REPEATABLE READ as a minimum isolation level. However,\ndistributed transactions should always use SERIALIZABLE.\n\nTrying to start more than one XA transaction at the same time produces a 1400\nerror (SQLSTATE \'XAE09\'). The same error is produced when attempting to start\nan XA transaction while a regular transaction is in effect. Trying to start a\nregular transaction while an XA transaction is in effect produces a 1399 error\n(SQLSTATE \'XAE07\').\n\nThe statements that cause an implicit COMMIT for regular transactions produce\na 1400 error (SQLSTATE \'XAE09\') if a XA transaction is in effect.\n\nInternal XA vs External XA\n--------------------------\n\nXA transactions are an overloaded term in MariaDB. If a storage engine is\nXA-capable, it can mean one or both of these:\n\n* It supports MariaDB\'s internal two-phase commit API. This is transparent to\nthe user. Sometimes this is called \"internal XA\", since MariaDB\'s internal\ntransaction coordinator log can handle coordinating these transactions.\n\n* It supports XA transactions, with the XA START, XA PREPARE, XA COMMIT, etc.\nstatements. Sometimes this is called \"external XA\", since it requires the use\nof an external transaction coordinator to use this feature properly.\n\nTransaction Coordinator Log\n---------------------------\n\nIf you have two or more XA-capable storage engines enabled, then a transaction\ncoordinator log must be available.\n\nThere are currently two implementations of the transaction coordinator log:\n\n* Binary log-based transaction coordinator log\n* Memory-mapped file-based transaction coordinator log\n\nIf the binary log is enabled on a server, then the server will use the binary\nlog-based transaction coordinator log. Otherwise, it will use the\nmemory-mapped file-based transaction coordinator log.\n\nSee Transaction Coordinator Log for more information.\n\nSyntax\n------\n\nXA {START|BEGIN} xid [JOIN|RESUME]\n\nXA END xid [SUSPEND [FOR MIGRATE]]\n\nXA PREPARE xid\n\nXA COMMIT xid [ONE PHASE]\n\nXA ROLLBACK xid\n\nXA RECOVER [FORMAT=[\'RAW\'|\'SQL\']]\n\nxid: gtrid [, bqual [, formatID ]]\n\nThe interface to XA transactions is a set of SQL statements starting with XA.\nEach statement changes a transaction\'s state, determining which actions it can\nperform. A transaction which does not exist is in the NON-EXISTING state.\n\nXA START (or BEGIN) starts a transaction and defines its xid (a transaction\nidentifier). The JOIN or RESUME keywords have no effect. The new transaction\nwill be in ACTIVE state.\n\nThe xid can have 3 components, though only the first one is mandatory. gtrid\nis a quoted string representing a global transaction identifier. bqual is a\nquoted string representing a local transaction identifier. formatID is an\nunsigned integer indicating the format used for the first two components; if\nnot specified, defaults to 1. MariaDB does not interpret in any way these\ncomponents, and only uses them to identify a transaction. xids of transactions\nin effect must be unique.\n\nXA END declares that the specified ACTIVE transaction is finished and it\nchanges its state to IDLE. SUSPEND [FOR MIGRATE] has no effect.\n\nXA PREPARE prepares an IDLE transaction for commit, changing its state to\nPREPARED. This is the first commit.\n\nXA COMMIT definitely commits and terminates a transaction which has already\nbeen PREPARED. If the ONE PHASE clause is specified, this statements performs\na 1-phase commit on an IDLE transaction.\n\nXA ROLLBACK rolls back and terminates an IDLE or PREPARED transaction.\n\nXA RECOVER shows information about all PREPARED transactions.\n\nWhen trying to execute an operation which is not allowed for the transaction\'s\ncurrent state, an error is produced:\n\nXA COMMIT \'test\' ONE PHASE;\nERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global\ntransaction is in the ACTIVE state\n\nXA COMMIT \'test2\';\nERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global\ntransaction is in the NON-EXISTING state\n\nXA RECOVER\n----------\n\nThe XA RECOVER statement shows information about all transactions which are in\nthe PREPARED state. It does not matter which connection created the\ntransaction: if it has been PREPARED, it appears. But this does not mean that\na connection can commit or rollback a transaction which was started by another\nconnection. Note that transactions using a 1-phase commit are never in the\nPREPARED state, so they cannot be shown by XA RECOVER.\n\nXA RECOVER produces four columns:\n\nXA RECOVER;\n+----------+--------------+--------------+------+\n| formatID | gtrid_length | bqual_length | data |\n+----------+--------------+--------------+------+\n| 1 | 4 | 0 | test |\n+----------+--------------+--------------+------+\n\nMariaDB starting with 10.3.3\n----------------------------\nYou can use XA RECOVER FORMAT=\'SQL\' to get the data in a human readable form\nthat can be directly copy-pasted into XA COMMIT or XA ROLLBACK. This is\nparticularly useful for binary xid generated by some transaction coordinators.\n\nformatID is the formatID part of xid.\n\ndata are the gtrid and bqual parts of xid, concatenated.\n\ngtrid_length and bqual_length are the lengths of gtrid and bqual, respectevely.\n\nExamples\n--------\n\n2-phases commit:\n\nXA START \'test\';\n\nINSERT INTO t VALUES (1,2);\n\nXA END \'test\';\n\nXA PREPARE \'test\';\n\nXA COMMIT \'test\';\n\n1-phase commit:\n\nXA START \'test\';\n\nINSERT INTO t VALUES (1,2);\n\nXA END \'test\';\n\nXA COMMIT \'test\' ONE PHASE;\n\nHuman-readable:\n\nxa start \'12\\r34\\t67\\v78\', \'abc\\ndef\', 3;\n\ninsert t1 values (40);\n\nxa end \'12\\r34\\t67\\v78\', \'abc\\ndef\', 3;\n\nxa prepare \'12\\r34\\t67\\v78\', \'abc\\ndef\', 3;\n\nxa recover format=\'RAW\';\n+----------+--------------+--------------+--------------------+\n| formatID | gtrid_length | bqual_length | data |\n+----------+--------------+--------------+--------------------+\n34 67v78abc 11 | 7 | 12\ndef |\n+----------+--------------+--------------+--------------------+\n\nxa recover format=\'SQL\';\n+----------+--------------+--------------+-------------------------------------\n---------+\n| formatID | gtrid_length | bqual_length | data \n |\n+----------+--------------+--------------+-------------------------------------\n---------+\n| 3 | 11 | 7 |\nX\'31320d3334093637763738\',X\'6162630a646566\',3 |\n+----------+--------------+--------------+-------------------------------------\n---------+\n\nxa rollback X\'31320d3334093637763738\',X\'6162630a646566\',3;\n\nKnown Issues\n------------\n\nMariaDB Galera Cluster\n----------------------\n\nMariaDB Galera Cluster does not support XA transactions.\n\nHowever, MariaDB Galera Cluster builds include a built-in plugin called wsrep.\nPrior to MariaDB 10.4.3, this plugin was internally considered an XA-capable\nstorage engine. Consequently, these MariaDB Galera Cluster builds have\nmultiple XA-capable storage engines by default, even if the only \"real\"\nstorage engine that supports external XA transactions enabled on these builds\nby default is InnoDB. Therefore, when using one these builds MariaDB would be\nforced to use a transaction coordinator log by default, which could have\nperformance implications.\n\nSee Transaction Coordinator Log Overview: MariaDB Galera Cluster for more\ninformation.\n\nURL: https://mariadb.com/kb/en/xa-transactions/','','https://mariadb.com/kb/en/xa-transactions/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (104,10,'CREATE USER','Syntax\n------\n\nCREATE [OR REPLACE] USER [IF NOT EXISTS] \n user_specification [,user_specification ...] \n [REQUIRE {NONE | tls_option [[AND] tls_option ...] }]\n [WITH resource_option [resource_option ...] ]\n [lock_option] [password_option]\n\nuser_specification:\n username [authentication_option]\n\nauthentication_option:\n IDENTIFIED BY \'password\'\n | IDENTIFIED BY PASSWORD \'password_hash\'\n | IDENTIFIED {VIA|WITH} authentication_rule [OR authentication_rule ...]\n\nauthentication_rule:\n authentication_plugin\n | authentication_plugin {USING|AS} \'authentication_string\'\n | authentication_plugin {USING|AS} PASSWORD(\'password\')\n\ntls_option:\n SSL\n | X509\n | CIPHER \'cipher\'\n | ISSUER \'issuer\'\n | SUBJECT \'subject\'\n\nresource_option:\n MAX_QUERIES_PER_HOUR count\n | MAX_UPDATES_PER_HOUR count\n | MAX_CONNECTIONS_PER_HOUR count\n | MAX_USER_CONNECTIONS count\n | MAX_STATEMENT_TIME time\n\npassword_option:\n PASSWORD EXPIRE\n | PASSWORD EXPIRE DEFAULT\n | PASSWORD EXPIRE NEVER\n | PASSWORD EXPIRE INTERVAL N DAY\n\nlock_option:\n ACCOUNT LOCK\n | ACCOUNT UNLOCK\n}\n\nDescription\n-----------\n\nThe CREATE USER statement creates new MariaDB accounts. To use it, you must\nhave the global CREATE USER privilege or the INSERT privilege for the mysql\ndatabase. For each account, CREATE USER creates a new row in mysql.user (until\nMariaDB 10.3 this is a table, from MariaDB 10.4 it\'s a view) or\nmysql.global_priv_table (from MariaDB 10.4) that has no privileges.\n\nIf any of the specified accounts, or any permissions for the specified\naccounts, already exist, then the server returns ERROR 1396 (HY000). If an\nerror occurs, CREATE USER will still create the accounts that do not result in\nan error. Only one error is produced for all users which have not been created:\n\nERROR 1396 (HY000): \n Operation CREATE USER failed for \'u1\'@\'%\',\'u2\'@\'%\'\n\nCREATE USER, DROP USER, CREATE ROLE, and DROP ROLE all produce the same error\ncode when they fail.\n\nSee Account Names below for details on how account names are specified.\n\nOR REPLACE\n----------\n\nIf the optional OR REPLACE clause is used, it is basically a shortcut for:\n\nDROP USER IF EXISTS name;\nCREATE USER name ...;\n\nFor example:\n\nCREATE USER foo2@test IDENTIFIED BY \'password\';\nERROR 1396 (HY000): Operation CREATE USER failed for \'foo2\'@\'test\'\n\nCREATE OR REPLACE USER foo2@test IDENTIFIED BY \'password\';\nQuery OK, 0 rows affected (0.00 sec)\n\nIF NOT EXISTS\n-------------\n\nWhen the IF NOT EXISTS clause is used, MariaDB will return a warning instead\nof an error if the specified user already exists.\n\nFor example:\n\nCREATE USER foo2@test IDENTIFIED BY \'password\';\nERROR 1396 (HY000): Operation CREATE USER failed for \'foo2\'@\'test\'\n\nCREATE USER IF NOT EXISTS foo2@test IDENTIFIED BY \'password\';\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+----------------------------------------------------+\n| Level | Code | Message |\n+-------+------+----------------------------------------------------+\n| Note | 1973 | Can\'t create user \'foo2\'@\'test\'; it already exists |\n+-------+------+----------------------------------------------------+\n\nAuthentication Options\n----------------------\n\nIDENTIFIED BY \'password\'\n------------------------\n\nThe optional IDENTIFIED BY clause can be used to provide an account with a\npassword. The password should be specified in plain text. It will be hashed by\nthe PASSWORD function prior to being stored in the\nmysql.user/mysql.global_priv_table table.\n\nFor example, if our password is mariadb, then we can create the user with:\n\nCREATE USER foo2@test IDENTIFIED BY \'mariadb\';\n\nIf you do not specify a password with the IDENTIFIED BY clause, the user will\nbe able to connect without a password. A blank password is not a wildcard to\nmatch any password. The user must connect without providing a password if no\npassword is set.\n\nThe only authentication plugins that this clause supports are\nmysql_native_password and mysql_old_password.\n\nIDENTIFIED BY PASSWORD \'password_hash\'\n--------------------------------------\n\nThe optional IDENTIFIED BY PASSWORD clause can be used to provide an account\nwith a password that has already been hashed. The password should be specified\nas a hash that was provided by the PASSWORD function. It will be stored in the\nmysql.user/mysql.global_priv_table table as-is.\n\nFor example, if our password is mariadb, then we can find the hash with:\n\nSELECT PASSWORD(\'mariadb\');\n+-------------------------------------------+\n| PASSWORD(\'mariadb\') |\n+-------------------------------------------+\n| *54958E764CE10E50764C2EECBB71D01F08549980 |\n+-------------------------------------------+\n1 row in set (0.00 sec)\n\nAnd then we can create a user with the hash:\n\nCREATE USER foo2@test IDENTIFIED BY PASSWORD\n\'*54958E764CE10E50764C2EECBB71D01F08549980\';\n\nIf you do not specify a password with the IDENTIFIED BY clause, the user will\nbe able to connect without a password. A blank password is not a wildcard to\nmatch any password. The user must connect without providing a password if no\npassword is set.\n\nThe only authentication plugins that this clause supports are\nmysql_native_password and mysql_old_password.\n\nIDENTIFIED {VIA|WITH} authentication_plugin\n-------------------------------------------\n\nThe optional IDENTIFIED VIA authentication_plugin allows you to specify that\nthe account should be authenticated by a specific authentication plugin. The\nplugin name must be an active authentication plugin as per SHOW PLUGINS. If it\ndoesn\'t show up in that output, then you will need to install it with INSTALL\nPLUGIN or INSTALL SONAME.\n\nFor example, this could be used with the PAM authentication plugin:\n\nCREATE USER foo2@test IDENTIFIED VIA pam;\n\nSome authentication plugins allow additional arguments to be specified after a\nUSING or AS keyword. For example, the PAM authentication plugin accepts a\nservice name:\n\nCREATE USER foo2@test IDENTIFIED VIA pam USING \'mariadb\';\n\nThe exact meaning of the additional argument would depend on the specific\nauthentication plugin.\n\nMariaDB starting with 10.4.0\n----------------------------\nThe USING or AS keyword can also be used to provide a plain-text password to a\nplugin if it\'s provided as an argument to the PASSWORD() function. This is\nonly valid for authentication plugins that have implemented a hook for the\nPASSWORD() function. For example, the ed25519 authentication plugin supports\nthis:\n\nCREATE USER safe@\'%\' IDENTIFIED VIA ed25519 USING PASSWORD(\'secret\');\n\nMariaDB starting with 10.4.3\n----------------------------\nOne can specify many authentication plugins, they all work as alternatives\nways of authenticating a user:\n\nCREATE USER safe@\'%\' IDENTIFIED VIA ed25519 USING PASSWORD(\'secret\') OR\nunix_socket;\n\nBy default, when you create a user without specifying an authentication\nplugin, MariaDB uses the mysql_native_password plugin.\n\nTLS Options\n-----------\n\nBy default, MariaDB transmits data between the server and clients without\nencrypting it. This is generally acceptable when the server and client run on\nthe same host or in networks where security is guaranteed through other means.\nHowever, in cases where the server and client exist on separate networks or\nthey are in a high-risk network, the lack of encryption does introduce\nsecurity concerns as a malicious actor could potentially eavesdrop on the\ntraffic as it is sent over the network between them.\n\nTo mitigate this concern, MariaDB allows you to encrypt data in transit\nbetween the server and clients using the Transport Layer Security (TLS)\nprotocol. TLS was formerly known as Secure Socket Layer (SSL), but strictly\nspeaking the SSL protocol is a predecessor to TLS and, that version of the\nprotocol is now considered insecure. The documentation still uses the term SSL\noften and for compatibility reasons TLS-related server system and status\nvariables still use the prefix ssl_, but internally, MariaDB only supports its\nsecure successors.\n\nSee Secure Connections Overview for more information about how to determine\nwhether your MariaDB server has TLS support.\n\nYou can set certain TLS-related restrictions for specific user accounts. For\ninstance, you might use this with user accounts that require access to\nsensitive data while sending it across networks that you do not control. These\nrestrictions can be enabled for a user account with the CREATE USER, ALTER\nUSER, or GRANT statements. The following options are available:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| REQUIRE NONE | TLS is not required for this account, but can |\n| | still be used. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SSL | The account must use TLS, but no valid X509 |\n| | certificate is required. This option cannot |\n| | be combined with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE X509 | The account must use TLS and must have a |\n| | valid X509 certificate. This option implies |\n| | REQUIRE SSL. This option cannot be combined |\n| | with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE ISSUER \'issuer\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the Certificate |\n| | Authority must be the one specified via the |\n| | string issuer. This option implies REQUIRE |\n| | X509. This option can be combined with the |\n| | SUBJECT, and CIPHER options in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SUBJECT \'subject\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the |\n| | certificate\'s Subject must be the one |\n| | specified via the string subject. This option |\n| | implies REQUIRE X509. This option can be |\n| | combined with the ISSUER, and CIPHER options |\n| | in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE CIPHER \'cipher\' | The account must use TLS, but no valid X509 |\n| | certificate is required. Also, the encryption |\n| | used for the connection must use a specific |\n| | cipher method specified in the string cipher. |\n| | This option implies REQUIRE SSL. This option |\n| | can be combined with the ISSUER, and SUBJECT |\n| | options in any order. |\n+---------------------------+------------------------------------------------+\n\nThe REQUIRE keyword must be used only once for all specified options, and the\nAND keyword can be used to separate individual options, but it is not required.\n\nFor example, you can create a user account that requires these TLS options\nwith the following:\n\nCREATE USER \'alice\'@\'%\'\n REQUIRE SUBJECT \'/CN=alice/O=My Dom, Inc./C=US/ST=Oregon/L=Portland\'\n AND ISSUER \'/C=FI/ST=Somewhere/L=City/ O=Some Company/CN=Peter\nParker/emailAddress=p.parker@marvel.com\'\n AND CIPHER \'SHA-DES-CBC3-EDH-RSA\';\n\nIf any of these options are set for a specific user account, then any client\nwho tries to connect with that user account will have to be configured to\nconnect with TLS.\n\nSee Securing Connections for Client and Server for information on how to\nenable TLS on the client and server.\n\nResource Limit Options\n----------------------\n\nIt is possible to set per-account limits for certain server resources. The\nfollowing table shows the values that can be set per account:\n\n+--------------------------------------+--------------------------------------+\n| Limit Type | Decription |\n+--------------------------------------+--------------------------------------+\n| MAX_QUERIES_PER_HOUR | Number of statements that the |\n| | account can issue per hour |\n| | (including updates) |\n+--------------------------------------+--------------------------------------+\n| MAX_UPDATES_PER_HOUR | Number of updates (not queries) |\n| | that the account can issue per hour |\n+--------------------------------------+--------------------------------------+\n| MAX_CONNECTIONS_PER_HOUR | Number of connections that the |\n| | account can start per hour |\n+--------------------------------------+--------------------------------------+\n| MAX_USER_CONNECTIONS | Number of simultaneous connections |\n| | that can be accepted from the same |\n| | account; if it is 0, |\n| | max_connections will be used |\n| | instead; if max_connections is 0, |\n| | there is no limit for this |\n| | account\'s simultaneous connections. |\n+--------------------------------------+--------------------------------------+\n| MAX_STATEMENT_TIME | Timeout, in seconds, for statements |','','https://mariadb.com/kb/en/create-user/'); -update help_topic set description = CONCAT(description, '\n| | executed by the user. See also |\n| | Aborting Statements that Exceed a |\n| | Certain Time to Execute. |\n+--------------------------------------+--------------------------------------+\n\nIf any of these limits are set to 0, then there is no limit for that resource\nfor that user.\n\nHere is an example showing how to create a user with resource limits:\n\nCREATE USER \'someone\'@\'localhost\' WITH\n MAX_USER_CONNECTIONS 10\n MAX_QUERIES_PER_HOUR 200;\n\nThe resources are tracked per account, which means \'user\'@\'server\'; not per\nuser name or per connection.\n\nThe count can be reset for all users using FLUSH USER_RESOURCES, FLUSH\nPRIVILEGES or mysqladmin reload.\n\nPer account resource limits are stored in the user table, in the mysql\ndatabase. Columns used for resources limits are named max_questions,\nmax_updates, max_connections (for MAX_CONNECTIONS_PER_HOUR), and\nmax_user_connections (for MAX_USER_CONNECTIONS).\n\nAccount Names\n-------------\n\nAccount names have both a user name component and a host name component, and\nare specified as \'user_name\'@\'host_name\'.\n\nThe user name and host name may be unquoted, quoted as strings using double\nquotes (\") or single quotes (\'), or quoted as identifiers using backticks (`).\nYou must use quotes when using special characters (such as a hyphen) or\nwildcard characters. If you quote, you must quote the user name and host name\nseparately (for example \'user_name\'@\'host_name\').\n\nHost Name Component\n-------------------\n\nIf the host name is not provided, it is assumed to be \'%\'.\n\nHost names may contain the wildcard characters % and _. They are matched as if\nby the LIKE clause. If you need to use a wildcard character literally (for\nexample, to match a domain name with an underscore), prefix the character with\na backslash. See LIKE for more information on escaping wildcard characters.\n\nHost name matches are case-insensitive. Host names can match either domain\nnames or IP addresses. Use \'localhost\' as the host name to allow only local\nclient connections.\n\nYou can use a netmask to match a range of IP addresses using \'base_ip/netmask\'\nas the host name. A user with an IP address ip_addr will be allowed to connect\nif the following condition is true:\n\nip_addr & netmask = base_ip\n\nFor example, given a user:\n\nCREATE USER \'maria\'@\'247.150.130.0/255.255.255.0\';\n\nthe IP addresses satisfying this condition range from 247.150.130.0 to\n247.150.130.255.\n\nUsing 255.255.255.255 is equivalent to not using a netmask at all. Netmasks\ncannot be used for IPv6 addresses.\n\nNote that the credentials added when creating a user with the \'%\' wildcard\nhost will not grant access in all cases. For example, some systems come with\nan anonymous localhost user, and when connecting from localhost this will take\nprecedence.\n\nBefore MariaDB 10.6, the host name component could be up to 60 characters in\nlength. Starting from MariaDB 10.6, it can be up to 255 characters.\n\nUser Name Component\n-------------------\n\nUser names must match exactly, including case. A user name that is empty is\nknown as an anonymous account and is allowed to match a login attempt with any\nuser name component. These are described more in the next section.\n\nFor valid identifiers to use as user names, see Identifier Names.\n\nIt is possible for more than one account to match when a user connects.\nMariaDB selects the first matching account after sorting according to the\nfollowing criteria:\n\n* Accounts with an exact host name are sorted before accounts using a wildcard\nin the\nhost name. Host names using a netmask are considered to be exact for sorting.\n* Accounts with a wildcard in the host name are sorted according to the\nposition of\nthe first wildcard character. Those with a wildcard character later in the\nhost name\nsort before those with a wildcard character earlier in the host name.\n* Accounts with a non-empty user name sort before accounts with an empty user\nname.\n* Accounts with an empty user name are sorted last. As mentioned previously,\nthese are known as anonymous accounts. These are described more in the next\nsection.\n\nThe following table shows a list of example account as sorted by these\ncriteria:\n\n+---------+-------------+\n| User | Host |\n+---------+-------------+\n| joffrey | 192.168.0.3 |\n| | 192.168.0.% |\n| joffrey | 192.168.% |\n| | 192.168.% |\n+---------+-------------+\n\nOnce connected, you only have the privileges granted to the account that\nmatched, not all accounts that could have matched. For example, consider the\nfollowing commands:\n\nCREATE USER \'joffrey\'@\'192.168.0.3\';\nCREATE USER \'joffrey\'@\'%\';\nGRANT SELECT ON test.t1 to \'joffrey\'@\'192.168.0.3\';\nGRANT SELECT ON test.t2 to \'joffrey\'@\'%\';\n\nIf you connect as joffrey from 192.168.0.3, you will have the SELECT privilege\non the table test.t1, but not on the table test.t2. If you connect as joffrey\nfrom any other IP address, you will have the SELECT privilege on the table\ntest.t2, but not on the table test.t1.\n\nUsernames can be up to 80 characters long before 10.6 and starting from 10.6\nit can be 128 characters long.\n\nAnonymous Accounts\n------------------\n\nAnonymous accounts are accounts where the user name portion of the account\nname is empty. These accounts act as special catch-all accounts. If a user\nattempts to log into the system from a host, and an anonymous account exists\nwith a host name portion that matches the user\'s host, then the user will log\nin as the anonymous account if there is no more specific account match for the\nuser name that the user entered.\n\nFor example, here are some anonymous accounts:\n\nCREATE USER \'\'@\'localhost\';\nCREATE USER \'\'@\'192.168.0.3\';\n\nFixing a Legacy Default Anonymous Account\n-----------------------------------------\n\nOn some systems, the mysql.db table has some entries for the \'\'@\'%\' anonymous\naccount by default. Unfortunately, there is no matching entry in the\nmysql.user/mysql.global_priv_table table, which means that this anonymous\naccount doesn\'t exactly exist, but it does have privileges--usually on the\ndefault test database created by mysql_install_db. These account-less\nprivileges are a legacy that is leftover from a time when MySQL\'s privilege\nsystem was less advanced.\n\nThis situation means that you will run into errors if you try to create a\n\'\'@\'%\' account. For example:\n\nCREATE USER \'\'@\'%\';\nERROR 1396 (HY000): Operation CREATE USER failed for \'\'@\'%\'\n\nThe fix is to DELETE the row in the mysql.db table and then execute FLUSH\nPRIVILEGES:\n\nDELETE FROM mysql.db WHERE User=\'\' AND Host=\'%\';\nFLUSH PRIVILEGES;\n\nAnd then the account can be created:\n\nCREATE USER \'\'@\'%\';\nQuery OK, 0 rows affected (0.01 sec)\n\nSee MDEV-13486 for more information.\n\nPassword Expiry\n---------------\n\nMariaDB starting with 10.4.3\n----------------------------\nBesides automatic password expiry, as determined by default_password_lifetime,\npassword expiry times can be set on an individual user basis, overriding the\nglobal setting, for example:\n\nCREATE USER \'monty\'@\'localhost\' PASSWORD EXPIRE INTERVAL 120 DAY;\n\nSee User Password Expiry for more details.\n\nAccount Locking\n---------------\n\nMariaDB starting with 10.4.2\n----------------------------\nAccount locking permits privileged administrators to lock/unlock user\naccounts. No new client connections will be permitted if an account is locked\n(existing connections are not affected). For example:\n\nCREATE USER \'marijn\'@\'localhost\' ACCOUNT LOCK;\n\nSee Account Locking for more details.\n\nFrom MariaDB 10.4.7 and MariaDB 10.5.8, the lock_option and password_option\nclauses can occur in either order.\n\nURL: https://mariadb.com/kb/en/create-user/') WHERE help_topic_id = 104; -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (105,10,'ALTER USER','Syntax\n------\n\nALTER USER [IF EXISTS] \n user_specification [,user_specification] ...\n [REQUIRE {NONE | tls_option [[AND] tls_option] ...}]\n [WITH resource_option [resource_option] ...]\n [lock_option] [password_option]\n\nuser_specification:\n username [authentication_option]\n\nauthentication_option:\n IDENTIFIED BY \'password\'\n | IDENTIFIED BY PASSWORD \'password_hash\'\n | IDENTIFIED {VIA|WITH} authentication_rule [OR authentication_rule] ...\n\nauthentication_rule:\n authentication_plugin\n | authentication_plugin {USING|AS} \'authentication_string\'\n | authentication_plugin {USING|AS} PASSWORD(\'password\')\n\ntls_option\n SSL\n | X509\n | CIPHER \'cipher\'\n | ISSUER \'issuer\'\n | SUBJECT \'subject\'\n\nresource_option\n MAX_QUERIES_PER_HOUR count\n | MAX_UPDATES_PER_HOUR count\n | MAX_CONNECTIONS_PER_HOUR count\n | MAX_USER_CONNECTIONS count\n | MAX_STATEMENT_TIME time\n\npassword_option:\n PASSWORD EXPIRE\n | PASSWORD EXPIRE DEFAULT\n | PASSWORD EXPIRE NEVER\n | PASSWORD EXPIRE INTERVAL N DAY\n\nlock_option:\n ACCOUNT LOCK\n | ACCOUNT UNLOCK\n}\n\nDescription\n-----------\n\nThe ALTER USER statement modifies existing MariaDB accounts. To use it, you\nmust have the global CREATE USER privilege or the UPDATE privilege for the\nmysql database. The global SUPER privilege is also required if the read_only\nsystem variable is enabled.\n\nIf any of the specified user accounts do not yet exist, an error results. If\nan error occurs, ALTER USER will still modify the accounts that do not result\nin an error. Only one error is produced for all users which have not been\nmodified.\n\nIF EXISTS\n---------\n\nWhen the IF EXISTS clause is used, MariaDB will return a warning instead of an\nerror for each specified user that does not exist.\n\nAccount Names\n-------------\n\nFor ALTER USER statements, account names are specified as the username\nargument in the same way as they are for CREATE USER statements. See account\nnames from the CREATE USER page for details on how account names are specified.\n\nCURRENT_USER or CURRENT_USER() can also be used to alter the account logged\ninto the current session. For example, to change the current user\'s password\nto mariadb:\n\nALTER USER CURRENT_USER() IDENTIFIED BY \'mariadb\';\n\nAuthentication Options\n----------------------\n\nMariaDB starting with 10.4\n--------------------------\nFrom MariaDB 10.4, it is possible to use more than one authentication plugin\nfor each user account. For example, this can be useful to slowly migrate users\nto the more secure ed25519 authentication plugin over time, while allowing the\nold mysql_native_password authentication plugin as an alternative for the\ntransitional period. See Authentication from MariaDB 10.4 for more.\n\nWhen running ALTER USER, not specifying an authentication option in the\nIDENTIFIED VIA clause will remove that authentication method. (However this\nwas not the case before MariaDB 10.4.13, see MDEV-21928)\n\nFor example, a user is created with the ability to authenticate via both a\npassword and unix_socket:\n\nCREATE USER \'bob\'@\'localhost\' \n IDENTIFIED VIA mysql_native_password USING PASSWORD(\'pwd\')\n OR unix_socket;\n\nSHOW CREATE USER \'bob\'@\'localhost\'\\G\n*************************** 1. row ***************************\nCREATE USER for bob@localhost: CREATE USER `bob`@`localhost` \n IDENTIFIED VIA mysql_native_password\n USING \'*975B2CD4FF9AE554FE8AD33168FBFC326D2021DD\'\n OR unix_socket\n\nIf the user\'s password is updated, but unix_socket authentication is not\nspecified in the IDENTIFIED VIA clause, unix_socket authentication will no\nlonger be permitted.\n\nALTER USER \'bob\'@\'localhost\' IDENTIFIED VIA mysql_native_password \n USING PASSWORD(\'pwd2\');\n\nSHOW CREATE USER \'bob\'@\'localhost\'\\G\n*************************** 1. row ***************************\nCREATE USER for bob@localhost: CREATE USER `bob`@`localhost` \n IDENTIFIED BY PASSWORD \'*38366FDA01695B6A5A9DD4E428D9FB8F7EB75512\'\n\nIDENTIFIED BY \'password\'\n------------------------\n\nThe optional IDENTIFIED BY clause can be used to provide an account with a\npassword. The password should be specified in plain text. It will be hashed by\nthe PASSWORD function prior to being stored to the mysql.user table.\n\nFor example, if our password is mariadb, then we can set the account\'s\npassword with:\n\nALTER USER foo2@test IDENTIFIED BY \'mariadb\';\n\nIf you do not specify a password with the IDENTIFIED BY clause, the user will\nbe able to connect without a password. A blank password is not a wildcard to\nmatch any password. The user must connect without providing a password if no\npassword is set.\n\nThe only authentication plugins that this clause supports are\nmysql_native_password and mysql_old_password.\n\nIDENTIFIED BY PASSWORD \'password_hash\'\n--------------------------------------\n\nThe optional IDENTIFIED BY PASSWORD clause can be used to provide an account\nwith a password that has already been hashed. The password should be specified\nas a hash that was provided by the PASSWORD#function. It will be stored to the\nmysql.user table as-is.\n\nFor example, if our password is mariadb, then we can find the hash with:\n\nSELECT PASSWORD(\'mariadb\');\n+-------------------------------------------+\n| PASSWORD(\'mariadb\') |\n+-------------------------------------------+\n| *54958E764CE10E50764C2EECBB71D01F08549980 |\n+-------------------------------------------+\n\nAnd then we can set an account\'s password with the hash:\n\nALTER USER foo2@test \n IDENTIFIED BY PASSWORD \'*54958E764CE10E50764C2EECBB71D01F08549980\';\n\nIf you do not specify a password with the IDENTIFIED BY clause, the user will\nbe able to connect without a password. A blank password is not a wildcard to\nmatch any password. The user must connect without providing a password if no\npassword is set.\n\nThe only authentication plugins that this clause supports are\nmysql_native_password and mysql_old_password.\n\nIDENTIFIED {VIA|WITH} authentication_plugin\n-------------------------------------------\n\nThe optional IDENTIFIED VIA authentication_plugin allows you to specify that\nthe account should be authenticated by a specific authentication plugin. The\nplugin name must be an active authentication plugin as per SHOW PLUGINS. If it\ndoesn\'t show up in that output, then you will need to install it with INSTALL\nPLUGIN or INSTALL SONAME.\n\nFor example, this could be used with the PAM authentication plugin:\n\nALTER USER foo2@test IDENTIFIED VIA pam;\n\nSome authentication plugins allow additional arguments to be specified after a\nUSING or AS keyword. For example, the PAM authentication plugin accepts a\nservice name:\n\nALTER USER foo2@test IDENTIFIED VIA pam USING \'mariadb\';\n\nThe exact meaning of the additional argument would depend on the specific\nauthentication plugin.\n\nIn MariaDB 10.4 and later, the USING or AS keyword can also be used to provide\na plain-text password to a plugin if it\'s provided as an argument to the\nPASSWORD() function. This is only valid for authentication plugins that have\nimplemented a hook for the PASSWORD() function. For example, the ed25519\nauthentication plugin supports this:\n\nALTER USER safe@\'%\' IDENTIFIED VIA ed25519 USING PASSWORD(\'secret\');\n\nTLS Options\n-----------\n\nBy default, MariaDB transmits data between the server and clients without\nencrypting it. This is generally acceptable when the server and client run on\nthe same host or in networks where security is guaranteed through other means.\nHowever, in cases where the server and client exist on separate networks or\nthey are in a high-risk network, the lack of encryption does introduce\nsecurity concerns as a malicious actor could potentially eavesdrop on the\ntraffic as it is sent over the network between them.\n\nTo mitigate this concern, MariaDB allows you to encrypt data in transit\nbetween the server and clients using the Transport Layer Security (TLS)\nprotocol. TLS was formerly known as Secure Socket Layer (SSL), but strictly\nspeaking the SSL protocol is a predecessor to TLS and, that version of the\nprotocol is now considered insecure. The documentation still uses the term SSL\noften and for compatibility reasons TLS-related server system and status\nvariables still use the prefix ssl_, but internally, MariaDB only supports its\nsecure successors.\n\nSee Secure Connections Overview for more information about how to determine\nwhether your MariaDB server has TLS support.\n\nYou can set certain TLS-related restrictions for specific user accounts. For\ninstance, you might use this with user accounts that require access to\nsensitive data while sending it across networks that you do not control. These\nrestrictions can be enabled for a user account with the CREATE USER, ALTER\nUSER, or GRANT statements. The following options are available:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| REQUIRE NONE | TLS is not required for this account, but can |\n| | still be used. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SSL | The account must use TLS, but no valid X509 |\n| | certificate is required. This option cannot |\n| | be combined with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE X509 | The account must use TLS and must have a |\n| | valid X509 certificate. This option implies |\n| | REQUIRE SSL. This option cannot be combined |\n| | with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE ISSUER \'issuer\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the Certificate |\n| | Authority must be the one specified via the |\n| | string issuer. This option implies REQUIRE |\n| | X509. This option can be combined with the |\n| | SUBJECT, and CIPHER options in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SUBJECT \'subject\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the |\n| | certificate\'s Subject must be the one |\n| | specified via the string subject. This option |\n| | implies REQUIRE X509. This option can be |\n| | combined with the ISSUER, and CIPHER options |\n| | in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE CIPHER \'cipher\' | The account must use TLS, but no valid X509 |\n| | certificate is required. Also, the encryption |\n| | used for the connection must use a specific |\n| | cipher method specified in the string cipher. |\n| | This option implies REQUIRE SSL. This option |\n| | can be combined with the ISSUER, and SUBJECT |\n| | options in any order. |\n+---------------------------+------------------------------------------------+\n\nThe REQUIRE keyword must be used only once for all specified options, and the\nAND keyword can be used to separate individual options, but it is not required.\n\nFor example, you can alter a user account to require these TLS options with\nthe following:\n\nALTER USER \'alice\'@\'%\'\n REQUIRE SUBJECT \'/CN=alice/O=My Dom, Inc./C=US/ST=Oregon/L=Portland\' AND\n ISSUER \'/C=FI/ST=Somewhere/L=City/ O=Some Company/CN=Peter\nParker/emailAddress=p.parker@marvel.com\'\n AND CIPHER \'SHA-DES-CBC3-EDH-RSA\';\n\nIf any of these options are set for a specific user account, then any client\nwho tries to connect with that user account will have to be configured to\nconnect with TLS.\n\nSee Securing Connections for Client and Server for information on how to\nenable TLS on the client and server.\n\nResource Limit Options\n----------------------\n\nIt is possible to set per-account limits for certain server resources. The\nfollowing table shows the values that can be set per account:\n\n+------------------------------------+---------------------------------------+\n| Limit Type | Description |\n+------------------------------------+---------------------------------------+\n| MAX_QUERIES_PER_HOUR | Number of statements that the |\n| | account can issue per hour |\n| | (including updates) |\n+------------------------------------+---------------------------------------+\n| MAX_UPDATES_PER_HOUR | Number of updates (not queries) that |\n| | the account can issue per hour |\n+------------------------------------+---------------------------------------+\n| MAX_CONNECTIONS_PER_HOUR | Number of connections that the |\n| | account can start per hour |\n+------------------------------------+---------------------------------------+\n| MAX_USER_CONNECTIONS | Number of simultaneous connections |\n| | that can be accepted from the same |\n| | account; if it is 0, max_connections |\n| | will be used instead; if |\n| | max_connections is 0, there is no |\n| | limit for this account\'s |\n| | simultaneous connections. |','','https://mariadb.com/kb/en/alter-user/'); -update help_topic set description = CONCAT(description, '\n+------------------------------------+---------------------------------------+\n| MAX_STATEMENT_TIME | Timeout, in seconds, for statements |\n| | executed by the user. See also |\n| | Aborting Statements that Exceed a |\n| | Certain Time to Execute. |\n+------------------------------------+---------------------------------------+\n\nIf any of these limits are set to 0, then there is no limit for that resource\nfor that user.\n\nHere is an example showing how to set an account\'s resource limits:\n\nALTER USER \'someone\'@\'localhost\' WITH\n MAX_USER_CONNECTIONS 10\n MAX_QUERIES_PER_HOUR 200;\n\nThe resources are tracked per account, which means \'user\'@\'server\'; not per\nuser name or per connection.\n\nThe count can be reset for all users using FLUSH USER_RESOURCES, FLUSH\nPRIVILEGES or mysqladmin reload.\n\nPer account resource limits are stored in the user table, in the mysql\ndatabase. Columns used for resources limits are named max_questions,\nmax_updates, max_connections (for MAX_CONNECTIONS_PER_HOUR), and\nmax_user_connections (for MAX_USER_CONNECTIONS).\n\nPassword Expiry\n---------------\n\nMariaDB starting with 10.4.3\n----------------------------\nBesides automatic password expiry, as determined by default_password_lifetime,\npassword expiry times can be set on an individual user basis, overriding the\nglobal setting, for example:\n\nALTER USER \'monty\'@\'localhost\' PASSWORD EXPIRE INTERVAL 120 DAY;\nALTER USER \'monty\'@\'localhost\' PASSWORD EXPIRE NEVER;\nALTER USER \'monty\'@\'localhost\' PASSWORD EXPIRE DEFAULT;\n\nSee User Password Expiry for more details.\n\nAccount Locking\n---------------\n\nMariaDB starting with 10.4.2\n----------------------------\nAccount locking permits privileged administrators to lock/unlock user\naccounts. No new client connections will be permitted if an account is locked\n(existing connections are not affected). For example:\n\nALTER USER \'marijn\'@\'localhost\' ACCOUNT LOCK;\n\nSee Account Locking for more details.\n\nFrom MariaDB 10.4.7 and MariaDB 10.5.8, the lock_option and password_option\nclauses can occur in either order.\n\nURL: https://mariadb.com/kb/en/alter-user/') WHERE help_topic_id = 105; -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (106,10,'DROP USER','Syntax\n------\n\nDROP USER [IF EXISTS] user_name [, user_name] ...\n\nDescription\n-----------\n\nThe DROP USER statement removes one or more MariaDB accounts. It removes\nprivilege rows for the account from all grant tables. To use this statement,\nyou must have the global CREATE USER privilege or the DELETE privilege for the\nmysql database. Each account is named using the same format as for the CREATE\nUSER statement; for example, \'jeffrey\'@\'localhost\'. If you specify only the\nuser name part of the account name, a host name part of \'%\' is used. For\nadditional information about specifying account names, see CREATE USER.\n\nNote that, if you specify an account that is currently connected, it will not\nbe deleted until the connection is closed. The connection will not be\nautomatically closed.\n\nIf any of the specified user accounts do not exist, ERROR 1396 (HY000)\nresults. If an error occurs, DROP USER will still drop the accounts that do\nnot result in an error. Only one error is produced for all users which have\nnot been dropped:\n\nERROR 1396 (HY000): Operation DROP USER failed for \'u1\'@\'%\',\'u2\'@\'%\'\n\nFailed CREATE or DROP operations, for both users and roles, produce the same\nerror code.\n\nIF EXISTS\n---------\n\nIf the IF EXISTS clause is used, MariaDB will return a note instead of an\nerror if the user does not exist.\n\nExamples\n--------\n\nDROP USER bob;\n\nIF EXISTS:\n\nDROP USER bob;\nERROR 1396 (HY000): Operation DROP USER failed for \'bob\'@\'%\'\n\nDROP USER IF EXISTS bob;\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+---------------------------------------------+\n| Level | Code | Message |\n+-------+------+---------------------------------------------+\n| Note | 1974 | Can\'t drop user \'bob\'@\'%\'; it doesn\'t exist |\n+-------+------+---------------------------------------------+\n\nURL: https://mariadb.com/kb/en/drop-user/','','https://mariadb.com/kb/en/drop-user/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (107,10,'GRANT','Syntax\n------\n\nGRANT\n priv_type [(column_list)]\n [, priv_type [(column_list)]] ...\n ON [object_type] priv_level\n TO user_specification [ user_options ...]\n\nuser_specification:\n username [authentication_option]\n | PUBLIC\nauthentication_option:\n IDENTIFIED BY \'password\'\n | IDENTIFIED BY PASSWORD \'password_hash\'\n | IDENTIFIED {VIA|WITH} authentication_rule [OR authentication_rule ...]\n\nauthentication_rule:\n authentication_plugin\n | authentication_plugin {USING|AS} \'authentication_string\'\n | authentication_plugin {USING|AS} PASSWORD(\'password\')\n\nGRANT PROXY ON username\n TO user_specification [, user_specification ...]\n [WITH GRANT OPTION]\n\nGRANT rolename TO grantee [, grantee ...]\n [WITH ADMIN OPTION]\n\ngrantee:\n rolename\n username [authentication_option]\n\nuser_options:\n [REQUIRE {NONE | tls_option [[AND] tls_option] ...}]\n [WITH with_option [with_option] ...]\n\nobject_type:\n TABLE\n | FUNCTION\n | PROCEDURE\n | PACKAGE\n\npriv_level:\n *\n | *.*\n | db_name.*\n | db_name.tbl_name\n | tbl_name\n | db_name.routine_name\n\nwith_option:\n GRANT OPTION\n | resource_option\n\nresource_option:\n MAX_QUERIES_PER_HOUR count\n | MAX_UPDATES_PER_HOUR count\n | MAX_CONNECTIONS_PER_HOUR count\n | MAX_USER_CONNECTIONS count\n | MAX_STATEMENT_TIME time\n\ntls_option:\n SSL\n | X509\n | CIPHER \'cipher\'\n | ISSUER \'issuer\'\n | SUBJECT \'subject\'\n\nDescription\n-----------\n\nThe GRANT statement allows you to grant privileges or roles to accounts. To\nuse GRANT, you must have the GRANT OPTION privilege, and you must have the\nprivileges that you are granting.\n\nUse the REVOKE statement to revoke privileges granted with the GRANT statement.\n\nUse the SHOW GRANTS statement to determine what privileges an account has.\n\nAccount Names\n-------------\n\nFor GRANT statements, account names are specified as the username argument in\nthe same way as they are for CREATE USER statements. See account names from\nthe CREATE USER page for details on how account names are specified.\n\nImplicit Account Creation\n-------------------------\n\nThe GRANT statement also allows you to implicitly create accounts in some\ncases.\n\nIf the account does not yet exist, then GRANT can implicitly create it. To\nimplicitly create an account with GRANT, a user is required to have the same\nprivileges that would be required to explicitly create the account with the\nCREATE USER statement.\n\nIf the NO_AUTO_CREATE_USER SQL_MODE is set, then accounts can only be created\nif authentication information is specified, or with a CREATE USER statement.\nIf no authentication information is provided, GRANT will produce an error when\nthe specified account does not exist, for example:\n\nshow variables like \'%sql_mode%\' ;\n+---------------+--------------------------------------------+\n| Variable_name | Value |\n+---------------+--------------------------------------------+\n| sql_mode | NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |\n+---------------+--------------------------------------------+\n\nGRANT USAGE ON *.* TO \'user123\'@\'%\' IDENTIFIED BY \'\';\nERROR 1133 (28000): Can\'t find any matching row in the user table\n\nGRANT USAGE ON *.* TO \'user123\'@\'%\' \n IDENTIFIED VIA PAM using \'mariadb\' require ssl ;\nQuery OK, 0 rows affected (0.00 sec)\n\nselect host, user from mysql.user where user=\'user123\' ;\n\n+------+----------+\n| host | user |\n+------+----------+\n| % | user123 |\n+------+----------+\n\nPrivilege Levels\n----------------\n\nPrivileges can be set globally, for an entire database, for a table or\nroutine, or for individual columns in a table. Certain privileges can only be\nset at certain levels.\n\n* Global privileges priv_type are granted using *.* for\npriv_level. Global privileges include privileges to administer the database\nand manage user accounts, as well as privileges for all tables, functions, and\nprocedures. Global privileges are stored in the mysql.user table prior to\nMariaDB 10.4, and in mysql.global_priv table afterwards.\n* Database privileges priv_type are granted using db_name.*\nfor priv_level, or using just * to use default database. Database\nprivileges include privileges to create tables and functions, as well as\nprivileges for all tables, functions, and procedures in the database. Database\nprivileges are stored in the mysql.db table.\n* Table privileges priv_type are granted using db_name.tbl_name\nfor priv_level, or using just tbl_name to specify a table in the default\ndatabase. The TABLE keyword is optional. Table privileges include the\nability to select and change data in the table. Certain table privileges can\nbe granted for individual columns.\n* Column privileges priv_type are granted by specifying a table for\npriv_level and providing a column list after the privilege type. They allow\nyou to control exactly which columns in a table users can select and change.\n* Function privileges priv_type are granted using FUNCTION db_name.routine_name\nfor priv_level, or using just FUNCTION routine_name to specify a function\nin the default database.\n* Procedure privileges priv_type are granted using PROCEDURE\ndb_name.routine_name\nfor priv_level, or using just PROCEDURE routine_name to specify a procedure\nin the default database.\n\nThe USAGE Privilege\n-------------------\n\nThe USAGE privilege grants no real privileges. The SHOW GRANTS statement will\nshow a global USAGE privilege for a newly-created user. You can use USAGE with\nthe GRANT statement to change options like GRANT OPTION and\nMAX_USER_CONNECTIONS without changing any account privileges.\n\nThe ALL PRIVILEGES Privilege\n----------------------------\n\nThe ALL PRIVILEGES privilege grants all available privileges. Granting all\nprivileges only affects the given privilege level. For example, granting all\nprivileges on a table does not grant any privileges on the database or\nglobally.\n\nUsing ALL PRIVILEGES does not grant the special GRANT OPTION privilege.\n\nYou can use ALL instead of ALL PRIVILEGES.\n\nThe GRANT OPTION Privilege\n--------------------------\n\nUse the WITH GRANT OPTION clause to give users the ability to grant privileges\nto other users at the given privilege level. Users with the GRANT OPTION\nprivilege can only grant privileges they have. They cannot grant privileges at\na higher privilege level than they have the GRANT OPTION privilege.\n\nThe GRANT OPTION privilege cannot be set for individual columns. If you use\nWITH GRANT OPTION when specifying column privileges, the GRANT OPTION\nprivilege will be granted for the entire table.\n\nUsing the WITH GRANT OPTION clause is equivalent to listing GRANT OPTION as a\nprivilege.\n\nGlobal Privileges\n-----------------\n\nThe following table lists the privileges that can be granted globally. You can\nalso grant all database, table, and function privileges globally. When granted\nglobally, these privileges apply to all databases, tables, or functions,\nincluding those created later.\n\nTo set a global privilege, use *.* for priv_level.\n\nBINLOG ADMIN\n------------\n\nEnables administration of the binary log, including the PURGE BINARY LOGS\nstatement and setting the system variables:\n\n* binlog_annotate_row_events\n* binlog_cache_size\n* binlog_commit_wait_count\n* binlog_commit_wait_usec\n* binlog_direct_non_transactional_updates\n* binlog_expire_logs_seconds\n* binlog_file_cache_size\n* binlog_format\n* binlog_row_image\n* binlog_row_metadata\n* binlog_stmt_cache_size\n* expire_logs_days\n* log_bin_compress\n* log_bin_compress_min_len\n* log_bin_trust_function_creators\n* max_binlog_cache_size\n* max_binlog_size\n* max_binlog_stmt_cache_size\n* sql_log_bin and\n* sync_binlog.\n\nAdded in MariaDB 10.5.2.\n\nBINLOG MONITOR\n--------------\n\nNew name for REPLICATION CLIENT from MariaDB 10.5.2, (REPLICATION CLIENT still\nsupported as an alias for compatibility purposes). Permits running SHOW\ncommands related to the binary log, in particular the SHOW BINLOG STATUS and\nSHOW BINARY LOGS statements. Unlike REPLICATION CLIENT prior to MariaDB 10.5,\nSHOW REPLICA STATUS isn\'t included in this privilege, and REPLICA MONITOR is\nrequired.\n\nBINLOG REPLAY\n-------------\n\nEnables replaying the binary log with the BINLOG statement (generated by\nmariadb-binlog), executing SET timestamp when secure_timestamp is set to\nreplication, and setting the session values of system variables usually\nincluded in BINLOG output, in particular:\n\n* gtid_domain_id\n* gtid_seq_no\n* pseudo_thread_id\n* server_id.\n\nAdded in MariaDB 10.5.2\n\nCONNECTION ADMIN\n----------------\n\nEnables administering connection resource limit options. This includes\nignoring the limits specified by:\n\n* max_connections\n* max_user_connections and\n* max_password_errors.\n\nThe statements specified in init_connect are not executed, killing connections\nand queries owned by other users is permitted. The following\nconnection-related system variables can be changed:\n\n* connect_timeout\n* disconnect_on_expired_password\n* extra_max_connections\n* init_connect\n* max_connections\n* max_connect_errors\n* max_password_errors\n* proxy_protocol_networks\n* secure_auth\n* slow_launch_time\n* thread_pool_exact_stats\n* thread_pool_dedicated_listener\n* thread_pool_idle_timeout\n* thread_pool_max_threads\n* thread_pool_min_threads\n* thread_pool_oversubscribe\n* thread_pool_prio_kickup_timer\n* thread_pool_priority\n* thread_pool_size, and\n* thread_pool_stall_limit.\n\nAdded in MariaDB 10.5.2.\n\nCREATE USER\n-----------\n\nCreate a user using the CREATE USER statement, or implicitly create a user\nwith the GRANT statement.\n\nFEDERATED ADMIN\n---------------\n\nExecute CREATE SERVER, ALTER SERVER, and DROP SERVER statements. Added in\nMariaDB 10.5.2.\n\nFILE\n----\n\nRead and write files on the server, using statements like LOAD DATA INFILE or\nfunctions like LOAD_FILE(). Also needed to create CONNECT outward tables.\nMariaDB server must have the permissions to access those files.\n\nGRANT OPTION\n------------\n\nGrant global privileges. You can only grant privileges that you have.\n\nPROCESS\n-------\n\nShow information about the active processes, for example via SHOW PROCESSLIST\nor mysqladmin processlist. If you have the PROCESS privilege, you can see all\nthreads. Otherwise, you can see only your own threads (that is, threads\nassociated with the MariaDB account that you are using).\n\nREAD_ONLY ADMIN\n---------------\n\nUser can set the read_only system variable and allows the user to perform\nwrite operations, even when the read_only option is active. Added in MariaDB\n10.5.2.\n\nFrom MariaDB 10.11.0, the READ_ONLY ADMIN privilege has been removed from\nSUPER. The benefit of this is that one can remove the READ_ONLY ADMIN\nprivilege from all users and ensure that no one can make any changes on any\nnon-temporary tables. This is useful on replicas when one wants to ensure that\nthe replica is kept identical to the primary.\n\nRELOAD\n------\n\nExecute FLUSH statements or equivalent mariadb-admin/mysqladmin commands.\n\nREPLICATION CLIENT\n------------------\n\nExecute SHOW MASTER STATUS and SHOW BINARY LOGS informative statements.\nRenamed to BINLOG MONITOR in MariaDB 10.5.2 (but still supported as an alias\nfor compatibility reasons). SHOW SLAVE STATUS was part of REPLICATION CLIENT\nprior to MariaDB 10.5.\n\nREPLICATION MASTER ADMIN\n------------------------\n\nPermits administration of primary servers, including the SHOW REPLICA HOSTS\nstatement, and setting the gtid_binlog_state, gtid_domain_id,\nmaster_verify_checksum and server_id system variables. Added in MariaDB 10.5.2.\n\nREPLICA MONITOR\n---------------\n\nPermit SHOW REPLICA STATUS and SHOW RELAYLOG EVENTS. From MariaDB 10.5.9.\n\nWhen a user would upgrade from an older major release to a MariaDB 10.5 minor\nrelease prior to MariaDB 10.5.9, certain user accounts would lose\ncapabilities. For example, a user account that had the REPLICATION CLIENT\nprivilege in older major releases could run SHOW REPLICA STATUS, but after\nupgrading to a MariaDB 10.5 minor release prior to MariaDB 10.5.9, they could\nno longer run SHOW REPLICA STATUS, because that statement was changed to\nrequire the REPLICATION REPLICA ADMIN privilege.\n\nThis issue is fixed in MariaDB 10.5.9 with this new privilege, which now\ngrants the user the ability to execute SHOW [ALL] (SLAVE | REPLICA) STATUS.\n\nWhen a database is upgraded from an older major release to MariaDB Server\n10.5.9 or later, any user accounts with the REPLICATION CLIENT or REPLICATION\nSLAVE privileges will automatically be granted the new REPLICA MONITOR\nprivilege. The privilege fix occurs when the server is started up, not when\nmariadb-upgrade is performed.\n\nHowever, when a database is upgraded from an early 10.5 minor release to\n10.5.9 and later, the user will have to fix any user account privileges\nmanually.\n\nREPLICATION REPLICA\n-------------------\n\nSynonym for REPLICATION SLAVE. From MariaDB 10.5.1.\n\nREPLICATION SLAVE\n-----------------\n\nAccounts used by replica servers on the primary need this privilege. This is\nneeded to get the updates made on the master. From MariaDB 10.5.1, REPLICATION\nREPLICA is an alias for REPLICATION SLAVE.\n\nREPLICATION SLAVE ADMIN\n-----------------------\n\nPermits administering replica servers, including START REPLICA/SLAVE, STOP\nREPLICA/SLAVE, CHANGE MASTER, SHOW REPLICA/SLAVE STATUS, SHOW RELAYLOG EVENTS\nstatements, replaying the binary log with the BINLOG statement (generated by\nmariadb-binlog), and setting the system variables:\n\n* gtid_cleanup_batch_size\n* gtid_ignore_duplicates\n* gtid_pos_auto_engines\n* gtid_slave_pos\n* gtid_strict_mode\n* init_slave\n* read_binlog_speed_limit\n* relay_log_purge\n* relay_log_recovery\n* replicate_do_db\n* replicate_do_table\n* replicate_events_marked_for_skip\n* replicate_ignore_db\n* replicate_ignore_table\n* replicate_wild_do_table\n* replicate_wild_ignore_table\n* slave_compressed_protocol\n* slave_ddl_exec_mode\n* slave_domain_parallel_threads\n* slave_exec_mode\n* slave_max_allowed_packet\n* slave_net_timeout\n* slave_parallel_max_queued\n* slave_parallel_mode\n* slave_parallel_threads\n* slave_parallel_workers\n* slave_run_triggers_for_rbr\n* slave_sql_verify_checksum\n* slave_transaction_retry_interval\n* slave_type_conversions\n* sync_master_info\n* sync_relay_log, and\n* sync_relay_log_info.\n\nAdded in MariaDB 10.5.2.\n\nSET USER\n--------\n','','https://mariadb.com/kb/en/grant/'); -update help_topic set description = CONCAT(description, '\nEnables setting the DEFINER when creating triggers, views, stored functions\nand stored procedures. Added in MariaDB 10.5.2.\n\nSHOW DATABASES\n--------------\n\nList all databases using the SHOW DATABASES statement. Without the SHOW\nDATABASES privilege, you can still issue the SHOW DATABASES statement, but it\nwill only list databases containing tables on which you have privileges.\n\nSHUTDOWN\n--------\n\nShut down the server using SHUTDOWN or the mysqladmin shutdown command.\n\nSUPER\n-----\n\nExecute superuser statements: CHANGE MASTER TO, KILL (users who do not have\nthis privilege can only KILL their own threads), PURGE LOGS, SET global system\nvariables, or the mysqladmin debug command. Also, this permission allows the\nuser to write data even if the read_only startup option is set, enable or\ndisable logging, enable or disable replication on replica, specify a DEFINER\nfor statements that support that clause, connect once reaching the\nMAX_CONNECTIONS. If a statement has been specified for the init-connect mysqld\noption, that command will not be executed when a user with SUPER privileges\nconnects to the server.\n\nThe SUPER privilege has been split into multiple smaller privileges from\nMariaDB 10.5.2 to allow for more fine-grained privileges, although it remains\nan alias for these smaller privileges.\n\nFrom MariaDB 10.11.0, the READ_ONLY ADMIN privilege has been removed from\nSUPER. The benefit of this is that one can remove the READ_ONLY ADMIN\nprivilege from all users and ensure that no one can make any changes on any\nnon-temporary tables. This is useful on replicas when one wants to ensure that\nthe replica is kept identical to the primary.\n\nDatabase Privileges\n-------------------\n\nThe following table lists the privileges that can be granted at the database\nlevel. You can also grant all table and function privileges at the database\nlevel. Table and function privileges on a database apply to all tables or\nfunctions in that database, including those created later.\n\nTo set a privilege for a database, specify the database using db_name.* for\npriv_level, or just use * to specify the default database.\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |\n+----------------------------------+-----------------------------------------+\n| CREATE | Create a database using the CREATE |\n| | DATABASE statement, when the privilege |\n| | is granted for a database. You can |\n| | grant the CREATE privilege on |\n| | databases that do not yet exist. This |\n| | also grants the CREATE privilege on |\n| | all tables in the database. |\n+----------------------------------+-----------------------------------------+\n| CREATE ROUTINE | Create Stored Programs using the |\n| | CREATE PROCEDURE and CREATE FUNCTION |\n| | statements. |\n+----------------------------------+-----------------------------------------+\n| CREATE TEMPORARY TABLES | Create temporary tables with the |\n| | CREATE TEMPORARY TABLE statement. This |\n| | privilege enable writing and dropping |\n| | those temporary tables |\n+----------------------------------+-----------------------------------------+\n| DROP | Drop a database using the DROP |\n| | DATABASE statement, when the privilege |\n| | is granted for a database. This also |\n| | grants the DROP privilege on all |\n| | tables in the database. |\n+----------------------------------+-----------------------------------------+\n| EVENT | Create, drop and alter EVENTs. |\n+----------------------------------+-----------------------------------------+\n| GRANT OPTION | Grant database privileges. You can |\n| | only grant privileges that you have. |\n+----------------------------------+-----------------------------------------+\n| LOCK TABLES | Acquire explicit locks using the LOCK |\n| | TABLES statement; you also need to |\n| | have the SELECT privilege on a table, |\n| | in order to lock it. |\n+----------------------------------+-----------------------------------------+\n\nTable Privileges\n----------------\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |\n+----------------------------------+-----------------------------------------+\n| ALTER | Change the structure of an existing |\n| | table using the ALTER TABLE statement. |\n+----------------------------------+-----------------------------------------+\n| CREATE | Create a table using the CREATE TABLE |\n| | statement. You can grant the CREATE |\n| | privilege on tables that do not yet |\n| | exist. |\n+----------------------------------+-----------------------------------------+\n| CREATE VIEW | Create a view using the CREATE_VIEW |\n| | statement. |\n+----------------------------------+-----------------------------------------+\n| DELETE | Remove rows from a table using the |\n| | DELETE statement. |\n+----------------------------------+-----------------------------------------+\n| DELETE HISTORY | Remove historical rows from a table |\n| | using the DELETE HISTORY statement. |\n| | Displays as DELETE VERSIONING ROWS |\n| | when running SHOW GRANTS until MariaDB |\n| | 10.3.15 and until MariaDB 10.4.5 |\n| | (MDEV-17655), or when running SHOW |\n| | PRIVILEGES until MariaDB 10.5.2, |\n| | MariaDB 10.4.13 and MariaDB 10.3.23 |\n| | (MDEV-20382). From MariaDB 10.3.4. |\n| | From MariaDB 10.3.5, if a user has the |\n| | SUPER privilege but not this |\n| | privilege, running mysql_upgrade will |\n| | grant this privilege as well. |\n+----------------------------------+-----------------------------------------+\n| DROP | Drop a table using the DROP TABLE |\n| | statement or a view using the DROP |\n| | VIEW statement. Also required to |\n| | execute the TRUNCATE TABLE statement. |\n+----------------------------------+-----------------------------------------+\n| GRANT OPTION | Grant table privileges. You can only |\n| | grant privileges that you have. |\n+----------------------------------+-----------------------------------------+\n| INDEX | Create an index on a table using the |\n| | CREATE INDEX statement. Without the |\n| | INDEX privilege, you can still create |\n| | indexes when creating a table using |\n| | the CREATE TABLE statement if the you |\n| | have the CREATE privilege, and you can |\n| | create indexes using the ALTER TABLE |\n| | statement if you have the ALTER |\n| | privilege. |\n+----------------------------------+-----------------------------------------+\n| INSERT | Add rows to a table using the INSERT |\n| | statement. The INSERT privilege can |\n| | also be set on individual columns; see |\n| | Column Privileges below for details. |\n+----------------------------------+-----------------------------------------+\n| REFERENCES | Unused. |\n+----------------------------------+-----------------------------------------+\n| SELECT | Read data from a table using the |\n| | SELECT statement. The SELECT privilege |\n| | can also be set on individual columns; |\n| | see Column Privileges below for |\n| | details. |\n+----------------------------------+-----------------------------------------+\n| SHOW VIEW | Show the CREATE VIEW statement to |\n| | create a view using the SHOW CREATE |\n| | VIEW statement. |\n+----------------------------------+-----------------------------------------+\n| TRIGGER | Execute triggers associated to tables |\n| | you update, execute the CREATE TRIGGER |\n| | and DROP TRIGGER statements. You will |\n| | still be able to see triggers. |\n+----------------------------------+-----------------------------------------+\n| UPDATE | Update existing rows in a table using |\n| | the UPDATE statement. UPDATE |\n| | statements usually include a WHERE |\n| | clause to update only certain rows. |\n| | You must have SELECT privileges on the |\n| | table or the appropriate columns for |\n| | the WHERE clause. The UPDATE privilege |\n| | can also be set on individual columns; |\n| | see Column Privileges below for |\n| | details. |\n+----------------------------------+-----------------------------------------+\n\nColumn Privileges\n-----------------\n\nSome table privileges can be set for individual columns of a table. To use\ncolumn privileges, specify the table explicitly and provide a list of column\nnames after the privilege type. For example, the following statement would\nallow the user to read the names and positions of employees, but not other\ninformation from the same table, such as salaries.\n\nGRANT SELECT (name, position) on Employee to \'jeffrey\'@\'localhost\';\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |\n+----------------------------------+-----------------------------------------+\n| INSERT (column_list) | Add rows specifying values in columns |\n| | using the INSERT statement. If you |\n| | only have column-level INSERT |\n| | privileges, you must specify the |\n| | columns you are setting in the INSERT |\n| | statement. All other columns will be |\n| | set to their default values, or NULL. |\n+----------------------------------+-----------------------------------------+\n| REFERENCES (column_list) | Unused. |\n+----------------------------------+-----------------------------------------+\n| SELECT (column_list) | Read values in columns using the |\n| | SELECT statement. You cannot access or |\n| | query any columns for which you do not |\n| | have SELECT privileges, including in |\n| | WHERE, ON, GROUP BY, and ORDER BY |\n| | clauses. |\n+----------------------------------+-----------------------------------------+\n| UPDATE (column_list) | Update values in columns of existing |\n| | rows using the UPDATE statement. |\n| | UPDATE statements usually include a |\n| | WHERE clause to update only certain |\n| | rows. You must have SELECT privileges |\n| | on the table or the appropriate |\n| | columns for the WHERE clause. |\n+----------------------------------+-----------------------------------------+\n\nFunction Privileges\n-------------------\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |') WHERE help_topic_id = 107; -update help_topic set description = CONCAT(description, '\n+----------------------------------+-----------------------------------------+\n| ALTER ROUTINE | Change the characteristics of a stored |\n| | function using the ALTER FUNCTION |\n| | statement. |\n+----------------------------------+-----------------------------------------+\n| EXECUTE | Use a stored function. You need SELECT |\n| | privileges for any tables or columns |\n| | accessed by the function. |\n+----------------------------------+-----------------------------------------+\n| GRANT OPTION | Grant function privileges. You can |\n| | only grant privileges that you have. |\n+----------------------------------+-----------------------------------------+\n\nProcedure Privileges\n--------------------\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |\n+----------------------------------+-----------------------------------------+\n| ALTER ROUTINE | Change the characteristics of a stored |\n| | procedure using the ALTER PROCEDURE |\n| | statement. |\n+----------------------------------+-----------------------------------------+\n| EXECUTE | Execute a stored procedure using the |\n| | CALL statement. The privilege to call |\n| | a procedure may allow you to perform |\n| | actions you wouldn\'t otherwise be able |\n| | to do, such as insert rows into a |\n| | table. |\n+----------------------------------+-----------------------------------------+\n| GRANT OPTION | Grant procedure privileges. You can |\n| | only grant privileges that you have. |\n+----------------------------------+-----------------------------------------+\n\nGRANT EXECUTE ON PROCEDURE mysql.create_db TO maintainer;\n\nProxy Privileges\n----------------\n\n+----------------------------------+-----------------------------------------+\n| Privilege | Description |\n+----------------------------------+-----------------------------------------+\n| PROXY | Permits one user to be a proxy for |\n| | another. |\n+----------------------------------+-----------------------------------------+\n\nThe PROXY privilege allows one user to proxy as another user, which means\ntheir privileges change to that of the proxy user, and the CURRENT_USER()\nfunction returns the user name of the proxy user.\n\nThe PROXY privilege only works with authentication plugins that support it.\nThe default mysql_native_password authentication plugin does not support proxy\nusers.\n\nThe pam authentication plugin is the only plugin included with MariaDB that\ncurrently supports proxy users. The PROXY privilege is commonly used with the\npam authentication plugin to enable user and group mapping with PAM.\n\nFor example, to grant the PROXY privilege to an anonymous account that\nauthenticates with the pam authentication plugin, you could execute the\nfollowing:\n\nCREATE USER \'dba\'@\'%\' IDENTIFIED BY \'strongpassword\';\nGRANT ALL PRIVILEGES ON *.* TO \'dba\'@\'%\' ;\n\nCREATE USER \'\'@\'%\' IDENTIFIED VIA pam USING \'mariadb\';\nGRANT PROXY ON \'dba\'@\'%\' TO \'\'@\'%\';\n\nA user account can only grant the PROXY privilege for a specific user account\nif the granter also has the PROXY privilege for that specific user account,\nand if that privilege is defined WITH GRANT OPTION. For example, the following\nexample fails because the granter does not have the PROXY privilege for that\nspecific user account at all:\n\nSELECT USER(), CURRENT_USER();\n+-----------------+-----------------+\n| USER() | CURRENT_USER() |\n+-----------------+-----------------+\n| alice@localhost | alice@localhost |\n+-----------------+-----------------+\n\nSHOW GRANTS;\n+------------------------------------------------------------------------------\n----------------------------------------+\n| Grants for alice@localhost \n |\n+------------------------------------------------------------------------------\n----------------------------------------+\n| GRANT ALL PRIVILEGES ON *.* TO \'alice\'@\'localhost\' IDENTIFIED BY PASSWORD\n\'*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19\' |\n+------------------------------------------------------------------------------\n----------------------------------------+\n\nGRANT PROXY ON \'dba\'@\'localhost\' TO \'bob\'@\'localhost\';\nERROR 1698 (28000): Access denied for user \'alice\'@\'localhost\'\n\nAnd the following example fails because the granter does have the PROXY\nprivilege for that specific user account, but it is not defined WITH GRANT\nOPTION:\n\nSELECT USER(), CURRENT_USER();\n+-----------------+-----------------+\n| USER() | CURRENT_USER() |\n+-----------------+-----------------+\n| alice@localhost | alice@localhost |\n+-----------------+-----------------+\n\nSHOW GRANTS;\n+------------------------------------------------------------------------------\n----------------------------------------+\n| Grants for alice@localhost \n |\n+------------------------------------------------------------------------------\n----------------------------------------+\n| GRANT ALL PRIVILEGES ON *.* TO \'alice\'@\'localhost\' IDENTIFIED BY PASSWORD\n\'*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19\' |\n| GRANT PROXY ON \'dba\'@\'localhost\' TO \'alice\'@\'localhost\' \n |\n+------------------------------------------------------------------------------\n----------------------------------------+\n\nGRANT PROXY ON \'dba\'@\'localhost\' TO \'bob\'@\'localhost\';\nERROR 1698 (28000): Access denied for user \'alice\'@\'localhost\'\n\nBut the following example succeeds because the granter does have the PROXY\nprivilege for that specific user account, and it is defined WITH GRANT OPTION:\n\nSELECT USER(), CURRENT_USER();\n+-----------------+-----------------+\n| USER() | CURRENT_USER() |\n+-----------------+-----------------+\n| alice@localhost | alice@localhost |\n+-----------------+-----------------+\n\nSHOW GRANTS;\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n| Grants for alice@localhost \n |\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n| GRANT ALL PRIVILEGES ON *.* TO \'alice\'@\'localhost\' IDENTIFIED BY PASSWORD\n\'*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19\' WITH GRANT OPTION |\n| GRANT PROXY ON \'dba\'@\'localhost\' TO \'alice\'@\'localhost\' WITH GRANT OPTION \n |\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n\nGRANT PROXY ON \'dba\'@\'localhost\' TO \'bob\'@\'localhost\';\n\nA user account can grant the PROXY privilege for any other user account if the\ngranter has the PROXY privilege for the \'\'@\'%\' anonymous user account, like\nthis:\n\nGRANT PROXY ON \'\'@\'%\' TO \'dba\'@\'localhost\' WITH GRANT OPTION;\n\nFor example, the following example succeeds because the user can grant the\nPROXY privilege for any other user account:\n\nSELECT USER(), CURRENT_USER();\n+-----------------+-----------------+\n| USER() | CURRENT_USER() |\n+-----------------+-----------------+\n| alice@localhost | alice@localhost |\n+-----------------+-----------------+\n\nSHOW GRANTS;\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n| Grants for alice@localhost \n |\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n| GRANT ALL PRIVILEGES ON *.* TO \'alice\'@\'localhost\' IDENTIFIED BY PASSWORD\n\'*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19\' WITH GRANT OPTION |\n| GRANT PROXY ON \'\'@\'%\' TO \'alice\'@\'localhost\' WITH GRANT OPTION \n |\n+------------------------------------------------------------------------------\n----------------------------------------------------------+\n\nGRANT PROXY ON \'app1_dba\'@\'localhost\' TO \'bob\'@\'localhost\';\nQuery OK, 0 rows affected (0.004 sec)\n\nGRANT PROXY ON \'app2_dba\'@\'localhost\' TO \'carol\'@\'localhost\';\nQuery OK, 0 rows affected (0.004 sec)\n\nThe default root user accounts created by mysql_install_db have this\nprivilege. For example:\n\nGRANT ALL PRIVILEGES ON *.* TO \'root\'@\'localhost\' WITH GRANT OPTION;\nGRANT PROXY ON \'\'@\'%\' TO \'root\'@\'localhost\' WITH GRANT OPTION;\n\nThis allows the default root user accounts to grant the PROXY privilege for\nany other user account, and it also allows the default root user accounts to\ngrant others the privilege to do the same.\n\nAuthentication Options\n----------------------\n\nThe authentication options for the GRANT statement are the same as those for\nthe CREATE USER statement.\n\nIDENTIFIED BY \'password\'\n------------------------\n\nThe optional IDENTIFIED BY clause can be used to provide an account with a\npassword. The password should be specified in plain text. It will be hashed by\nthe PASSWORD function prior to being stored.\n\nFor example, if our password is mariadb, then we can create the user with:\n\nGRANT USAGE ON *.* TO foo2@test IDENTIFIED BY \'mariadb\';\n\nIf you do not specify a password with the IDENTIFIED BY clause, the user will\nbe able to connect without a password. A blank password is not a wildcard to\nmatch any password. The user must connect without providing a password if no\npassword is set.\n\nIf the user account already exists and if you provide the IDENTIFIED BY\nclause, then the user\'s password will be changed. You must have the privileges\nneeded for the SET PASSWORD statement to change a user\'s password with GRANT.\n\nThe only authentication plugins that this clause supports are\nmysql_native_password and mysql_old_password.\n\nIDENTIFIED BY PASSWORD \'password_hash\'\n--------------------------------------\n\nThe optional IDENTIFIED BY PASSWORD clause can be used to provide an account\nwith a password that has already been hashed. The password should be specified\nas a hash that was provided by the PASSWORD function. It will be stored as-is.\n\nFor example, if our password is mariadb, then we can find the hash with:\n\nSELECT PASSWORD(\'mariadb\');\n+-------------------------------------------+\n| PASSWORD(\'mariadb\') |\n+-------------------------------------------+\n| *54958E764CE10E50764C2EECBB71D01F08549980 |\n+-------------------------------------------+\n1 row in set (0.00 sec)\n\nAnd then we can create a user with the hash:\n\nGRANT USAGE ON *.* TO foo2@test IDENTIFIED BY \n PASSWORD \'*54958E764CE10E50764C2EECBB71D01F08549980\';\n\nIf you do not specify a password with the IDENTIFIED BY clause, the user will\nbe able to connect without a password. A blank password is not a wildcard to\nmatch any password. The user must connect without providing a password if no\npassword is set.\n\nIf the user account already exists and if you provide the IDENTIFIED BY\nclause, then the user\'s password will be changed. You must have the privileges\nneeded for the SET PASSWORD statement to change a user\'s password with GRANT.\n\nThe only authentication plugins that this clause supports are\nmysql_native_password and mysql_old_password.\n\nIDENTIFIED {VIA|WITH} authentication_plugin\n-------------------------------------------\n\nThe optional IDENTIFIED VIA authentication_plugin allows you to specify that\nthe account should be authenticated by a specific authentication plugin. The\nplugin name must be an active authentication plugin as per SHOW PLUGINS. If it\ndoesn\'t show up in that output, then you will need to install it with INSTALL\nPLUGIN or INSTALL SONAME.\n\nFor example, this could be used with the PAM authentication plugin:\n\nGRANT USAGE ON *.* TO foo2@test IDENTIFIED VIA pam;\n\nSome authentication plugins allow additional arguments to be specified after a\nUSING or AS keyword. For example, the PAM authentication plugin accepts a\nservice name:\n\nGRANT USAGE ON *.* TO foo2@test IDENTIFIED VIA pam USING \'mariadb\';\n\nThe exact meaning of the additional argument would depend on the specific\nauthentication plugin.\n\nMariaDB starting with 10.4.0\n----------------------------\nThe USING or AS keyword can also be used to provide a plain-text password to a\nplugin if it\'s provided as an argument to the PASSWORD() function. This is\nonly valid for authentication plugins that have implemented a hook for the\nPASSWORD() function. For example, the ed25519 authentication plugin supports\nthis:\n\nCREATE USER safe@\'%\' IDENTIFIED VIA ed25519 \n USING PASSWORD(\'secret\');\n\nMariaDB starting with 10.4.3\n----------------------------\nOne can specify many authentication plugins, they all work as alternatives\nways of authenticating a user:\n\nCREATE USER safe@\'%\' IDENTIFIED VIA ed25519 \n USING PASSWORD(\'secret\') OR unix_socket;\n\nBy default, when you create a user without specifying an authentication\nplugin, MariaDB uses the mysql_native_password plugin.\n\nResource Limit Options\n----------------------\n\nIt is possible to set per-account limits for certain server resources. The\nfollowing table shows the values that can be set per account:\n\n+--------------------------------------+--------------------------------------+') WHERE help_topic_id = 107; -update help_topic set description = CONCAT(description, '\n| Limit Type | Decription |\n+--------------------------------------+--------------------------------------+\n| MAX_QUERIES_PER_HOUR | Number of statements that the |\n| | account can issue per hour |\n| | (including updates) |\n+--------------------------------------+--------------------------------------+\n| MAX_UPDATES_PER_HOUR | Number of updates (not queries) |\n| | that the account can issue per hour |\n+--------------------------------------+--------------------------------------+\n| MAX_CONNECTIONS_PER_HOUR | Number of connections that the |\n| | account can start per hour |\n+--------------------------------------+--------------------------------------+\n| MAX_USER_CONNECTIONS | Number of simultaneous connections |\n| | that can be accepted from the same |\n| | account; if it is 0, |\n| | max_connections will be used |\n| | instead; if max_connections is 0, |\n| | there is no limit for this |\n| | account\'s simultaneous connections. |\n+--------------------------------------+--------------------------------------+\n| MAX_STATEMENT_TIME | Timeout, in seconds, for statements |\n| | executed by the user. See also |\n| | Aborting Statements that Exceed a |\n| | Certain Time to Execute. |\n+--------------------------------------+--------------------------------------+\n\nIf any of these limits are set to 0, then there is no limit for that resource\nfor that user.\n\nTo set resource limits for an account, if you do not want to change that\naccount\'s privileges, you can issue a GRANT statement with the USAGE\nprivilege, which has no meaning. The statement can name some or all limit\ntypes, in any order.\n\nHere is an example showing how to set resource limits:\n\nGRANT USAGE ON *.* TO \'someone\'@\'localhost\' WITH\n MAX_USER_CONNECTIONS 0\n MAX_QUERIES_PER_HOUR 200;\n\nThe resources are tracked per account, which means \'user\'@\'server\'; not per\nuser name or per connection.\n\nThe count can be reset for all users using FLUSH USER_RESOURCES, FLUSH\nPRIVILEGES or mysqladmin reload.\n\nUsers with the CONNECTION ADMIN privilege (in MariaDB 10.5.2 and later) or the\nSUPER privilege are not restricted by max_user_connections, max_connections,\nor max_password_errors.\n\nPer account resource limits are stored in the user table, in the mysql\ndatabase. Columns used for resources limits are named max_questions,\nmax_updates, max_connections (for MAX_CONNECTIONS_PER_HOUR), and\nmax_user_connections (for MAX_USER_CONNECTIONS).\n\nTLS Options\n-----------\n\nBy default, MariaDB transmits data between the server and clients without\nencrypting it. This is generally acceptable when the server and client run on\nthe same host or in networks where security is guaranteed through other means.\nHowever, in cases where the server and client exist on separate networks or\nthey are in a high-risk network, the lack of encryption does introduce\nsecurity concerns as a malicious actor could potentially eavesdrop on the\ntraffic as it is sent over the network between them.\n\nTo mitigate this concern, MariaDB allows you to encrypt data in transit\nbetween the server and clients using the Transport Layer Security (TLS)\nprotocol. TLS was formerly known as Secure Socket Layer (SSL), but strictly\nspeaking the SSL protocol is a predecessor to TLS and, that version of the\nprotocol is now considered insecure. The documentation still uses the term SSL\noften and for compatibility reasons TLS-related server system and status\nvariables still use the prefix ssl_, but internally, MariaDB only supports its\nsecure successors.\n\nSee Secure Connections Overview for more information about how to determine\nwhether your MariaDB server has TLS support.\n\nYou can set certain TLS-related restrictions for specific user accounts. For\ninstance, you might use this with user accounts that require access to\nsensitive data while sending it across networks that you do not control. These\nrestrictions can be enabled for a user account with the CREATE USER, ALTER\nUSER, or GRANT statements. The following options are available:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| REQUIRE NONE | TLS is not required for this account, but can |\n| | still be used. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SSL | The account must use TLS, but no valid X509 |\n| | certificate is required. This option cannot |\n| | be combined with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE X509 | The account must use TLS and must have a |\n| | valid X509 certificate. This option implies |\n| | REQUIRE SSL. This option cannot be combined |\n| | with other TLS options. |\n+---------------------------+------------------------------------------------+\n| REQUIRE ISSUER \'issuer\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the Certificate |\n| | Authority must be the one specified via the |\n| | string issuer. This option implies REQUIRE |\n| | X509. This option can be combined with the |\n| | SUBJECT, and CIPHER options in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE SUBJECT \'subject\' | The account must use TLS and must have a |\n| | valid X509 certificate. Also, the |\n| | certificate\'s Subject must be the one |\n| | specified via the string subject. This option |\n| | implies REQUIRE X509. This option can be |\n| | combined with the ISSUER, and CIPHER options |\n| | in any order. |\n+---------------------------+------------------------------------------------+\n| REQUIRE CIPHER \'cipher\' | The account must use TLS, but no valid X509 |\n| | certificate is required. Also, the encryption |\n| | used for the connection must use a specific |\n| | cipher method specified in the string cipher. |\n| | This option implies REQUIRE SSL. This option |\n| | can be combined with the ISSUER, and SUBJECT |\n| | options in any order. |\n+---------------------------+------------------------------------------------+\n\nThe REQUIRE keyword must be used only once for all specified options, and the\nAND keyword can be used to separate individual options, but it is not required.\n\nFor example, you can create a user account that requires these TLS options\nwith the following:\n\nGRANT USAGE ON *.* TO \'alice\'@\'%\'\n REQUIRE SUBJECT \'/CN=alice/O=My Dom, Inc./C=US/ST=Oregon/L=Portland\'\n AND ISSUER \'/C=FI/ST=Somewhere/L=City/ O=Some Company/CN=Peter\nParker/emailAddress=p.parker@marvel.com\'\n AND CIPHER \'SHA-DES-CBC3-EDH-RSA\';\n\nIf any of these options are set for a specific user account, then any client\nwho tries to connect with that user account will have to be configured to\nconnect with TLS.\n\nSee Securing Connections for Client and Server for information on how to\nenable TLS on the client and server.\n\nRoles\n-----\n\nSyntax\n------\n\nGRANT role TO grantee [, grantee ... ]\n[ WITH ADMIN OPTION ]\n\ngrantee:\n rolename\n username [authentication_option]\n\nThe GRANT statement is also used to grant the use a role to one or more users\nor other roles. In order to be able to grant a role, the grantor doing so must\nhave permission to do so (see WITH ADMIN in the CREATE ROLE article).\n\nSpecifying the WITH ADMIN OPTION permits the grantee to in turn grant the role\nto another.\n\nFor example, the following commands show how to grant the same role to a\ncouple different users.\n\nGRANT journalist TO hulda;\n\nGRANT journalist TO berengar WITH ADMIN OPTION;\n\nIf a user has been granted a role, they do not automatically obtain all\npermissions associated with that role. These permissions are only in use when\nthe user activates the role with the SET ROLE statement.\n\nTO PUBLIC\n---------\n\nMariaDB starting with 10.11\n---------------------------\n\nSyntax\n------\n\nGRANT ON . TO PUBLIC;\nREVOKE ON . FROM PUBLIC;\n\nGRANT ... TO PUBLIC grants privileges to all users with access to the server.\nThe privileges also apply to users created after the privileges are granted.\nThis can be useful when one only wants to state once that all users need to\nhave a certain set of privileges.\n\nWhen running SHOW GRANTS, a user will also see all privileges inherited from\nPUBLIC. SHOW GRANTS FOR PUBLIC will only show TO PUBLIC grants.\n\nGrant Examples\n--------------\n\nGranting Root-like Privileges\n-----------------------------\n\nYou can create a user that has privileges similar to the default root accounts\nby executing the following:\n\nCREATE USER \'alexander\'@\'localhost\';\nGRANT ALL PRIVILEGES ON *.* to \'alexander\'@\'localhost\' WITH GRANT OPTION;\n\nURL: https://mariadb.com/kb/en/grant/') WHERE help_topic_id = 107; -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (108,10,'RENAME USER','Syntax\n------\n\nRENAME USER old_user TO new_user\n [, old_user TO new_user] ...\n\nDescription\n-----------\n\nThe RENAME USER statement renames existing MariaDB accounts. To use it, you\nmust have the global CREATE USER privilege or the UPDATE privilege for the\nmysql database. Each account is named using the same format as for the CREATE\nUSER statement; for example, \'jeffrey\'@\'localhost\'. If you specify only the\nuser name part of the account name, a host name part of \'%\' is used.\n\nIf any of the old user accounts do not exist or any of the new user accounts\nalready exist, ERROR 1396 (HY000) results. If an error occurs, RENAME USER\nwill still rename the accounts that do not result in an error.\n\nExamples\n--------\n\nCREATE USER \'donald\', \'mickey\';\nRENAME USER \'donald\' TO \'duck\'@\'localhost\', \'mickey\' TO \'mouse\'@\'localhost\';\n\nURL: https://mariadb.com/kb/en/rename-user/','','https://mariadb.com/kb/en/rename-user/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (109,10,'REVOKE','Privileges\n----------\n\nSyntax\n------\n\nREVOKE \n priv_type [(column_list)]\n [, priv_type [(column_list)]] ...\n ON [object_type] priv_level\n FROM user [, user] ...\n\nREVOKE ALL PRIVILEGES, GRANT OPTION\n FROM user [, user] ...\n\nDescription\n-----------\n\nThe REVOKE statement enables system administrators to revoke privileges (or\nroles - see section below) from MariaDB accounts. Each account is named using\nthe same format as for the GRANT statement; for example,\n\'jeffrey\'@\'localhost\'. If you specify only the user name part of the account\nname, a host name part of \'%\' is used. For details on the levels at which\nprivileges exist, the allowable priv_type and priv_level values, and the\nsyntax for specifying users and passwords, see GRANT.\n\nTo use the first REVOKE syntax, you must have the GRANT OPTION privilege, and\nyou must have the privileges that you are revoking.\n\nTo revoke all privileges, use the second syntax, which drops all global,\ndatabase, table, column, and routine privileges for the named user or users:\n\nREVOKE ALL PRIVILEGES, GRANT OPTION FROM user [, user] ...\n\nTo use this REVOKE syntax, you must have the global CREATE USER privilege or\nthe UPDATE privilege for the mysql database. See GRANT.\n\nExamples\n--------\n\nREVOKE SUPER ON *.* FROM \'alexander\'@\'localhost\';\n\nRoles\n-----\n\nSyntax\n------\n\nREVOKE role [, role ...]\n FROM grantee [, grantee2 ... ]\n\nREVOKE ADMIN OPTION FOR role FROM grantee [, grantee2]\n\nDescription\n-----------\n\nREVOKE is also used to remove a role from a user or another role that it\'s\npreviously been assigned to. If a role has previously been set as a default\nrole, REVOKE does not remove the record of the default role from the\nmysql.user table. If the role is subsequently granted again, it will again be\nthe user\'s default. Use SET DEFAULT ROLE NONE to explicitly remove this.\n\nBefore MariaDB 10.1.13, the REVOKE role statement was not permitted in\nprepared statements.\n\nExample\n-------\n\nREVOKE journalist FROM hulda\n\nURL: https://mariadb.com/kb/en/revoke/','','https://mariadb.com/kb/en/revoke/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (110,10,'SET PASSWORD','Syntax\n------\n\nSET PASSWORD [FOR user] =\n {\n PASSWORD(\'some password\')\n | OLD_PASSWORD(\'some password\')\n | \'encrypted password\'\n }\n\nDescription\n-----------\n\nThe SET PASSWORD statement assigns a password to an existing MariaDB user\naccount.\n\nIf the password is specified using the PASSWORD() or OLD_PASSWORD() function,\nthe literal text of the password should be given. If the password is specified\nwithout using either function, the password should be the already-encrypted\npassword value as returned by PASSWORD().\n\nOLD_PASSWORD() should only be used if your MariaDB/MySQL clients are very old\n(< 4.0.0).\n\nWith no FOR clause, this statement sets the password for the current user. Any\nclient that has connected to the server using a non-anonymous account can\nchange the password for that account.\n\nWith a FOR clause, this statement sets the password for a specific account on\nthe current server host. Only clients that have the UPDATE privilege for the\nmysql database can do this. The user value should be given in\nuser_name@host_name format, where user_name and host_name are exactly as they\nare listed in the User and Host columns of the mysql.user table (or view in\nMariaDB-10.4 onwards) entry.\n\nThe argument to PASSWORD() and the password given to MariaDB clients can be of\narbitrary length.\n\nAuthentication Plugin Support\n-----------------------------\n\nMariaDB starting with 10.4\n--------------------------\nIn MariaDB 10.4 and later, SET PASSWORD (with or without PASSWORD()) works for\naccounts authenticated via any authentication plugin that supports passwords\nstored in the mysql.global_priv table.\n\nThe ed25519, mysql_native_password, and mysql_old_password authentication\nplugins store passwords in the mysql.global_priv table.\n\nIf you run SET PASSWORD on an account that authenticates with one of these\nauthentication plugins that stores passwords in the mysql.global_priv table,\nthen the PASSWORD() function is evaluated by the specific authentication\nplugin used by the account. The authentication plugin hashes the password with\na method that is compatible with that specific authentication plugin.\n\nThe unix_socket, named_pipe, gssapi, and pam authentication plugins do not\nstore passwords in the mysql.global_priv table. These authentication plugins\nrely on other methods to authenticate the user.\n\nIf you attempt to run SET PASSWORD on an account that authenticates with one\nof these authentication plugins that doesn\'t store a password in the\nmysql.global_priv table, then MariaDB Server will raise a warning like the\nfollowing:\n\nSET PASSWORD is ignored for users authenticating via unix_socket plugin\n\nSee Authentication from MariaDB 10.4 for an overview of authentication changes\nin MariaDB 10.4.\n\nMariaDB until 10.3\n------------------\nIn MariaDB 10.3 and before, SET PASSWORD (with or without PASSWORD()) only\nworks for accounts authenticated via mysql_native_password or\nmysql_old_password authentication plugins\n\nPasswordless User Accounts\n--------------------------\n\nUser accounts do not always require passwords to login.\n\nThe unix_socket , named_pipe and gssapi authentication plugins do not require\na password to authenticate the user.\n\nThe pam authentication plugin may or may not require a password to\nauthenticate the user, depending on the specific configuration.\n\nThe mysql_native_password and mysql_old_password authentication plugins\nrequire passwords for authentication, but the password can be blank. In that\ncase, no password is required.\n\nIf you provide a password while attempting to log into the server as an\naccount that doesn\'t require a password, then MariaDB server will simply\nignore the password.\n\nMariaDB starting with 10.4\n--------------------------\nIn MariaDB 10.4 and later, a user account can be defined to use multiple\nauthentication plugins in a specific order of preference. This specific\nscenario may be more noticeable in these versions, since an account could be\nassociated with some authentication plugins that require a password, and some\nthat do not.\n\nExample\n-------\n\nFor example, if you had an entry with User and Host column values of \'bob\' and\n\'%.loc.gov\', you would write the statement like this:\n\nSET PASSWORD FOR \'bob\'@\'%.loc.gov\' = PASSWORD(\'newpass\');\n\nIf you want to delete a password for a user, you would do:\n\nSET PASSWORD FOR \'bob\'@localhost = PASSWORD(\"\");\n\nURL: https://mariadb.com/kb/en/set-password/','','https://mariadb.com/kb/en/set-password/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (111,10,'CREATE ROLE','Syntax\n------\n\nCREATE [OR REPLACE] ROLE [IF NOT EXISTS] role \n [WITH ADMIN\n {CURRENT_USER | CURRENT_ROLE | user | role}]\n\nDescription\n-----------\n\nThe CREATE ROLE statement creates one or more MariaDB roles. To use it, you\nmust have the global CREATE USER privilege or the INSERT privilege for the\nmysql database. For each account, CREATE ROLE creates a new row in the\nmysql.user table that has no privileges, and with the corresponding is_role\nfield set to Y. It also creates a record in the mysql.roles_mapping table.\n\nIf any of the specified roles already exist, ERROR 1396 (HY000) results. If an\nerror occurs, CREATE ROLE will still create the roles that do not result in an\nerror. The maximum length for a role is 128 characters. Role names can be\nquoted, as explained in the Identifier names page. Only one error is produced\nfor all roles which have not been created:\n\nERROR 1396 (HY000): Operation CREATE ROLE failed for \'a\',\'b\',\'c\'\n\nFailed CREATE or DROP operations, for both users and roles, produce the same\nerror code.\n\nPUBLIC and NONE are reserved, and cannot be used as role names. NONE is used\nto unset a role and PUBLIC has a special use in other systems, such as Oracle,\nso is reserved for compatibility purposes.\n\nFor valid identifiers to use as role names, see Identifier Names.\n\nWITH ADMIN\n----------\n\nThe optional WITH ADMIN clause determines whether the current user, the\ncurrent role or another user or role has use of the newly created role. If the\nclause is omitted, WITH ADMIN CURRENT_USER is treated as the default, which\nmeans that the current user will be able to GRANT this role to users.\n\nOR REPLACE\n----------\n\nIf the optional OR REPLACE clause is used, it acts as a shortcut for:\n\nDROP ROLE IF EXISTS name;\nCREATE ROLE name ...;\n\nIF NOT EXISTS\n-------------\n\nWhen the IF NOT EXISTS clause is used, MariaDB will return a warning instead\nof an error if the specified role already exists. Cannot be used together with\nthe OR REPLACE clause.\n\nExamples\n--------\n\nCREATE ROLE journalist;\n\nCREATE ROLE developer WITH ADMIN lorinda@localhost;\n\nGranting the role to another user. Only user lorinda@localhost has permission\nto grant the developer role:\n\nSELECT USER();\n+-------------------+\n| USER() |\n+-------------------+\n| henning@localhost |\n+-------------------+\n...\nGRANT developer TO ian@localhost;\nAccess denied for user \'henning\'@\'localhost\'\n\nSELECT USER();\n+-------------------+\n| USER() |\n+-------------------+\n| lorinda@localhost |\n+-------------------+\n\nGRANT m_role TO ian@localhost;\n\nThe OR REPLACE and IF NOT EXISTS clauses. The journalist role already exists:\n\nCREATE ROLE journalist;\nERROR 1396 (HY000): Operation CREATE ROLE failed for \'journalist\'\n\nCREATE OR REPLACE ROLE journalist;\nQuery OK, 0 rows affected (0.00 sec)\n\nCREATE ROLE IF NOT EXISTS journalist;\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+---------------------------------------------------+\n| Level | Code | Message |\n+-------+------+---------------------------------------------------+\n| Note | 1975 | Can\'t create role \'journalist\'; it already exists |\n+-------+------+---------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/create-role/','','https://mariadb.com/kb/en/create-role/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (112,10,'DROP ROLE','Syntax\n------\n\nDROP ROLE [IF EXISTS] role_name [,role_name ...]\n\nDescription\n-----------\n\nThe DROP ROLE statement removes one or more MariaDB roles. To use this\nstatement, you must have the global CREATE USER privilege or the DELETE\nprivilege for the mysql database.\n\nDROP ROLE does not disable roles for connections which selected them with SET\nROLE. If a role has previously been set as a default role, DROP ROLE does not\nremove the record of the default role from the mysql.user table. If the role\nis subsequently recreated and granted, it will again be the user\'s default.\nUse SET DEFAULT ROLE NONE to explicitly remove this.\n\nIf any of the specified user accounts do not exist, ERROR 1396 (HY000)\nresults. If an error occurs, DROP ROLE will still drop the roles that do not\nresult in an error. Only one error is produced for all roles which have not\nbeen dropped:\n\nERROR 1396 (HY000): Operation DROP ROLE failed for \'a\',\'b\',\'c\'\n\nFailed CREATE or DROP operations, for both users and roles, produce the same\nerror code.\n\nIF EXISTS\n---------\n\nIf the IF EXISTS clause is used, MariaDB will return a warning instead of an\nerror if the role does not exist.\n\nExamples\n--------\n\nDROP ROLE journalist;\n\nThe same thing using the optional IF EXISTS clause:\n\nDROP ROLE journalist;\nERROR 1396 (HY000): Operation DROP ROLE failed for \'journalist\'\n\nDROP ROLE IF EXISTS journalist;\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nNote (Code 1975): Can\'t drop role \'journalist\'; it doesn\'t exist\n\nURL: https://mariadb.com/kb/en/drop-role/','','https://mariadb.com/kb/en/drop-role/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (113,10,'SET ROLE','Syntax\n------\n\nSET ROLE { role | NONE }\n\nDescription\n-----------\n\nThe SET ROLE statement enables a role, along with all of its associated\npermissions, for the current session. To unset a role, use NONE .\n\nIf a role that doesn\'t exist, or to which the user has not been assigned, is\nspecified, an ERROR 1959 (OP000): Invalid role specification error occurs.\n\nAn automatic SET ROLE is implicitly performed when a user connects if that\nuser has been assigned a default role. See SET DEFAULT ROLE.\n\nExample\n-------\n\nSELECT CURRENT_ROLE;\n+--------------+\n| CURRENT_ROLE |\n+--------------+\n| NULL |\n+--------------+\n\nSET ROLE staff;\n\nSELECT CURRENT_ROLE;\n+--------------+\n| CURRENT_ROLE |\n+--------------+\n| staff |\n+--------------+\n\nSET ROLE NONE;\n\nSELECT CURRENT_ROLE();\n+----------------+\n| CURRENT_ROLE() |\n+----------------+\n| NULL |\n+----------------+\n\nURL: https://mariadb.com/kb/en/set-role/','','https://mariadb.com/kb/en/set-role/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (114,10,'SET DEFAULT ROLE','Syntax\n------\n\nSET DEFAULT ROLE { role | NONE } [ FOR user@host ]\n\nDescription\n-----------\n\nThe SET DEFAULT ROLE statement sets a default role for a specified (or\ncurrent) user. A default role is automatically enabled when a user connects\n(an implicit SET ROLE statement is executed immediately after a connection is\nestablished).\n\nTo be able to set a role as a default, the role must already have been granted\nto that user, and one needs the privileges to enable this role (if you cannot\ndo SET ROLE X, you won\'t be able to do SET DEFAULT ROLE X). To set a default\nrole for another user one needs to have write access to the mysql database.\n\nTo remove a user\'s default role, use SET DEFAULT ROLE NONE [ FOR user@host ].\nThe record of the default role is not removed if the role is dropped or\nrevoked, so if the role is subsequently re-created or granted, it will again\nbe the user\'s default role.\n\nThe default role is stored in the default_role column in the mysql.user\ntable/view, as well as in the Information Schema APPLICABLE_ROLES table, so\nthese can be viewed to see which role has been assigned to a user as the\ndefault.\n\nExamples\n--------\n\nSetting a default role for the current user:\n\nSET DEFAULT ROLE journalist;\n\nRemoving a default role from the current user:\n\nSET DEFAULT ROLE NONE;\n\nSetting a default role for another user. The role has to have been granted to\nthe user before it can be set as default:\n\nCREATE ROLE journalist;\nCREATE USER taniel;\n\nSET DEFAULT ROLE journalist FOR taniel;\nERROR 1959 (OP000): Invalid role specification `journalist`\n\nGRANT journalist TO taniel;\nSET DEFAULT ROLE journalist FOR taniel;\n\nViewing mysql.user:\n\nselect * from mysql.user where user=\'taniel\'\\G\n*************************** 1. row ***************************\n Host: %\n User: taniel\n...\n is_role: N\n default_role: journalist\n...\n\nRemoving a default role for another user\n\nSET DEFAULT ROLE NONE FOR taniel;\n\nURL: https://mariadb.com/kb/en/set-default-role/','','https://mariadb.com/kb/en/set-default-role/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (115,10,'Roles Overview','Description\n-----------\n\nA role bundles a number of privileges together. It assists larger\norganizations where, typically, a number of users would have the same\nprivileges, and, previously, the only way to change the privileges for a group\nof users was by changing each user\'s privileges individually.\n\nAlternatively, multiple external users could have been assigned the same user,\nand there would have been no way to see which actual user was responsible for\nwhich action.\n\nWith roles, managing this is easy. For example, there could be a number of\nusers assigned to a journalist role, with identical privileges. Changing the\nprivileges for all the journalists is a matter of simply changing the role\'s\nprivileges, while the individual user is still linked with any changes that\ntake place.\n\nRoles are created with the CREATE ROLE statement, and dropped with the DROP\nROLE statement. Roles are then assigned to a user with an extension to the\nGRANT statement, while privileges are assigned to a role in the regular way\nwith GRANT. Similarly, the REVOKE statement can be used to both revoke a role\nfrom a user, or revoke a privilege from a role.\n\nOnce a user has connected, he can obtain all privileges associated with a role\nby setting a role with the SET ROLE statement. The CURRENT_ROLE function\nreturns the currently set role for the session, if any.\n\nOnly roles granted directly to a user can be set, roles granted to other roles\ncannot. Instead the privileges granted to a role, which is, in turn, granted\nto another role (grantee), will be immediately available to any user who sets\nthis second grantee role.\n\nThe SET DEFAULT ROLE statement allows one to set a default role for a user. A\ndefault role is automatically enabled when a user connects (an implicit SET\nROLE statement is executed immediately after a connection is established).\n\nRoles were implemented as a GSoC 2013 project by Vicentiu Ciorbaru.\n\nSystem Tables\n-------------\n\nInformation about roles and who they\'ve been granted to can be found in the\nInformation Schema APPLICABLE_ROLES table as well as the mysql.ROLES_MAPPING\ntable.\n\nThe Information Schema ENABLED_ROLES table shows the enabled roles for the\ncurrent session.\n\nExamples\n--------\n\nCreating a role and granting a privilege:\n\nCREATE ROLE journalist;\n\nGRANT SHOW DATABASES ON *.* TO journalist;\n\nGRANT journalist to hulda;\n\nNote, that hulda has no SHOW DATABASES privilege, even though she was granted\nthe journalist role. She needs to set the role first:\n\nSHOW DATABASES;\n+--------------------+\n| Database |\n+--------------------+\n| information_schema |\n+--------------------+\n\nSELECT CURRENT_ROLE;\n+--------------+\n| CURRENT_ROLE |\n+--------------+\n| NULL |\n+--------------+\n\nSET ROLE journalist;\n\nSELECT CURRENT_ROLE;\n+--------------+\n| CURRENT_ROLE |\n+--------------+\n| journalist |\n+--------------+\n\nSHOW DATABASES;\n+--------------------+\n| Database |\n+--------------------+\n| ... |\n| information_schema |\n| mysql |\n| performance_schema |\n| test |\n| ... |\n+--------------------+\n\nSET ROLE NONE;\n\nRoles can be granted to roles:\n\nCREATE ROLE writer;\n\nGRANT SELECT ON data.* TO writer;\n\nGRANT writer TO journalist;\n\nBut one does not need to set a role granted to a role. For example, hulda will\nautomatically get all writer privileges when she sets the journalist role:\n\nSELECT CURRENT_ROLE;\n+--------------+\n| CURRENT_ROLE |\n+--------------+\n| NULL |\n+--------------+\n\nSHOW TABLES FROM data;\nEmpty set (0.01 sec)\n\nSET ROLE journalist;\n\nSELECT CURRENT_ROLE;\n+--------------+\n| CURRENT_ROLE |\n+--------------+\n| journalist |\n+--------------+\n\nSHOW TABLES FROM data;\n+------------------------------+\n| Tables_in_data |\n+------------------------------+\n| set1 |\n| ... |\n+------------------------------+\n\nRoles and Views (and Stored Routines)\n-------------------------------------\n\nWhen a user sets a role, he, in a sense, has two identities with two\nassociated sets of privileges. But a view (or a stored routine) can have only\none definer. So, when a view (or a stored routine) is created with the SQL\nSECURITY DEFINER, one can specify whether the definer should be CURRENT_USER\n(and the view will have none of the privileges of the user\'s role) or\nCURRENT_ROLE (in this case, the view will use role\'s privileges, but none of\nthe user\'s privileges). As a result, sometimes one can create a view that is\nimpossible to use.\n\nCREATE ROLE r1;\n\nGRANT ALL ON db1.* TO r1;\n\nGRANT r1 TO foo@localhost;\n\nGRANT ALL ON db.* TO foo@localhost;\n\nSELECT CURRENT_USER\n+---------------+\n| current_user |\n+---------------+\n| foo@localhost |\n+---------------+\n\nSET ROLE r1;\n\nCREATE TABLE db1.t1 (i int);\n\nCREATE VIEW db.v1 AS SELECT * FROM db1.t1;\n\nSHOW CREATE VIEW db.v1;\n+------+-----------------------------------------------------------------------\n------------------------------------------------------------------+------------\n---------+----------------------+\n| View | Create View \n |\ncharacter_set_client | collation_connection |\n+------+-----------------------------------------------------------------------\n------------------------------------------------------------------+------------\n---------+----------------------+\n| v1 | CREATE ALGORITHM=UNDEFINED DEFINER=`foo`@`localhost` SQL SECURITY\nDEFINER VIEW `db`.`v1` AS SELECT `db1`.`t1`.`i` AS `i` from `db1`.`t1` | utf8 \n | utf8_general_ci |\n+------+-----------------------------------------------------------------------\n------------------------------------------------------------------+------------\n---------+----------------------+\n\nCREATE DEFINER=CURRENT_ROLE VIEW db.v2 AS SELECT * FROM db1.t1;\n\nSHOW CREATE VIEW db.b2;\n+------+-----------------------------------------------------------------------\n-----------------------------------------------------+----------------------+--\n-------------------+\n| View | Create View \n | character_set_client |\ncollation_connection |\n+------+-----------------------------------------------------------------------\n-----------------------------------------------------+----------------------+--\n-------------------+\n| v2 | CREATE ALGORITHM=UNDEFINED DEFINER=`r1` SQL SECURITY DEFINER VIEW\n`db`.`v2` AS select `db1`.`t1`.`a` AS `a` from `db1`.`t1` | utf8 \n | utf8_general_ci |\n+------+-----------------------------------------------------------------------\n-----------------------------------------------------+----------------------+--\n-------------------+\n\nOther Resources\n---------------\n\n* Roles Review by Peter Gulutzan\n\nURL: https://mariadb.com/kb/en/roles_overview/','','https://mariadb.com/kb/en/roles_overview/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (116,10,'Account Locking','MariaDB starting with 10.4.2\n----------------------------\nAccount locking was introduced in MariaDB 10.4.2.\n\nDescription\n-----------\n\nAccount locking permits privileged administrators to lock/unlock user\naccounts. No new client connections will be permitted if an account is locked\n(existing connections are not affected).\n\nUser accounts can be locked at creation, with the CREATE USER statement, or\nmodified after creation with the ALTER USER statement. For example:\n\nCREATE USER \'lorin\'@\'localhost\' ACCOUNT LOCK;\n\nor\n\nALTER USER \'marijn\'@\'localhost\' ACCOUNT LOCK;\n\nThe server will return an ER_ACCOUNT_HAS_BEEN_LOCKED error when locked users\nattempt to connect:\n\nmysql -ulorin\n ERROR 4151 (HY000): Access denied, this account is locked\n\nThe ALTER USER statement is also used to unlock a user:\n\nALTER USER \'lorin\'@\'localhost\' ACCOUNT UNLOCK;\n\nThe SHOW CREATE USER statement will show whether the account is locked:\n\nSHOW CREATE USER \'marijn\'@\'localhost\';\n+-----------------------------------------------+\n| CREATE USER for marijn@localhost |\n+-----------------------------------------------+\n| CREATE USER \'marijn\'@\'localhost\' ACCOUNT LOCK |\n+-----------------------------------------------+\n\nas well as querying the mysql.global_priv table:\n\nSELECT CONCAT(user, \'@\', host, \' => \', JSON_DETAILED(priv)) FROM\nmysql.global_priv \n WHERE user=\'marijn\';\n+------------------------------------------------------------------------------\n-------+\n| CONCAT(user, \'@\', host, \' => \', JSON_DETAILED(priv)) \n |\n+------------------------------------------------------------------------------\n-------+\n| marijn@localhost => {\n \"access\": 0,\n \"plugin\": \"mysql_native_password\",\n \"authentication_string\": \"\",\n \"account_locked\": true,\n \"password_last_changed\": 1558017158\n} |\n+------------------------------------------------------------------------------\n-------+\n\nURL: https://mariadb.com/kb/en/account-locking/','','https://mariadb.com/kb/en/account-locking/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (117,10,'Authentication from MariaDB 10.4','MariaDB starting with 10.4\n--------------------------\nMariaDB 10.4 introduced a number of changes to the authentication process,\nintended to make things easier and more intuitive.\n\nOverview\n--------\n\nThere are four new main features in 10.4 relating to authentication:\n\n* It is possible to use more than one authentication plugin for each user\naccount. For example, this can be useful to slowly migrate users to the more\nsecure ed25519 authentication plugin over time, while allowing the old\nmysql_native_password authentication plugin as an alternative for the\ntransitional period.\n* The root@localhost user account created by mysql_install_db is created with\nthe ability to use two authentication plugins.\nFirst, it is configured to try to use the unix_socket authentication plugin.\nThis allows the root@localhost user to login without a password via the local\nUnix socket file defined by the socket system variable, as long as the login\nis attempted from a process owned by the operating system root user account.\nSecond, if authentication fails with the unix_socket authentication plugin,\nthen it is configured to try to use the mysql_native_password authentication\nplugin. However, an invalid password is initially set, so in order to\nauthenticate this way, a password must be set with SET PASSWORD.\nHowever, just using the unix_socket authentication plugin may be fine for many\nusers, and it is very secure. You may want to try going without password\nauthentication to see how well it works for you. Remember, the best way to\nkeep your password safe is not to have one!\n\n* All user accounts, passwords, and global privileges are now stored in the\nmysql.global_priv table. The mysql.user table still exists and has exactly the\nsame set of columns as before, but it’s now a view that references the\nmysql.global_priv table. Tools that analyze the mysql.user table should\ncontinue to work as before. From MariaDB 10.4.13, the dedicated mariadb.sys\nuser is created as the definer of this view. Previously root was the definer,\nwhich resulted in privilege problems when this username was changed.\n* MariaDB 10.4 adds supports for User Password Expiry, which is not active by\ndefault.\n\nDescription\n-----------\n\nAs a result of the above changes, the open-for-everyone all-powerful root\naccount is finally gone. And installation scripts will no longer demand that\nyou \"PLEASE REMEMBER TO SET A PASSWORD FOR THE MariaDB root USER !\", because\nthe root account is securely created automatically.\n\nTwo all-powerful accounts are created by default — root and the OS user that\nowns the data directory, typically mysql. They are created as:\n\nCREATE USER root@localhost IDENTIFIED VIA unix_socket OR mysql_native_password\nUSING \'invalid\'\nCREATE USER mysql@localhost IDENTIFIED VIA unix_socket OR\nmysql_native_password USING \'invalid\'\n\nUsing unix_socket means that if you are the system root user, you can login as\nroot@locahost without a password. This technique was pioneered by Otto\nKekäläinen in Debian MariaDB packages and has been successfully used in Debian\nsince as early as MariaDB 10.0.\n\nIt is based on a simple fact that asking the system root for a password adds\nno extra security — root has full access to all the data files and all process\nmemory anyway. But not asking for a password means, there is no root password\nto forget (no need for the numerous tutorials on \"how to reset MariaDB root\npassword\"). And if you want to script some tedious database work, there is no\nneed to store the root password in plain text for the script to use (no need\nfor debian-sys-maint user).\n\nStill, some users may wish to log in as MariaDB root without using sudo. Hence\nthe old authentication method — conventional MariaDB password — is still\navailable. By default it is disabled (\"invalid\" is not a valid password hash),\nbut one can set the password with a usual SET PASSWORD statement. And still\nretain the password-less access via sudo.\n\nIf you install MariaDB locally (say from a tarball), you would not want to use\nsudo to be able to login. This is why MariaDB creates a second all-powerful\nuser with the same name as a system user that owns the data directory. In\nlocal (not system-wide) installations, this will be the user who installed\nMariaDB — they automatically get convenient password-less root-like access,\nbecause they can access all the data files anyway.\n\nEven if MariaDB is installed system-wide, you may not want to run your\ndatabase maintenance scripts as system root — now you can run them as system\nmysql user. And you will know that they will never destroy your entire system,\neven if you make a typo in a shell script.\n\nHowever, seasoned MariaDB DBAs who are used to the old ways do need to make\nsome changes. See the examples below for common tasks.\n\nCookbook\n--------\n\nAfter installing MariaDB system-wide the first thing you’ve got used to doing\nis logging in into the unprotected root account and protecting it, that is,\nsetting the root password:\n\n$ sudo dnf install MariaDB-server\n$ mysql -uroot\n...\nMariaDB> set password = password(\"XH4VmT3_jt\");\n\nThis is not only unnecessary now, it will simply not work — there is no\nunprotected root account. To login as root use\n\n$ sudo dnf install MariaDB-server\n$ sudo mysql\n\nNote that it implies you are connecting via the unix socket, not tcp. If you\nhappen to have protocol=tcp in a system-wide /etc/my.cnf file, use sudo mysql\n--protocol=socket.\n\nAfter installing MariaDB locally you’ve also used to connect to the\nunprotected root account using mysql -uroot. This will not work either, simply\nuse mysql without specifying a username.\n\nIf you\'ve forgotten your root password, no problem — you can still connect\nusing sudo and change the password. And if you\'ve also removed unix_socket\nauthentication, to restore access do as follows:\n\n* restart MariaDB with --skip-grant-tables\n* login into the unprotected server\n* run FLUSH PRIVILEGES (note, before 10.4 this would’ve been the last step,\nnot anymore). This disables --skip-grant-tables and allows you to change the\nstored authentication method\n* run SET PASSWORD FOR root@localhost to change the root password.\n\nTo view inside privilege tables, the old mysql.user table still exists. You\ncan select from it as before, although you cannot update it anymore. It\ndoesn’t show alternative authentication plugins and this was one of the\nreasons for switching to the mysql.global_priv table — complex authentication\nrules did not fit into rigid structure of a relational table. You can select\nfrom the new table, for example:\n\nselect concat(user, \'@\', host, \' => \', json_detailed(priv)) from\nmysql.global_priv;\n\nReverting to the Previous Authentication Method for root@localhost\n------------------------------------------------------------------\n\nIf you don\'t want the root@localhost user account created by mysql_install_db\nto use unix_socket authentication by default, then there are a few ways to\nrevert to the previous mysql_native_password authentication method for this\nuser account.\n\nConfiguring mysql_install_db to Revert to the Previous Authentication Method\n----------------------------------------------------------------------------\n\nOne way to revert to the previous mysql_native_password authentication method\nfor the root@localhost user account is to execute mysql_install_db with a\nspecial option. If mysql_install_db is executed while\n--auth-root-authentication-method=normal is specified, then it will create the\ndefault user accounts using the default behavior of MariaDB 10.3 and before.\n\nThis means that the root@localhost user account will use mysql_native_password\nauthentication by default. There are some other differences as well. See\nmysql_install_db: User Accounts Created by Default for more information.\n\nFor example, the option can be set on the command-line while running\nmysql_install_db:\n\nmysql_install_db --user=mysql --datadir=/var/lib/mysql\n--auth-root-authentication-method=normal\n\nThe option can also be set in an option file in an option group supported by\nmysql_install_db. For example:\n\n[mysql_install_db]\nauth_root_authentication_method=normal\n\nIf the option is set in an option file and if mysql_install_db is executed,\nthen mysql_install_db will read this option from the option file, and it will\nautomatically set this option.\n\nAltering the User Account to Revert to the Previous Authentication Method\n-------------------------------------------------------------------------\n\nIf you have already installed MariaDB, and if the root@localhost user account\nis already using unix_socket authentication, then you can revert to the old\nmysql_native_password authentication method for the user account by executing\nthe following:\n\nALTER USER root@localhost IDENTIFIED VIA mysql_native_password USING\nPASSWORD(\"verysecret\")\n\nURL: https://mariadb.com/kb/en/authentication-from-mariadb-104/','','https://mariadb.com/kb/en/authentication-from-mariadb-104/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (118,10,'User Password Expiry','MariaDB starting with 10.4.3\n----------------------------\nUser password expiry was introduced in MariaDB 10.4.3.\n\nPassword expiry permits administrators to expire user passwords, either\nmanually or automatically.\n\nSystem Variables\n----------------\n\nThere are two system variables which affect password expiry:\ndefault_password_lifetime, which determines the amount of time between\nrequiring the user to change their password. 0, the default, means automatic\npassword expiry is not active.\n\nThe second variable, disconnect_on_expired_password determines whether a\nclient is permitted to connect if their password has expired, or whether they\nare permitted to connect in sandbox mode, able to perform a limited subset of\nqueries related to resetting the password, in particular SET PASSWORD and SET.\n\nSetting a Password Expiry Limit for a User\n------------------------------------------\n\nBesides automatic password expiry, as determined by default_password_lifetime,\npassword expiry times can be set on an individual user basis, overriding the\nglobal using the CREATE USER or ALTER USER statements, for example:\n\nCREATE USER \'monty\'@\'localhost\' PASSWORD EXPIRE INTERVAL 120 DAY;\n\nALTER USER \'monty\'@\'localhost\' PASSWORD EXPIRE INTERVAL 120 DAY;\n\nLimits can be disabled by use of the NEVER keyword, for example:\n\nCREATE USER \'monty\'@\'localhost\' PASSWORD EXPIRE NEVER;\n\nALTER USER \'monty\'@\'localhost\' PASSWORD EXPIRE NEVER;\n\nA manually set limit can be restored the system default by use of DEFAULT, for\nexample:\n\nCREATE USER \'monty\'@\'localhost\' PASSWORD EXPIRE DEFAULT;\n\nALTER USER \'monty\'@\'localhost\' PASSWORD EXPIRE DEFAULT;\n\nSHOW CREATE USER\n----------------\n\nThe SHOW CREATE USER statement will display information about the password\nexpiry status of the user. Unlike MySQL, it will not display if the user is\nunlocked, or if the password expiry is set to default.\n\nCREATE USER \'monty\'@\'localhost\' PASSWORD EXPIRE INTERVAL 120 DAY;\nCREATE USER \'konstantin\'@\'localhost\' PASSWORD EXPIRE NEVER;\nCREATE USER \'amse\'@\'localhost\' PASSWORD EXPIRE DEFAULT;\n\nSHOW CREATE USER \'monty\'@\'localhost\';\n+------------------------------------------------------------------+\n| CREATE USER for monty@localhost |\n+------------------------------------------------------------------+\n| CREATE USER \'monty\'@\'localhost\' PASSWORD EXPIRE INTERVAL 120 DAY |\n+------------------------------------------------------------------+\n\nSHOW CREATE USER \'konstantin\'@\'localhost\';\n+------------------------------------------------------------+\n| CREATE USER for konstantin@localhost |\n+------------------------------------------------------------+\n| CREATE USER \'konstantin\'@\'localhost\' PASSWORD EXPIRE NEVER |\n+------------------------------------------------------------+\n\nSHOW CREATE USER \'amse\'@\'localhost\';\n+--------------------------------+\n| CREATE USER for amse@localhost |\n+--------------------------------+\n| CREATE USER \'amse\'@\'localhost\' |\n+--------------------------------+\n\nChecking When Passwords Expire\n------------------------------\n\nThe following query can be used to check when the current passwords expire for\nall users:\n\nWITH password_expiration_info AS (\n SELECT User, Host,\n IF(\n IFNULL(JSON_EXTRACT(Priv, \'$.password_lifetime\'), -1) = -1,\n @@global.default_password_lifetime,\n JSON_EXTRACT(Priv, \'$.password_lifetime\')\n ) AS password_lifetime,\n JSON_EXTRACT(Priv, \'$.password_last_changed\') AS password_last_changed\n FROM mysql.global_priv\n)\nSELECT pei.User, pei.Host,\n pei.password_lifetime,\n FROM_UNIXTIME(pei.password_last_changed) AS password_last_changed_datetime,\n FROM_UNIXTIME(\n pei.password_last_changed +\n (pei.password_lifetime * 60 * 60 * 24)\n ) AS password_expiration_datetime\n FROM password_expiration_info pei\n WHERE pei.password_lifetime != 0\n AND pei.password_last_changed IS NOT NULL\nUNION\nSELECT pei.User, pei.Host,\n pei.password_lifetime,\n FROM_UNIXTIME(pei.password_last_changed) AS password_last_changed_datetime,\n 0 AS password_expiration_datetime\n FROM password_expiration_info pei\n WHERE pei.password_lifetime = 0\n OR pei.password_last_changed IS NULL;\n\n--connect-expired-password Client Option\n----------------------------------------\n\nThe mysql client --connect-expired-password option notifies the server that\nthe client is prepared to handle expired password sandbox mode (even if the\n--batch option was specified).\n\nURL: https://mariadb.com/kb/en/user-password-expiry/','','https://mariadb.com/kb/en/user-password-expiry/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (119,11,'ST_X','Syntax\n------\n\nST_X(p)\nX(p)\n\nDescription\n-----------\n\nReturns the X-coordinate value for the point p as a double-precision number.\n\nST_X() and X() are synonyms.\n\nExamples\n--------\n\nSET @pt = \'Point(56.7 53.34)\';\n\nSELECT X(GeomFromText(@pt));\n+----------------------+\n| X(GeomFromText(@pt)) |\n+----------------------+\n| 56.7 |\n+----------------------+\n\nURL: https://mariadb.com/kb/en/st_x/','','https://mariadb.com/kb/en/st_x/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (120,11,'ST_Y','Syntax\n------\n\nST_Y(p)\nY(p)\n\nDescription\n-----------\n\nReturns the Y-coordinate value for the point p as a double-precision number.\n\nST_Y() and Y() are synonyms.\n\nExamples\n--------\n\nSET @pt = \'Point(56.7 53.34)\';\n\nSELECT Y(GeomFromText(@pt));\n+----------------------+\n| Y(GeomFromText(@pt)) |\n+----------------------+\n| 53.34 |\n+----------------------+\n\nURL: https://mariadb.com/kb/en/st_y/','','https://mariadb.com/kb/en/st_y/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (121,11,'X','A synonym for ST_X.\n\nURL: https://mariadb.com/kb/en/point-properties-x/','','https://mariadb.com/kb/en/point-properties-x/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (122,11,'Y','A synonym for ST_Y.\n\nURL: https://mariadb.com/kb/en/point-properties-y/','','https://mariadb.com/kb/en/point-properties-y/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (123,12,'UNCOMPRESS','Syntax\n------\n\nUNCOMPRESS(string_to_uncompress)\n\nDescription\n-----------\n\nUncompresses a string compressed by the COMPRESS() function. If the argument\nis not a compressed value, the result is NULL. This function requires MariaDB\nto have been compiled with a compression library such as zlib. Otherwise, the\nreturn value is always NULL. The have_compress server system variable\nindicates whether a compression library is present.\n\nExamples\n--------\n\nSELECT UNCOMPRESS(COMPRESS(\'a string\'));\n+----------------------------------+\n| UNCOMPRESS(COMPRESS(\'a string\')) |\n+----------------------------------+\n| a string |\n+----------------------------------+\n\nSELECT UNCOMPRESS(\'a string\');\n+------------------------+\n| UNCOMPRESS(\'a string\') |\n+------------------------+\n| NULL |\n+------------------------+\n\nURL: https://mariadb.com/kb/en/uncompress/','','https://mariadb.com/kb/en/uncompress/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (124,12,'DECODE','Syntax\n------\n\nDECODE(crypt_str,pass_str)\n\nIn Oracle mode from MariaDB 10.3.2:\n\nDECODE(expr, search_expr, result_expr [, search_expr2, result_expr2 ...]\n[default_expr])\n\nIn all modes from MariaDB 10.3.2:\n\nDECODE_ORACLE(expr, search_expr, result_expr [, search_expr2, result_expr2\n...] [default_expr])\n\nDescription\n-----------\n\nIn the default mode, DECODE decrypts the encrypted string crypt_str using\npass_str as the password. crypt_str should be a string returned from ENCODE().\nThe resulting string will be the original string only if pass_str is the same.\n\nIn Oracle mode from MariaDB 10.3.2, DECODE compares expr to the search\nexpressions, in order. If it finds a match, the corresponding result\nexpression is returned. If no matches are found, the default expression is\nreturned, or NULL if no default is provided.\n\nNULLs are treated as equivalent.\n\nDECODE_ORACLE is a synonym for the Oracle-mode version of the function, and is\navailable in all modes.\n\nExamples\n--------\n\nFrom MariaDB 10.3.2:\n\nSELECT DECODE_ORACLE(2+1,3*1,\'found1\',3*2,\'found2\',\'default\');\n+--------------------------------------------------------+\n| DECODE_ORACLE(2+1,3*1,\'found1\',3*2,\'found2\',\'default\') |\n+--------------------------------------------------------+\n| found1 |\n+--------------------------------------------------------+\n\nSELECT DECODE_ORACLE(2+4,3*1,\'found1\',3*2,\'found2\',\'default\');\n+--------------------------------------------------------+\n| DECODE_ORACLE(2+4,3*1,\'found1\',3*2,\'found2\',\'default\') |\n+--------------------------------------------------------+\n| found2 |\n+--------------------------------------------------------+\n\nSELECT DECODE_ORACLE(2+2,3*1,\'found1\',3*2,\'found2\',\'default\');\n+--------------------------------------------------------+\n| DECODE_ORACLE(2+2,3*1,\'found1\',3*2,\'found2\',\'default\') |\n+--------------------------------------------------------+\n| default |\n+--------------------------------------------------------+\n\nNulls are treated as equivalent:\n\nSELECT DECODE_ORACLE(NULL,NULL,\'Nulls are equivalent\',\'Nulls are not\nequivalent\');\n+----------------------------------------------------------------------------+\n| DECODE_ORACLE(NULL,NULL,\'Nulls are equivalent\',\'Nulls are not equivalent\') |\n+----------------------------------------------------------------------------+\n| Nulls are equivalent |\n+----------------------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/decode/','','https://mariadb.com/kb/en/decode/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (125,12,'DECODE_ORACLE','MariaDB starting with 10.3.2\n----------------------------\nDECODE_ORACLE is a synonym for the Oracle mode version of the DECODE function,\nand is available in all modes.\n\nURL: https://mariadb.com/kb/en/decode_oracle/','','https://mariadb.com/kb/en/decode_oracle/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (126,12,'AES_DECRYPT','Syntax\n------\n\nAES_DECRYPT(crypt_str,key_str)\n\nDescription\n-----------\n\nThis function allows decryption of data using the official AES (Advanced\nEncryption Standard) algorithm. For more information, see the description of\nAES_ENCRYPT().\n\nURL: https://mariadb.com/kb/en/aes_decrypt/','','https://mariadb.com/kb/en/aes_decrypt/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (127,12,'AES_ENCRYPT','Syntax\n------\n\nAES_ENCRYPT(str,key_str)\n\nDescription\n-----------\n\nAES_ENCRYPT() and AES_DECRYPT() allow encryption and decryption of data using\nthe official AES (Advanced Encryption Standard) algorithm, previously known as\n\"Rijndael.\" Encoding with a 128-bit key length is used, but you can extend it\nup to 256 bits by modifying the source. We chose 128 bits because it is much\nfaster and it is secure enough for most purposes.\n\nAES_ENCRYPT() encrypts a string str using the key key_str, and returns a\nbinary string.\n\nAES_DECRYPT() decrypts the encrypted string and returns the original string.\n\nThe input arguments may be any length. If either argument is NULL, the result\nof this function is also NULL.\n\nBecause AES is a block-level algorithm, padding is used to encode uneven\nlength strings and so the result string length may be calculated using this\nformula:\n\n16 x (trunc(string_length / 16) + 1)\n\nIf AES_DECRYPT() detects invalid data or incorrect padding, it returns NULL.\nHowever, it is possible for AES_DECRYPT() to return a non-NULL value (possibly\ngarbage) if the input data or the key is invalid.\n\nExamples\n--------\n\nINSERT INTO t VALUES (AES_ENCRYPT(\'text\',SHA2(\'password\',512)));\n\nURL: https://mariadb.com/kb/en/aes_encrypt/','','https://mariadb.com/kb/en/aes_encrypt/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (128,12,'COMPRESS','Syntax\n------\n\nCOMPRESS(string_to_compress)\n\nDescription\n-----------\n\nCompresses a string and returns the result as a binary string. This function\nrequires MariaDB to have been compiled with a compression library such as\nzlib. Otherwise, the return value is always NULL. The compressed string can be\nuncompressed with UNCOMPRESS().\n\nThe have_compress server system variable indicates whether a compression\nlibrary is present.\n\nExamples\n--------\n\nSELECT LENGTH(COMPRESS(REPEAT(\'a\',1000)));\n+------------------------------------+\n| LENGTH(COMPRESS(REPEAT(\'a\',1000))) |\n+------------------------------------+\n| 21 |\n+------------------------------------+\n\nSELECT LENGTH(COMPRESS(\'\'));\n+----------------------+\n| LENGTH(COMPRESS(\'\')) |\n+----------------------+\n| 0 |\n+----------------------+\n\nSELECT LENGTH(COMPRESS(\'a\'));\n+-----------------------+\n| LENGTH(COMPRESS(\'a\')) |\n+-----------------------+\n| 13 |\n+-----------------------+\n\nSELECT LENGTH(COMPRESS(REPEAT(\'a\',16)));\n+----------------------------------+\n| LENGTH(COMPRESS(REPEAT(\'a\',16))) |\n+----------------------------------+\n| 15 |\n+----------------------------------+\n\nURL: https://mariadb.com/kb/en/compress/','','https://mariadb.com/kb/en/compress/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (129,12,'DES_DECRYPT','DES_DECRYPT has been deprecated from MariaDB 10.10.0, and will be removed in a\nfuture release.\n\nSyntax\n------\n\nDES_DECRYPT(crypt_str[,key_str])\n\nDescription\n-----------\n\nDecrypts a string encrypted with DES_ENCRYPT(). If an error occurs, this\nfunction returns NULL.\n\nThis function works only if MariaDB has been configured with TLS support.\n\nIf no key_str argument is given, DES_DECRYPT() examines the first byte of the\nencrypted string to determine the DES key number that was used to encrypt the\noriginal string, and then reads the key from the DES key file to decrypt the\nmessage. For this to work, the user must have the SUPER privilege. The key\nfile can be specified with the --des-key-file server option.\n\nIf you pass this function a key_str argument, that string is used as the key\nfor decrypting the message.\n\nIf the crypt_str argument does not appear to be an encrypted string, MariaDB\nreturns the given crypt_str.\n\nURL: https://mariadb.com/kb/en/des_decrypt/','','https://mariadb.com/kb/en/des_decrypt/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (130,12,'DES_ENCRYPT','DES_ENCRYPT has been deprecated from MariaDB 10.10.0, and will be removed in a\nfuture release.\n\nSyntax\n------\n\nDES_ENCRYPT(str[,{key_num|key_str}])\n\nDescription\n-----------\n\nEncrypts the string with the given key using the Triple-DES algorithm.\n\nThis function works only if MariaDB has been configured with TLS support.\n\nThe encryption key to use is chosen based on the second argument to\nDES_ENCRYPT(), if one was given. With no argument, the first key from the DES\nkey file is used. With a key_num argument, the given key number (0-9) from the\nDES key file is used. With a key_str argument, the given key string is used to\nencrypt str.\n\nThe key file can be specified with the --des-key-file server option.\n\nThe return string is a binary string where the first character is CHAR(128 |\nkey_num). If an error occurs, DES_ENCRYPT() returns NULL.\n\nThe 128 is added to make it easier to recognize an encrypted key. If you use a\nstring key, key_num is 127.\n\nThe string length for the result is given by this formula:\n\nnew_len = orig_len + (8 - (orig_len % 8)) + 1\n\nEach line in the DES key file has the following format:\n\nkey_num des_key_str\n\nEach key_num value must be a number in the range from 0 to 9. Lines in the\nfile may be in any order. des_key_str is the string that is used to encrypt\nthe message. There should be at least one space between the number and the\nkey. The first key is the default key that is used if you do not specify any\nkey argument to DES_ENCRYPT().\n\nYou can tell MariaDB to read new key values from the key file with the FLUSH\nDES_KEY_FILE statement. This requires the RELOAD privilege.\n\nOne benefit of having a set of default keys is that it gives applications a\nway to check for the existence of encrypted column values, without giving the\nend user the right to decrypt those values.\n\nExamples\n--------\n\nSELECT customer_address FROM customer_table \n WHERE crypted_credit_card = DES_ENCRYPT(\'credit_card_number\');\n\nURL: https://mariadb.com/kb/en/des_encrypt/','','https://mariadb.com/kb/en/des_encrypt/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (131,12,'ENCODE','Syntax\n------\n\nENCODE(str,pass_str)\n\nDescription\n-----------\n\nENCODE is not considered cryptographically secure, and should not be used for\npassword encryption.\n\nEncrypt str using pass_str as the password. To decrypt the result, use\nDECODE().\n\nThe result is a binary string of the same length as str.\n\nThe strength of the encryption is based on how good the random generator is.\n\nIt is not recommended to rely on the encryption performed by the ENCODE\nfunction. Using a salt value (changed when a password is updated) will improve\nmatters somewhat, but for storing passwords, consider a more cryptographically\nsecure function, such as SHA2().\n\nExamples\n--------\n\nENCODE(\'not so secret text\', CONCAT(\'random_salt\',\'password\'))\n\nURL: https://mariadb.com/kb/en/encode/','','https://mariadb.com/kb/en/encode/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (132,12,'ENCRYPT','Syntax\n------\n\nENCRYPT(str[,salt])\n\nDescription\n-----------\n\nEncrypts a string using the Unix crypt() system call, returning an encrypted\nbinary string. The salt argument should be a string with at least two\ncharacters or the returned result will be NULL. If no salt argument is given,\na random value of sufficient length is used.\n\nIt is not recommended to use ENCRYPT() with utf16, utf32 or ucs2 multi-byte\ncharacter sets because the crypt() system call expects a string terminated\nwith a zero byte.\n\nNote that the underlying crypt() system call may have some limitations, such\nas ignoring all but the first eight characters.\n\nIf the have_crypt system variable is set to NO (because the crypt() system\ncall is not available), the ENCRYPT function will always return NULL.\n\nExamples\n--------\n\nSELECT ENCRYPT(\'encrypt me\');\n+-----------------------+\n| ENCRYPT(\'encrypt me\') |\n+-----------------------+\n| 4I5BsEx0lqTDk |\n+-----------------------+\n\nURL: https://mariadb.com/kb/en/encrypt/','','https://mariadb.com/kb/en/encrypt/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (133,12,'MD5','Syntax\n------\n\nMD5(str)\n\nDescription\n-----------\n\nCalculates an MD5 128-bit checksum for the string.\n\nThe return value is a 32-hex digit string, and as of MariaDB 5.5, is a\nnonbinary string in the connection character set and collation, determined by\nthe values of the character_set_connection and collation_connection system\nvariables. Before 5.5, the return value was a binary string.\n\nNULL is returned if the argument was NULL.\n\nExamples\n--------\n\nSELECT MD5(\'testing\');\n+----------------------------------+\n| MD5(\'testing\') |\n+----------------------------------+\n| ae2b1fca515949e5d54fb22b8ed95575 |\n+----------------------------------+\n\nURL: https://mariadb.com/kb/en/md5/','','https://mariadb.com/kb/en/md5/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (134,12,'OLD_PASSWORD','Syntax\n------\n\nOLD_PASSWORD(str)\n\nDescription\n-----------\n\nOLD_PASSWORD() was added to MySQL when the implementation of PASSWORD() was\nchanged to improve security. OLD_PASSWORD() returns the value of the old\n(pre-MySQL 4.1) implementation of PASSWORD() as a string, and is intended to\npermit you to reset passwords for any pre-4.1 clients that need to connect to\na more recent MySQL server version, or any version of MariaDB, without locking\nthem out.\n\nAs of MariaDB 5.5, the return value is a nonbinary string in the connection\ncharacter set and collation, determined by the values of the\ncharacter_set_connection and collation_connection system variables. Before\n5.5, the return value was a binary string.\n\nThe return value is 16 bytes in length, or NULL if the argument was NULL.\n\nURL: https://mariadb.com/kb/en/old_password/','','https://mariadb.com/kb/en/old_password/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (135,12,'PASSWORD','Syntax\n------\n\nPASSWORD(str)\n\nDescription\n-----------\n\nThe PASSWORD() function is used for hashing passwords for use in\nauthentication by the MariaDB server. It is not intended for use in other\napplications.\n\nCalculates and returns a hashed password string from the plaintext password\nstr. Returns an empty string (>= MariaDB 10.0.4) if the argument was NULL.\n\nThe return value is a nonbinary string in the connection character set and\ncollation, determined by the values of the character_set_connection and\ncollation_connection system variables.\n\nThis is the function that is used for hashing MariaDB passwords for storage in\nthe Password column of the user table (see privileges), usually used with the\nSET PASSWORD statement. It is not intended for use in other applications.\n\nUntil MariaDB 10.3, the return value is 41-bytes in length, and the first\ncharacter is always \'*\'. From MariaDB 10.4, the function takes into account\nthe authentication plugin where applicable (A CREATE USER or SET PASSWORD\nstatement). For example, when used in conjunction with a user authenticated by\nthe ed25519 plugin, the statement will create a longer hash:\n\nCREATE USER edtest@localhost IDENTIFIED VIA ed25519 USING PASSWORD(\'secret\');\n\nCREATE USER edtest2@localhost IDENTIFIED BY \'secret\';\n\nSELECT CONCAT(user, \'@\', host, \' => \', JSON_DETAILED(priv)) FROM\nmysql.global_priv\n WHERE user LIKE \'edtest%\'\\G\n*************************** 1. row ***************************\nCONCAT(user, \'@\', host, \' => \', JSON_DETAILED(priv)): edtest@localhost => {\n...\n \"plugin\": \"ed25519\",\n \"authentication_string\": \"ZIgUREUg5PVgQ6LskhXmO+eZLS0nC8be6HPjYWR4YJY\",\n...\n}\n*************************** 2. row ***************************\nCONCAT(user, \'@\', host, \' => \', JSON_DETAILED(priv)): edtest2@localhost => {\n...\n \"plugin\": \"mysql_native_password\",\n \"authentication_string\": \"*14E65567ABDB5135D0CFD9A70B3032C179A49EE7\",\n...\n}\n\nThe behavior of this function is affected by the value of the old_passwords\nsystem variable. If this is set to 1 (0 is default), MariaDB reverts to using\nthe mysql_old_password authentication plugin by default for newly created\nusers and passwords.\n\nExamples\n--------\n\nSELECT PASSWORD(\'notagoodpwd\');\n+-------------------------------------------+\n| PASSWORD(\'notagoodpwd\') |\n+-------------------------------------------+\n| *3A70EE9FC6594F88CE9E959CD51C5A1C002DC937 |\n+-------------------------------------------+\n\nSET PASSWORD FOR \'bob\'@\'%.loc.gov\' = PASSWORD(\'newpass\');\n\nURL: https://mariadb.com/kb/en/password/','','https://mariadb.com/kb/en/password/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (136,12,'SHA1','Syntax\n------\n\nSHA1(str), SHA(str)\n\nDescription\n-----------\n\nCalculates an SHA-1 160-bit checksum for the string str, as described in RFC\n3174 (Secure Hash Algorithm).\n\nThe value is returned as a string of 40 hex digits, or NULL if the argument\nwas NULL. As of MariaDB 5.5, the return value is a nonbinary string in the\nconnection character set and collation, determined by the values of the\ncharacter_set_connection and collation_connection system variables. Before\n5.5, the return value was a binary string.\n\nExamples\n--------\n\nSELECT SHA1(\'some boring text\');\n+------------------------------------------+\n| SHA1(\'some boring text\') |\n+------------------------------------------+\n| af969fc2085b1bb6d31e517d5c456def5cdd7093 |\n+------------------------------------------+\n\nURL: https://mariadb.com/kb/en/sha1/','','https://mariadb.com/kb/en/sha1/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (137,12,'SHA2','Syntax\n------\n\nSHA2(str,hash_len)\n\nDescription\n-----------\n\nGiven a string str, calculates an SHA-2 checksum, which is considered more\ncryptographically secure than its SHA-1 equivalent. The SHA-2 family includes\nSHA-224, SHA-256, SHA-384, and SHA-512, and the hash_len must correspond to\none of these, i.e. 224, 256, 384 or 512. 0 is equivalent to 256.\n\nThe return value is a nonbinary string in the connection character set and\ncollation, determined by the values of the character_set_connection and\ncollation_connection system variables.\n\nNULL is returned if the hash length is not valid, or the string str is NULL.\n\nSHA2 will only work if MariaDB was has been configured with TLS support.\n\nExamples\n--------\n\nSELECT SHA2(\'Maria\',224);\n+----------------------------------------------------------+\n| SHA2(\'Maria\',224) |\n+----------------------------------------------------------+\n| 6cc67add32286412efcab9d0e1675a43a5c2ef3cec8879f81516ff83 |\n+----------------------------------------------------------+\n\nSELECT SHA2(\'Maria\',256);\n+------------------------------------------------------------------+\n| SHA2(\'Maria\',256) |\n+------------------------------------------------------------------+\n| 9ff18ebe7449349f358e3af0b57cf7a032c1c6b2272cb2656ff85eb112232f16 |\n+------------------------------------------------------------------+\n\nSELECT SHA2(\'Maria\',0);\n+------------------------------------------------------------------+\n| SHA2(\'Maria\',0) |\n+------------------------------------------------------------------+\n| 9ff18ebe7449349f358e3af0b57cf7a032c1c6b2272cb2656ff85eb112232f16 |\n+------------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/sha2/','','https://mariadb.com/kb/en/sha2/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (138,13,'ENDPOINT','A synonym for ST_ENDPOINT.\n\nURL: https://mariadb.com/kb/en/linestring-properties-endpoint/','','https://mariadb.com/kb/en/linestring-properties-endpoint/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (139,13,'GLENGTH','Syntax\n------\n\nGLength(ls)\n\nDescription\n-----------\n\nReturns as a double-precision number the length of the LineString value ls in\nits associated spatial reference.\n\nExamples\n--------\n\nSET @ls = \'LineString(1 1,2 2,3 3)\';\n\nSELECT GLength(GeomFromText(@ls));\n+----------------------------+\n| GLength(GeomFromText(@ls)) |\n+----------------------------+\n| 2.82842712474619 |\n+----------------------------+\n\nURL: https://mariadb.com/kb/en/glength/','','https://mariadb.com/kb/en/glength/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (140,13,'NumPoints','A synonym for ST_NumPoints.\n\nURL: https://mariadb.com/kb/en/linestring-properties-numpoints/','','https://mariadb.com/kb/en/linestring-properties-numpoints/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (141,13,'PointN','A synonym for ST_PointN.\n\nURL: https://mariadb.com/kb/en/linestring-properties-pointn/','','https://mariadb.com/kb/en/linestring-properties-pointn/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (142,13,'STARTPOINT','A synonym for ST_STARTPOINT.\n\nURL: https://mariadb.com/kb/en/linestring-properties-startpoint/','','https://mariadb.com/kb/en/linestring-properties-startpoint/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (143,13,'ST_ENDPOINT','Syntax\n------\n\nST_EndPoint(ls)\nEndPoint(ls)\n\nDescription\n-----------\n\nReturns the Point that is the endpoint of the LineString value ls.\n\nST_EndPoint() and EndPoint() are synonyms.\n\nExamples\n--------\n\nSET @ls = \'LineString(1 1,2 2,3 3)\';\n\nSELECT AsText(EndPoint(GeomFromText(@ls)));\n+-------------------------------------+\n| AsText(EndPoint(GeomFromText(@ls))) |\n+-------------------------------------+\n| POINT(3 3) |\n+-------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_endpoint/','','https://mariadb.com/kb/en/st_endpoint/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (144,13,'ST_NUMPOINTS','Syntax\n------\n\nST_NumPoints(ls)\nNumPoints(ls)\n\nDescription\n-----------\n\nReturns the number of Point objects in the LineString value ls.\n\nST_NumPoints() and NumPoints() are synonyms.\n\nExamples\n--------\n\nSET @ls = \'LineString(1 1,2 2,3 3)\';\n\nSELECT NumPoints(GeomFromText(@ls));\n+------------------------------+\n| NumPoints(GeomFromText(@ls)) |\n+------------------------------+\n| 3 |\n+------------------------------+\n\nURL: https://mariadb.com/kb/en/st_numpoints/','','https://mariadb.com/kb/en/st_numpoints/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (145,13,'ST_POINTN','Syntax\n------\n\nST_PointN(ls,N)\nPointN(ls,N)\n\nDescription\n-----------\n\nReturns the N-th Point in the LineString value ls. Points are numbered\nbeginning with 1.\n\nST_PointN() and PointN() are synonyms.\n\nExamples\n--------\n\nSET @ls = \'LineString(1 1,2 2,3 3)\';\n\nSELECT AsText(PointN(GeomFromText(@ls),2));\n+-------------------------------------+\n| AsText(PointN(GeomFromText(@ls),2)) |\n+-------------------------------------+\n| POINT(2 2) |\n+-------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_pointn/','','https://mariadb.com/kb/en/st_pointn/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (146,13,'ST_STARTPOINT','Syntax\n------\n\nST_StartPoint(ls)\nStartPoint(ls)\n\nDescription\n-----------\n\nReturns the Point that is the start point of the LineString value ls.\n\nST_StartPoint() and StartPoint() are synonyms.\n\nExamples\n--------\n\nSET @ls = \'LineString(1 1,2 2,3 3)\';\n\nSELECT AsText(StartPoint(GeomFromText(@ls)));\n+---------------------------------------+\n| AsText(StartPoint(GeomFromText(@ls))) |\n+---------------------------------------+\n| POINT(1 1) |\n+---------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_startpoint/','','https://mariadb.com/kb/en/st_startpoint/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (147,14,'GET_LOCK','Syntax\n------\n\nGET_LOCK(str,timeout)\n\nDescription\n-----------\n\nTries to obtain a lock with a name given by the string str, using a timeout of\ntimeout seconds. Returns 1 if the lock was obtained successfully, 0 if the\nattempt timed out (for example, because another client has previously locked\nthe name), or NULL if an error occurred (such as running out of memory or the\nthread was killed with mysqladmin kill).\n\nA lock is released with RELEASE_LOCK(), when the connection terminates (either\nnormally or abnormally). A connection can hold multiple locks at the same\ntime, so a lock that is no longer needed needs to be explicitly released.\n\nThe IS_FREE_LOCK function returns whether a specified lock a free or not, and\nthe IS_USED_LOCK whether the function is in use or not.\n\nLocks obtained with GET_LOCK() do not interact with transactions. That is,\ncommitting a transaction does not release any such locks obtained during the\ntransaction.\n\nIt is also possible to recursively set the same lock. If a lock with the same\nname is set n times, it needs to be released n times as well.\n\nstr is case insensitive for GET_LOCK() and related functions. If str is an\nempty string or NULL, GET_LOCK() returns NULL and does nothing. From MariaDB\n10.2.2, timeout supports microseconds. Before then, it was rounded to the\nclosest integer.\n\nIf the metadata_lock_info plugin is installed, locks acquired with this\nfunction are visible in the Information Schema METADATA_LOCK_INFO table.\n\nThis function can be used to implement application locks or to simulate record\nlocks. Names are locked on a server-wide basis. If a name has been locked by\none client, GET_LOCK() blocks any request by another client for a lock with\nthe same name. This allows clients that agree on a given lock name to use the\nname to perform cooperative advisory locking. But be aware that it also allows\na client that is not among the set of cooperating clients to lock a name,\neither inadvertently or deliberately, and thus prevent any of the cooperating\nclients from locking that name. One way to reduce the likelihood of this is to\nuse lock names that are database-specific or application-specific. For\nexample, use lock names of the form db_name.str or app_name.str.\n\nStatements using the GET_LOCK function are not safe for statement-based\nreplication.\n\nThe patch to permit multiple locks was contributed by Konstantin \"Kostja\"\nOsipov (MDEV-3917).\n\nExamples\n--------\n\nSELECT GET_LOCK(\'lock1\',10);\n+----------------------+\n| GET_LOCK(\'lock1\',10) |\n+----------------------+\n| 1 |\n+----------------------+\n\nSELECT IS_FREE_LOCK(\'lock1\'), IS_USED_LOCK(\'lock1\');\n+-----------------------+-----------------------+\n| IS_FREE_LOCK(\'lock1\') | IS_USED_LOCK(\'lock1\') |\n+-----------------------+-----------------------+\n| 0 | 46 |\n+-----------------------+-----------------------+\n\nSELECT IS_FREE_LOCK(\'lock2\'), IS_USED_LOCK(\'lock2\');\n+-----------------------+-----------------------+\n| IS_FREE_LOCK(\'lock2\') | IS_USED_LOCK(\'lock2\') |\n+-----------------------+-----------------------+\n| 1 | NULL |\n+-----------------------+-----------------------+\n\nMultiple locks can be held:\n\nSELECT GET_LOCK(\'lock2\',10);\n+----------------------+\n| GET_LOCK(\'lock2\',10) |\n+----------------------+\n| 1 |\n+----------------------+\n\nSELECT IS_FREE_LOCK(\'lock1\'), IS_FREE_LOCK(\'lock2\');\n+-----------------------+-----------------------+\n| IS_FREE_LOCK(\'lock1\') | IS_FREE_LOCK(\'lock2\') |\n+-----------------------+-----------------------+\n| 0 | 0 |\n+-----------------------+-----------------------+\n\nSELECT RELEASE_LOCK(\'lock1\'), RELEASE_LOCK(\'lock2\');\n+-----------------------+-----------------------+\n| RELEASE_LOCK(\'lock1\') | RELEASE_LOCK(\'lock2\') |\n+-----------------------+-----------------------+\n| 1 | 1 |\n+-----------------------+-----------------------+\n\nIt is possible to hold the same lock recursively. This example is viewed using\nthe metadata_lock_info plugin:\n\nSELECT GET_LOCK(\'lock3\',10);\n+----------------------+\n| GET_LOCK(\'lock3\',10) |\n+----------------------+\n| 1 |\n+----------------------+\n\nSELECT GET_LOCK(\'lock3\',10);\n+----------------------+\n| GET_LOCK(\'lock3\',10) |\n+----------------------+\n| 1 |\n+----------------------+\n\nSELECT * FROM INFORMATION_SCHEMA.METADATA_LOCK_INFO;\n+-----------+---------------------+---------------+-----------+--------------+-\n----------+\n| THREAD_ID | LOCK_MODE | LOCK_DURATION | LOCK_TYPE | TABLE_SCHEMA |\nTABLE_NAME |\n+-----------+---------------------+---------------+-----------+--------------+-\n----------+\n| 46 | MDL_SHARED_NO_WRITE | NULL | User lock | lock3 |\n |\n+-----------+---------------------+---------------+-----------+--------------+-\n----------+\n\nSELECT RELEASE_LOCK(\'lock3\');\n+-----------------------+\n| RELEASE_LOCK(\'lock3\') |\n+-----------------------+\n| 1 |\n+-----------------------+\n\nSELECT * FROM INFORMATION_SCHEMA.METADATA_LOCK_INFO;\n+-----------+---------------------+---------------+-----------+--------------+-\n----------+\n| THREAD_ID | LOCK_MODE | LOCK_DURATION | LOCK_TYPE | TABLE_SCHEMA |\nTABLE_NAME |\n+-----------+---------------------+---------------+-----------+--------------+-\n----------+\n| 46 | MDL_SHARED_NO_WRITE | NULL | User lock | lock3 |\n |\n+-----------+---------------------+---------------+-----------+--------------+-\n----------+\n\nSELECT RELEASE_LOCK(\'lock3\');\n+-----------------------+\n| RELEASE_LOCK(\'lock3\') |\n+-----------------------+\n| 1 |\n+-----------------------+\n\nSELECT * FROM INFORMATION_SCHEMA.METADATA_LOCK_INFO;\nEmpty set (0.000 sec)\n\nTimeout example: Connection 1:\n\nSELECT GET_LOCK(\'lock4\',10);\n+----------------------+\n| GET_LOCK(\'lock4\',10) |\n+----------------------+\n| 1 |\n+----------------------+\n\nConnection 2:\n\nSELECT GET_LOCK(\'lock4\',10);\n\nAfter 10 seconds...\n\n+----------------------+\n| GET_LOCK(\'lock4\',10) |\n+----------------------+\n| 0 |\n+----------------------+\n\nDeadlocks are automatically detected and resolved. Connection 1:\n\nSELECT GET_LOCK(\'lock5\',10); \n+----------------------+\n| GET_LOCK(\'lock5\',10) |\n+----------------------+\n| 1 |\n+----------------------+\n\nConnection 2:\n\nSELECT GET_LOCK(\'lock6\',10);\n+----------------------+\n| GET_LOCK(\'lock6\',10) |\n+----------------------+\n| 1 |\n+----------------------+\n\nConnection 1:\n\nSELECT GET_LOCK(\'lock6\',10); \n+----------------------+\n| GET_LOCK(\'lock6\',10) |\n+----------------------+\n| 0 |\n+----------------------+\n\nConnection 2:\n\nSELECT GET_LOCK(\'lock5\',10);\nERROR 1213 (40001): Deadlock found when trying to get lock; try restarting\ntransaction\n\nURL: https://mariadb.com/kb/en/get_lock/','','https://mariadb.com/kb/en/get_lock/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (148,14,'INET6_ATON','Syntax\n------\n\nINET6_ATON(expr)\n\nDescription\n-----------\n\nGiven an IPv6 or IPv4 network address as a string, returns a binary string\nthat represents the numeric value of the address.\n\nNo trailing zone ID\'s or traling network masks are permitted. For IPv4\naddresses, or IPv6 addresses with IPv4 address parts, no classful addresses or\ntrailing port numbers are permitted and octal numbers are not supported.\n\nThe returned binary string will be VARBINARY(16) or VARBINARY(4) for IPv6 and\nIPv4 addresses respectively.\n\nReturns NULL if the argument is not understood.\n\nMariaDB starting with 10.5.0\n----------------------------\nFrom MariaDB 10.5.0, INET6_ATON can take INET6 as an argument.\n\nExamples\n--------\n\nSELECT HEX(INET6_ATON(\'10.0.1.1\'));\n+-----------------------------+\n| HEX(INET6_ATON(\'10.0.1.1\')) |\n+-----------------------------+\n| 0A000101 |\n+-----------------------------+\n\nSELECT HEX(INET6_ATON(\'48f3::d432:1431:ba23:846f\'));\n+----------------------------------------------+\n| HEX(INET6_ATON(\'48f3::d432:1431:ba23:846f\')) |\n+----------------------------------------------+\n| 48F3000000000000D4321431BA23846F |\n+----------------------------------------------+\n\nURL: https://mariadb.com/kb/en/inet6_aton/','','https://mariadb.com/kb/en/inet6_aton/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (149,14,'INET6_NTOA','Syntax\n------\n\nINET6_NTOA(expr)\n\nDescription\n-----------\n\nGiven an IPv6 or IPv4 network address as a numeric binary string, returns the\naddress as a nonbinary string in the connection character set.\n\nThe return string is lowercase, and is platform independent, since it does not\nuse functions specific to the operating system. It has a maximum length of 39\ncharacters.\n\nReturns NULL if the argument is not understood.\n\nExamples\n--------\n\nSELECT INET6_NTOA(UNHEX(\'0A000101\'));\n+-------------------------------+\n| INET6_NTOA(UNHEX(\'0A000101\')) |\n+-------------------------------+\n| 10.0.1.1 |\n+-------------------------------+\n\nSELECT INET6_NTOA(UNHEX(\'48F3000000000000D4321431BA23846F\'));\n+-------------------------------------------------------+\n| INET6_NTOA(UNHEX(\'48F3000000000000D4321431BA23846F\')) |\n+-------------------------------------------------------+\n| 48f3::d432:1431:ba23:846f |\n+-------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/inet6_ntoa/','','https://mariadb.com/kb/en/inet6_ntoa/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (150,14,'INET_ATON','Syntax\n------\n\nINET_ATON(expr)\n\nDescription\n-----------\n\nGiven the dotted-quad representation of an IPv4 network address as a string,\nreturns an integer that represents the numeric value of the address. Addresses\nmay be 4- or 8-byte addresses.\n\nReturns NULL if the argument is not understood.\n\nExamples\n--------\n\nSELECT INET_ATON(\'192.168.1.1\');\n+--------------------------+\n| INET_ATON(\'192.168.1.1\') |\n+--------------------------+\n| 3232235777 |\n+--------------------------+\n\nThis is calculated as follows: 192 x 2563 + 168 x 256 2 + 1 x 256 + 1\n\nURL: https://mariadb.com/kb/en/inet_aton/','','https://mariadb.com/kb/en/inet_aton/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (151,14,'INET_NTOA','Syntax\n------\n\nINET_NTOA(expr)\n\nDescription\n-----------\n\nGiven a numeric IPv4 network address in network byte order (4 or 8 byte),\nreturns the dotted-quad representation of the address as a string.\n\nExamples\n--------\n\nSELECT INET_NTOA(3232235777);\n+-----------------------+\n| INET_NTOA(3232235777) |\n+-----------------------+\n| 192.168.1.1 |\n+-----------------------+\n\n192.168.1.1 corresponds to 3232235777 since 192 x 2563 + 168 x 256 2 + 1 x 256\n+ 1 = 3232235777\n\nURL: https://mariadb.com/kb/en/inet_ntoa/','','https://mariadb.com/kb/en/inet_ntoa/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (152,14,'IS_FREE_LOCK','Syntax\n------\n\nIS_FREE_LOCK(str)\n\nDescription\n-----------\n\nChecks whether the lock named str is free to use (that is, not locked).\nReturns 1 if the lock is free (no one is using the lock), 0 if the lock is in\nuse, and NULL if an error occurs (such as an incorrect argument, like an empty\nstring or NULL). str is case insensitive.\n\nIf the metadata_lock_info plugin is installed, the Information Schema\nmetadata_lock_info table contains information about locks of this kind (as\nwell as metadata locks).\n\nStatements using the IS_FREE_LOCK function are not safe for statement-based\nreplication.\n\nURL: https://mariadb.com/kb/en/is_free_lock/','','https://mariadb.com/kb/en/is_free_lock/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (153,14,'IS_IPV4','Syntax\n------\n\nIS_IPV4(expr)\n\nDescription\n-----------\n\nIf the expression is a valid IPv4 address, returns 1, otherwise returns 0.\n\nIS_IPV4() is stricter than INET_ATON(), but as strict as INET6_ATON(), in\ndetermining the validity of an IPv4 address. This implies that if IS_IPV4\nreturns 1, the same expression will always return a non-NULL result when\npassed to INET_ATON(), but that the reverse may not apply.\n\nExamples\n--------\n\nSELECT IS_IPV4(\'1110.0.1.1\');\n+-----------------------+\n| IS_IPV4(\'1110.0.1.1\') |\n+-----------------------+\n| 0 |\n+-----------------------+\n\nSELECT IS_IPV4(\'48f3::d432:1431:ba23:846f\');\n+--------------------------------------+\n| IS_IPV4(\'48f3::d432:1431:ba23:846f\') |\n+--------------------------------------+\n| 0 |\n+--------------------------------------+\n\nURL: https://mariadb.com/kb/en/is_ipv4/','','https://mariadb.com/kb/en/is_ipv4/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (154,14,'IS_IPV4_COMPAT','Syntax\n------\n\nIS_IPV4_COMPAT(expr)\n\nDescription\n-----------\n\nReturns 1 if a given numeric binary string IPv6 address, such as returned by\nINET6_ATON(), is IPv4-compatible, otherwise returns 0.\n\nMariaDB starting with 10.5.0\n----------------------------\nFrom MariaDB 10.5.0, when the argument is not INET6, automatic implicit CAST\nto INET6 is applied. As a consequence, IS_IPV4_COMPAT now understands\narguments in both text representation and binary(16) representation. Before\nMariaDB 10.5.0, the function understood only binary(16) representation.\n\nExamples\n--------\n\nSELECT IS_IPV4_COMPAT(INET6_ATON(\'::10.0.1.1\'));\n+------------------------------------------+\n| IS_IPV4_COMPAT(INET6_ATON(\'::10.0.1.1\')) |\n+------------------------------------------+\n| 1 |\n+------------------------------------------+\n\nSELECT IS_IPV4_COMPAT(INET6_ATON(\'::48f3::d432:1431:ba23:846f\'));\n+-----------------------------------------------------------+\n| IS_IPV4_COMPAT(INET6_ATON(\'::48f3::d432:1431:ba23:846f\')) |\n+-----------------------------------------------------------+\n| 0 |\n+-----------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/is_ipv4_compat/','','https://mariadb.com/kb/en/is_ipv4_compat/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (155,14,'IS_IPV4_MAPPED','Syntax\n------\n\nIS_IPV4_MAPPED(expr)\n\nDescription\n-----------\n\nReturns 1 if a given a numeric binary string IPv6 address, such as returned by\nINET6_ATON(), is a valid IPv4-mapped address, otherwise returns 0.\n\nMariaDB starting with 10.5.0\n----------------------------\nFrom MariaDB 10.5.0, when the argument is not INET6, automatic implicit CAST\nto INET6 is applied. As a consequence, IS_IPV4_MAPPED now understands\narguments in both text representation and binary(16) representation. Before\nMariaDB 10.5.0, the function understood only binary(16) representation.\n\nExamples\n--------\n\nSELECT IS_IPV4_MAPPED(INET6_ATON(\'::10.0.1.1\'));\n+------------------------------------------+\n| IS_IPV4_MAPPED(INET6_ATON(\'::10.0.1.1\')) |\n+------------------------------------------+\n| 0 |\n+------------------------------------------+\n\nSELECT IS_IPV4_MAPPED(INET6_ATON(\'::ffff:10.0.1.1\'));\n+-----------------------------------------------+\n| IS_IPV4_MAPPED(INET6_ATON(\'::ffff:10.0.1.1\')) |\n+-----------------------------------------------+\n| 1 |\n+-----------------------------------------------+\n\nURL: https://mariadb.com/kb/en/is_ipv4_mapped/','','https://mariadb.com/kb/en/is_ipv4_mapped/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (156,14,'IS_IPV6','Syntax\n------\n\nIS_IPV6(expr)\n\nDescription\n-----------\n\nReturns 1 if the expression is a valid IPv6 address specified as a string,\notherwise returns 0. Does not consider IPv4 addresses to be valid IPv6\naddresses.\n\nExamples\n--------\n\nSELECT IS_IPV6(\'48f3::d432:1431:ba23:846f\');\n+--------------------------------------+\n| IS_IPV6(\'48f3::d432:1431:ba23:846f\') |\n+--------------------------------------+\n| 1 |\n+--------------------------------------+\n1 row in set (0.02 sec)\n\nSELECT IS_IPV6(\'10.0.1.1\');\n+---------------------+\n| IS_IPV6(\'10.0.1.1\') |\n+---------------------+\n| 0 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/is_ipv6/','','https://mariadb.com/kb/en/is_ipv6/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (157,14,'IS_USED_LOCK','Syntax\n------\n\nIS_USED_LOCK(str)\n\nDescription\n-----------\n\nChecks whether the lock named str is in use (that is, locked). If so, it\nreturns the connection identifier of the client that holds the lock.\nOtherwise, it returns NULL. str is case insensitive.\n\nIf the metadata_lock_info plugin is installed, the Information Schema\nmetadata_lock_info table contains information about locks of this kind (as\nwell as metadata locks).\n\nStatements using the IS_USED_LOCK function are not safe for statement-based\nreplication.\n\nURL: https://mariadb.com/kb/en/is_used_lock/','','https://mariadb.com/kb/en/is_used_lock/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (158,14,'MASTER_GTID_WAIT','Syntax\n------\n\nMASTER_GTID_WAIT(gtid-list[, timeout)\n\nDescription\n-----------\n\nThis function takes a string containing a comma-separated list of global\ntransaction id\'s (similar to the value of, for example, gtid_binlog_pos). It\nwaits until the value of gtid_slave_pos has the same or higher seq_no within\nall replication domains specified in the gtid-list; in other words, it waits\nuntil the slave has reached the specified GTID position.\n\nAn optional second argument gives a timeout in seconds. If the timeout expires\nbefore the specified GTID position is reached, then the function returns -1.\nPassing NULL or a negative number for the timeout means no timeout, and the\nfunction will wait indefinitely.\n\nIf the wait completes without a timeout, 0 is returned. Passing NULL for the\ngtid-list makes the function return NULL immediately, without waiting.\n\nThe gtid-list may be the empty string, in which case MASTER_GTID_WAIT()\nreturns immediately. If the gtid-list contains fewer domains than\ngtid_slave_pos, then only those domains are waited upon. If gtid-list contains\na domain that is not present in @@gtid_slave_pos, then MASTER_GTID_WAIT() will\nwait until an event containing such domain_id arrives on the slave (or until\ntimed out or killed).\n\nMASTER_GTID_WAIT() can be useful to ensure that a slave has caught up to a\nmaster. Simply take the value of gtid_binlog_pos on the master, and use it in\na MASTER_GTID_WAIT() call on the slave; when the call completes, the slave\nwill have caught up with that master position.\n\nMASTER_GTID_WAIT() can also be used in client applications together with the\nlast_gtid session variable. This is useful in a read-scaleout replication\nsetup, where the application writes to a single master but divides the reads\nout to a number of slaves to distribute the load. In such a setup, there is a\nrisk that an application could first do an update on the master, and then a\nbit later do a read on a slave, and if the slave is not fast enough, the data\nread from the slave might not include the update just made, possibly confusing\nthe application and/or the end-user. One way to avoid this is to request the\nvalue of last_gtid on the master just after the update. Then before doing the\nread on the slave, do a MASTER_GTID_WAIT() on the value obtained from the\nmaster; this will ensure that the read is not performed until the slave has\nreplicated sufficiently far for the update to have become visible.\n\nNote that MASTER_GTID_WAIT() can be used even if the slave is configured not\nto use GTID for connections (CHANGE MASTER TO master_use_gtid=no). This is\nbecause from MariaDB 10, GTIDs are always logged on the master server, and\nalways recorded on the slave servers.\n\nDifferences to MASTER_POS_WAIT()\n--------------------------------\n\n* MASTER_GTID_WAIT() is global; it waits for any master connection to reach\n the specified GTID position. MASTER_POS_WAIT() works only against a\n specific connection. This also means that while MASTER_POS_WAIT() aborts if\n its master connection is terminated with STOP SLAVE or due to an error,\n MASTER_GTID_WAIT() continues to wait while slaves are stopped.\n\n* MASTER_GTID_WAIT() can take its timeout as a floating-point value, so a\n timeout in fractional seconds is supported, eg. MASTER_GTID_WAIT(\"0-1-100\",\n 0.5). (The minimum wait is one microsecond, 0.000001 seconds).\n\n* MASTER_GTID_WAIT() allows one to specify a timeout of zero in order to do a\n non-blocking check to see if the slaves have progressed to a specific GTID\nposition\n (MASTER_POS_WAIT() takes a zero timeout as meaning an infinite wait). To do\n an infinite MASTER_GTID_WAIT(), specify a negative timeout, or omit the\n timeout argument.\n\n* MASTER_GTID_WAIT() does not return the number of events executed since the\n wait started, nor does it return NULL if a slave thread is stopped. It\n always returns either 0 for successful wait completed, or -1 for timeout\n reached (or NULL if the specified gtid-pos is NULL).\n\nSince MASTER_GTID_WAIT() looks only at the seq_no part of the GTIDs, not the\nserver_id, care is needed if a slave becomes diverged from another server so\nthat two different GTIDs with the same seq_no (in the same domain) arrive at\nthe same server. This situation is in any case best avoided; setting\ngtid_strict_mode is recommended, as this will prevent any such out-of-order\nsequence numbers from ever being replicated on a slave.\n\nURL: https://mariadb.com/kb/en/master_gtid_wait/','','https://mariadb.com/kb/en/master_gtid_wait/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (159,14,'MASTER_POS_WAIT','Syntax\n------\n\nMASTER_POS_WAIT(log_name,log_pos[,timeout,[\"connection_name\"]])\n\nDescription\n-----------\n\nThis function is useful in replication for controlling primary/replica\nsynchronization. It blocks until the replica has read and applied all updates\nup to the specified position (log_name,log_pos) in the primary log. The return\nvalue is the number of log events the replica had to wait for to advance to\nthe specified position. The function returns NULL if the replica SQL thread is\nnot started, the replica\'s primary information is not initialized, the\narguments are incorrect, or an error occurs. It returns -1 if the timeout has\nbeen exceeded. If the replica SQL thread stops while MASTER_POS_WAIT() is\nwaiting, the function returns NULL. If the replica is past the specified\nposition, the function returns immediately.\n\nIf a timeout value is specified, MASTER_POS_WAIT() stops waiting when timeout\nseconds have elapsed. timeout must be greater than 0; a zero or negative\ntimeout means no timeout.\n\nThe connection_name is used when you are using multi-source-replication. If\nyou don\'t specify it, it\'s set to the value of the default_master_connection\nsystem variable.\n\nStatements using the MASTER_POS_WAIT() function are not safe for replication.\n\nURL: https://mariadb.com/kb/en/master_pos_wait/','','https://mariadb.com/kb/en/master_pos_wait/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (160,14,'NAME_CONST','Syntax\n------\n\nNAME_CONST(name,value)\n\nDescription\n-----------\n\nReturns the given value. When used to produce a result set column,\nNAME_CONST() causes the column to have the given name. The arguments should be\nconstants.\n\nThis function is used internally when replicating stored procedures. It makes\nlittle sense to use it explicitly in SQL statements, and it was not supposed\nto be used like that.\n\nSELECT NAME_CONST(\'myname\', 14);\n+--------+\n| myname |\n+--------+\n| 14 |\n+--------+\n\nURL: https://mariadb.com/kb/en/name_const/','','https://mariadb.com/kb/en/name_const/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (161,14,'RELEASE_ALL_LOCKS','MariaDB until 10.5.2\n--------------------\nRELEASE_ALL_LOCKS was added in MariaDB 10.5.2.\n\nSyntax\n------\n\nRELEASE_ALL_LOCKS()\n\nDescription\n-----------\n\nReleases all named locks held by the current session. Returns the number of\nlocks released, or 0 if none were held.\n\nStatements using the RELEASE_ALL_LOCKS function are not safe for\nstatement-based replication.\n\nExamples\n--------\n\nSELECT RELEASE_ALL_LOCKS();\n+---------------------+\n| RELEASE_ALL_LOCKS() | \n+---------------------+\n| 0 |\n+---------------------+\n\nSELECT GET_LOCK(\'lock1\',10);\n+----------------------+\n| GET_LOCK(\'lock1\',10) |\n+----------------------+\n| 1 |\n+----------------------+\n\nSELECT RELEASE_ALL_LOCKS();\n+---------------------+\n| RELEASE_ALL_LOCKS() | \n+---------------------+\n| 1 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/release_all_locks/','','https://mariadb.com/kb/en/release_all_locks/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (162,14,'RELEASE_LOCK','Syntax\n------\n\nRELEASE_LOCK(str)\n\nDescription\n-----------\n\nReleases the lock named by the string str that was obtained with GET_LOCK().\nReturns 1 if the lock was released, 0 if the lock was not established by this\nthread (in which case the lock is not released), and NULL if the named lock\ndid not exist. The lock does not exist if it was never obtained by a call to\nGET_LOCK() or if it has previously been released.\n\nstr is case insensitive. If str is an empty string or NULL, RELEASE_LOCK()\nreturns NULL and does nothing.\n\nStatements using the RELEASE_LOCK() function are not safe for replication.\n\nThe DO statement is convenient to use with RELEASE_LOCK().\n\nExamples\n--------\n\nConnection1:\n\nSELECT GET_LOCK(\'lock1\',10);\n+----------------------+\n| GET_LOCK(\'lock1\',10) |\n+----------------------+\n| 1 |\n+----------------------+\n\nConnection 2:\n\nSELECT GET_LOCK(\'lock2\',10);\n+----------------------+\n| GET_LOCK(\'lock2\',10) |\n+----------------------+\n| 1 |\n+----------------------+\n\nConnection 1:\n\nSELECT RELEASE_LOCK(\'lock1\'), RELEASE_LOCK(\'lock2\'), RELEASE_LOCK(\'lock3\');\n+-----------------------+-----------------------+-----------------------+\n| RELEASE_LOCK(\'lock1\') | RELEASE_LOCK(\'lock2\') | RELEASE_LOCK(\'lock3\') |\n+-----------------------+-----------------------+-----------------------+\n| 1 | 0 | NULL |\n+-----------------------+-----------------------+-----------------------+\n\nFrom MariaDB 10.0.2, it is possible to hold the same lock recursively. This\nexample is viewed using the metadata_lock_info plugin:\n\nSELECT GET_LOCK(\'lock3\',10);\n+----------------------+\n| GET_LOCK(\'lock3\',10) |\n+----------------------+\n| 1 |\n+----------------------+\n\nSELECT GET_LOCK(\'lock3\',10);\n+----------------------+\n| GET_LOCK(\'lock3\',10) |\n+----------------------+\n| 1 |\n+----------------------+\n\nSELECT * FROM INFORMATION_SCHEMA.METADATA_LOCK_INFO;\n+-----------+---------------------+---------------+-----------+--------------+-\n----------+\n| THREAD_ID | LOCK_MODE | LOCK_DURATION | LOCK_TYPE | TABLE_SCHEMA |\nTABLE_NAME |\n+-----------+---------------------+---------------+-----------+--------------+-\n----------+\n| 46 | MDL_SHARED_NO_WRITE | NULL | User lock | lock3 |\n |\n+-----------+---------------------+---------------+-----------+--------------+-\n----------+\n\nSELECT RELEASE_LOCK(\'lock3\');\n+-----------------------+\n| RELEASE_LOCK(\'lock3\') |\n+-----------------------+\n| 1 |\n+-----------------------+\n\nSELECT * FROM INFORMATION_SCHEMA.METADATA_LOCK_INFO;\n+-----------+---------------------+---------------+-----------+--------------+-\n----------+\n| THREAD_ID | LOCK_MODE | LOCK_DURATION | LOCK_TYPE | TABLE_SCHEMA |\nTABLE_NAME |\n+-----------+---------------------+---------------+-----------+--------------+-\n----------+\n| 46 | MDL_SHARED_NO_WRITE | NULL | User lock | lock3 |\n |\n+-----------+---------------------+---------------+-----------+--------------+-\n----------+\n\nSELECT RELEASE_LOCK(\'lock3\');\n+-----------------------+\n| RELEASE_LOCK(\'lock3\') |\n+-----------------------+\n| 1 |\n+-----------------------+\n\nSELECT * FROM INFORMATION_SCHEMA.METADATA_LOCK_INFO;\nEmpty set (0.000 sec)\n\nURL: https://mariadb.com/kb/en/release_lock/','','https://mariadb.com/kb/en/release_lock/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (163,14,'SLEEP','Syntax\n------\n\nSLEEP(duration)\n\nDescription\n-----------\n\nSleeps (pauses) for the number of seconds given by the duration argument, then\nreturns 0. If SLEEP() is interrupted, it returns 1. The duration may have a\nfractional part given in microseconds.\n\nStatements using the SLEEP() function are not safe for replication.\n\nExample\n-------\n\nSELECT SLEEP(5.5);\n+------------+\n| SLEEP(5.5) |\n+------------+\n| 0 |\n+------------+\n1 row in set (5.50 sec)\n\nURL: https://mariadb.com/kb/en/sleep/','','https://mariadb.com/kb/en/sleep/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (164,14,'SYS_GUID','MariaDB starting with 10.6.1\n----------------------------\nThe SYS_GUID function was introduced in MariaDB 10.6.1 to enhance Oracle\ncompatibility. Similar functionality can be achieved with the UUID function.\n\nSyntax\n------\n\nSYS_GUID()\n\nDescription\n-----------\n\nReturns a 16-byte globally unique identifier (GUID), similar to the UUID\nfunction, but without the - character.\n\nExample\n-------\n\nSELECT SYS_GUID();\n+----------------------------------+\n| SYS_GUID() |\n+----------------------------------+\n| 2C574E45BA2811EBB265F859713E4BE4 |\n+----------------------------------+\n\nURL: https://mariadb.com/kb/en/sys_guid/','','https://mariadb.com/kb/en/sys_guid/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (165,14,'UUID','Syntax\n------\n\nUUID()\n\nDescription\n-----------\n\nReturns a Universally Unique Identifier (UUID).\n\nA UUID is designed as a number that is globally unique in space and time. Two\ncalls to UUID() are expected to generate two different values, even if these\ncalls are performed on two separate computers that are not connected to each\nother.\n\nUUID() results are intended to be unique, but cannot always be relied upon to\nunpredictable and unguessable, so should not be relied upon for these purposes.\n\nA UUID is a 128-bit number represented by a utf8 string of five hexadecimal\nnumbers in aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee format:\n\n* The first three numbers are generated from a timestamp.\n* The fourth number preserves temporal uniqueness in case the timestamp value\n loses monotonicity (for example, due to daylight saving time).\n* The fifth number is an IEEE 802 node number that provides spatial uniqueness.\n A random number is substituted if the latter is not available (for example,\n because the host computer has no Ethernet card, or we do not know how to find\n the hardware address of an interface on your operating system). In this case,\n spatial uniqueness cannot be guaranteed. Nevertheless, a collision should\n have very low probability.\n\nCurrently, the MAC address of an interface is taken into account only on\nFreeBSD and Linux. On other operating systems, MariaDB uses a randomly\ngenerated 48-bit number.\n\nStatements using the UUID() function are not safe for replication.\n\nThe results are generated according to the \"DCE 1.1:Remote Procedure Call\"\n(Appendix A) CAE (Common Applications Environment) Specifications published by\nThe Open Group in October 1997 (Document Number C706).\n\nExamples\n--------\n\nSELECT UUID();\n+--------------------------------------+\n| UUID() |\n+--------------------------------------+\n| cd41294a-afb0-11df-bc9b-00241dd75637 |\n+--------------------------------------+\n\nURL: https://mariadb.com/kb/en/uuid/','','https://mariadb.com/kb/en/uuid/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (166,14,'UUID_SHORT','Syntax\n------\n\nUUID_SHORT()\n\nDescription\n-----------\n\nReturns a \"short\" universally unique identifier as a 64-bit unsigned integer\n(rather than a string-form 128-bit identifier as returned by the UUID()\nfunction).\n\nThe value of UUID_SHORT() is guaranteed to be unique if the following\nconditions hold:\n\n* The server_id of the current host is unique among your set of master and\n slave servers\n* server_id is between 0 and 255\n* You don\'t set back your system time for your server between mysqld restarts\n* You do not invoke UUID_SHORT() on average more than 16\n million times per second between mysqld restarts\n\nThe UUID_SHORT() return value is constructed this way:\n\n(server_id & 255) << 56\n+ (server_startup_time_in_seconds << 24)\n+ incremented_variable++;\n\nStatements using the UUID_SHORT() function are not safe for statement-based\nreplication.\n\nExamples\n--------\n\nSELECT UUID_SHORT();\n+-------------------+\n| UUID_SHORT() |\n+-------------------+\n| 21517162376069120 |\n+-------------------+\n\ncreate table t1 (a bigint unsigned default(uuid_short()) primary key);\ninsert into t1 values(),();\nselect * from t1;\n+-------------------+\n| a |\n+-------------------+\n| 98113699159474176 |\n| 98113699159474177 |\n+-------------------+\n\nURL: https://mariadb.com/kb/en/uuid_short/','','https://mariadb.com/kb/en/uuid_short/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (167,14,'VALUES / VALUE','Syntax\n------\n\nMariaDB starting with 10.3.3\n----------------------------\n\nVALUE(col_name)\n\nMariaDB until 10.3.2\n--------------------\n\nVALUES(col_name)\n\nDescription\n-----------\n\nIn an INSERT ... ON DUPLICATE KEY UPDATE statement, you can use the\nVALUES(col_name) function in the UPDATE clause to refer to column values from\nthe INSERT portion of the statement. In other words, VALUES(col_name) in the\nUPDATE clause refers to the value of col_name that would be inserted, had no\nduplicate-key conflict occurred. This function is especially useful in\nmultiple-row inserts.\n\nThe VALUES() function is meaningful only in INSERT ... ON DUPLICATE KEY UPDATE\nstatements and returns NULL otherwise.\n\nIn MariaDB 10.3.3 this function was renamed to VALUE(), because it\'s\nincompatible with the standard Table Value Constructors syntax, implemented in\nMariaDB 10.3.3.\n\nThe VALUES() function can still be used even from MariaDB 10.3.3, but only in\nINSERT ... ON DUPLICATE KEY UPDATE statements; it\'s a syntax error otherwise.\n\nExamples\n--------\n\nMariaDB starting with 10.3.3\n----------------------------\n\nINSERT INTO t (a,b,c) VALUES (1,2,3),(4,5,6)\n ON DUPLICATE KEY UPDATE c=VALUE(a)+VALUE(b);\n\nMariaDB until 10.3.2\n--------------------\n\nINSERT INTO t (a,b,c) VALUES (1,2,3),(4,5,6)\n ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);\n\nURL: https://mariadb.com/kb/en/values-value/','','https://mariadb.com/kb/en/values-value/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (168,15,'!','Syntax\n------\n\nNOT, !\n\nDescription\n-----------\n\nLogical NOT. Evaluates to 1 if the operand is 0, to 0 if the operand is\nnon-zero, and NOT NULL returns NULL.\n\nBy default, the ! operator has a higher precedence. If the HIGH_NOT_PRECEDENCE\nSQL_MODE flag is set, NOT and ! have the same precedence.\n\nExamples\n--------\n\nSELECT NOT 10;\n+--------+\n| NOT 10 |\n+--------+\n| 0 |\n+--------+\n\nSELECT NOT 0;\n+-------+\n| NOT 0 |\n+-------+\n| 1 |\n+-------+\n\nSELECT NOT NULL;\n+----------+\n| NOT NULL |\n+----------+\n| NULL |\n+----------+\n\nSELECT ! (1+1);\n+---------+\n| ! (1+1) |\n+---------+\n| 0 |\n+---------+\n\nSELECT ! 1+1;\n+-------+\n| ! 1+1 |\n+-------+\n| 1 |\n+-------+\n\nURL: https://mariadb.com/kb/en/not/','','https://mariadb.com/kb/en/not/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (169,15,'&&','Syntax\n------\n\nAND, &&\n\nDescription\n-----------\n\nLogical AND. Evaluates to 1 if all operands are non-zero and not NULL, to 0 if\none or more operands are 0, otherwise NULL is returned.\n\nFor this operator, short-circuit evaluation can be used.\n\nExamples\n--------\n\nSELECT 1 && 1;\n+--------+\n| 1 && 1 |\n+--------+\n| 1 |\n+--------+\n\nSELECT 1 && 0;\n+--------+\n| 1 && 0 |\n+--------+\n| 0 |\n+--------+\n\nSELECT 1 && NULL;\n+-----------+\n| 1 && NULL |\n+-----------+\n| NULL |\n+-----------+\n\nSELECT 0 && NULL;\n+-----------+\n| 0 && NULL |\n+-----------+\n| 0 |\n+-----------+\n\nSELECT NULL && 0;\n+-----------+\n| NULL && 0 |\n+-----------+\n| 0 |\n+-----------+\n\nURL: https://mariadb.com/kb/en/and/','','https://mariadb.com/kb/en/and/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (170,15,'XOR','Syntax\n------\n\nXOR\n\nDescription\n-----------\n\nXOR stands for eXclusive OR. Returns NULL if either operand is NULL. For\nnon-NULL operands, evaluates to 1 if an odd number of operands is non-zero,\notherwise 0 is returned.\n\nExamples\n--------\n\nSELECT 1 XOR 1;\n+---------+\n| 1 XOR 1 |\n+---------+\n| 0 |\n+---------+\n\nSELECT 1 XOR 0;\n+---------+\n| 1 XOR 0 |\n+---------+\n| 1 |\n+---------+\n\nSELECT 1 XOR NULL;\n+------------+\n| 1 XOR NULL |\n+------------+\n| NULL |\n+------------+\n\nIn the following example, the right 1 XOR 1 is evaluated first, and returns 0.\nThen, 1 XOR 0 is evaluated, and 1 is returned.\n\nSELECT 1 XOR 1 XOR 1;\n+---------------+\n| 1 XOR 1 XOR 1 |\n+---------------+\n| 1 |\n+---------------+\n\nURL: https://mariadb.com/kb/en/xor/','','https://mariadb.com/kb/en/xor/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (171,15,'||','Syntax\n------\n\nOR, ||\n\nDescription\n-----------\n\nLogical OR. When both operands are non-NULL, the result is 1 if any operand is\nnon-zero, and 0 otherwise. With a NULL operand, the result is 1 if the other\noperand is non-zero, and NULL otherwise. If both operands are NULL, the result\nis NULL.\n\nFor this operator, short-circuit evaluation can be used.\n\nNote that, if the PIPES_AS_CONCAT SQL_MODE is set, || is used as a string\nconcatenation operator. This means that a || b is the same as CONCAT(a,b). See\nCONCAT() for details.\n\nOracle Mode\n-----------\n\nMariaDB starting with 10.3\n--------------------------\nIn Oracle mode from MariaDB 10.3, || ignores NULL.\n\nExamples\n--------\n\nSELECT 1 || 1;\n+--------+\n| 1 || 1 |\n+--------+\n| 1 |\n+--------+\n\nSELECT 1 || 0;\n+--------+\n| 1 || 0 |\n+--------+\n| 1 |\n+--------+\n\nSELECT 0 || 0;\n+--------+\n| 0 || 0 |\n+--------+\n| 0 |\n+--------+\n\nSELECT 0 || NULL;\n+-----------+\n| 0 || NULL |\n+-----------+\n| NULL |\n+-----------+\n\nSELECT 1 || NULL;\n+-----------+\n| 1 || NULL |\n+-----------+\n| 1 |\n+-----------+\n\nIn Oracle mode, from MariaDB 10.3:\n\nSELECT 0 || NULL;\n+-----------+\n| 0 || NULL |\n+-----------+\n| 0 |\n+-----------+\n\nURL: https://mariadb.com/kb/en/or/','','https://mariadb.com/kb/en/or/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (172,16,'Stored Aggregate Functions','MariaDB starting with 10.3.3\n----------------------------\nThe ability to create stored aggregate functions was added in MariaDB 10.3.3.\n\nAggregate functions are functions that are computed over a sequence of rows\nand return one result for the sequence of rows.\n\nCreating a custom aggregate function is done using the CREATE FUNCTION\nstatement with two main differences:\n\n* The addition of the AGGREGATE keyword, so CREATE AGGREGATE FUNCTION\n* The FETCH GROUP NEXT ROW instruction inside the loop\n* Oracle PL/SQL compatibility using SQL/PL is provided\n\nStandard Syntax\n---------------\n\nCREATE AGGREGATE FUNCTION function_name (parameters) RETURNS return_type\nBEGIN\n All types of declarations\n DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN return_val;\n LOOP\n FETCH GROUP NEXT ROW; // fetches next row from table\n other instructions\n END LOOP;\nEND\n\nStored aggregate functions were a 2016 Google Summer of Code project by Varun\nGupta.\n\nUsing SQL/PL\n------------\n\nSET sql_mode=Oracle;\nDELIMITER //\n\nCREATE AGGREGATE FUNCTION function_name (parameters) RETURN return_type\n declarations\nBEGIN\n LOOP\n FETCH GROUP NEXT ROW; -- fetches next row from table\n -- other instructions\n\nEND LOOP;\nEXCEPTION\n WHEN NO_DATA_FOUND THEN\n RETURN return_val;\nEND //\n\nDELIMITER ;\n\nExamples\n--------\n\nFirst a simplified example:\n\nCREATE TABLE marks(stud_id INT, grade_count INT);\n\nINSERT INTO marks VALUES (1,6), (2,4), (3,7), (4,5), (5,8);\n\nSELECT * FROM marks;\n+---------+-------------+\n| stud_id | grade_count |\n+---------+-------------+\n| 1 | 6 |\n| 2 | 4 |\n| 3 | 7 |\n| 4 | 5 |\n| 5 | 8 |\n+---------+-------------+\n\nDELIMITER //\nCREATE AGGREGATE FUNCTION IF NOT EXISTS aggregate_count(x INT) RETURNS INT\nBEGIN\n DECLARE count_students INT DEFAULT 0;\n DECLARE CONTINUE HANDLER FOR NOT FOUND\n RETURN count_students;\n LOOP\n FETCH GROUP NEXT ROW;\n IF x THEN\n SET count_students = count_students+1;\n END IF;\n END LOOP;\nEND //\nDELIMITER ;\n\nA non-trivial example that cannot easily be rewritten using existing functions:\n\nDELIMITER //\nCREATE AGGREGATE FUNCTION medi_int(x INT) RETURNS DOUBLE\nBEGIN\n DECLARE CONTINUE HANDLER FOR NOT FOUND\n BEGIN\n DECLARE res DOUBLE;\n DECLARE cnt INT DEFAULT (SELECT COUNT(*) FROM tt);\n DECLARE lim INT DEFAULT (cnt-1) DIV 2;\n IF cnt % 2 = 0 THEN\n SET res = (SELECT AVG(a) FROM (SELECT a FROM tt ORDER BY a LIMIT\nlim,2) ttt);\n ELSE\n SET res = (SELECT a FROM tt ORDER BY a LIMIT lim,1);\n END IF;\n DROP TEMPORARY TABLE tt;\n RETURN res;\n END;\n CREATE TEMPORARY TABLE tt (a INT);\n LOOP\n FETCH GROUP NEXT ROW;\n INSERT INTO tt VALUES (x);\n END LOOP;\nEND //\nDELIMITER ;\n\nSQL/PL Example\n--------------\n\nThis uses the same marks table as created above.\n\nSET sql_mode=Oracle;\nDELIMITER //\n\nCREATE AGGREGATE FUNCTION aggregate_count(x INT) RETURN INT AS count_students\nINT DEFAULT 0;\nBEGIN\n LOOP\n FETCH GROUP NEXT ROW;\n IF x THEN\n SET count_students := count_students+1;\n END IF;\n END LOOP;\nEXCEPTION\n WHEN NO_DATA_FOUND THEN\n RETURN count_students;\nEND aggregate_count //\nDELIMITER ;\n\nSELECT aggregate_count(stud_id) FROM marks;\n\nURL: https://mariadb.com/kb/en/stored-aggregate-functions/','','https://mariadb.com/kb/en/stored-aggregate-functions/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (173,16,'AVG','Syntax\n------\n\nAVG([DISTINCT] expr)\n\nDescription\n-----------\n\nReturns the average value of expr. The DISTINCT option can be used to return\nthe average of the distinct values of expr. NULL values are ignored. It is an\naggregate function, and so can be used with the GROUP BY clause.\n\nAVG() returns NULL if there were no matching rows.\n\nAVG() can be used as a window function.\n\nExamples\n--------\n\nCREATE TABLE sales (sales_value INT);\n\nINSERT INTO sales VALUES(10),(20),(20),(40);\n\nSELECT AVG(sales_value) FROM sales;\n+------------------+\n| AVG(sales_value) |\n+------------------+\n| 22.5000 |\n+------------------+\n\nSELECT AVG(DISTINCT(sales_value)) FROM sales;\n+----------------------------+\n| AVG(DISTINCT(sales_value)) |\n+----------------------------+\n| 23.3333 |\n+----------------------------+\n\nCommonly, AVG() is used with a GROUP BY clause:\n\nCREATE TABLE student (name CHAR(10), test CHAR(10), score TINYINT);\n\nINSERT INTO student VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87), (\'Tatiana\', \'Tuning\', 83);\n\nSELECT name, AVG(score) FROM student GROUP BY name;\n+---------+------------+\n| name | AVG(score) |\n+---------+------------+\n| Chun | 74.0000 |\n| Esben | 37.0000 |\n| Kaolin | 72.0000 |\n| Tatiana | 85.0000 |\n+---------+------------+\n\nBe careful to avoid this common mistake, not grouping correctly and returning\nmismatched data:\n\nSELECT name,test,AVG(score) FROM student;\n+------+------+------------+\n| name | test | MIN(score) |\n+------+------+------------+\n| Chun | SQL | 31 |\n+------+------+------------+\n\nAs a window function:\n\nCREATE TABLE student_test (name CHAR(10), test CHAR(10), score TINYINT);\n\nINSERT INTO student_test VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87), (\'Tatiana\', \'Tuning\', 83);\n\nSELECT name, test, score, AVG(score) OVER (PARTITION BY test) \n AS average_by_test FROM student_test;\n+---------+--------+-------+-----------------+\n| name | test | score | average_by_test |\n+---------+--------+-------+-----------------+\n| Chun | SQL | 75 | 65.2500 |\n| Chun | Tuning | 73 | 68.7500 |\n| Esben | SQL | 43 | 65.2500 |\n| Esben | Tuning | 31 | 68.7500 |\n| Kaolin | SQL | 56 | 65.2500 |\n| Kaolin | Tuning | 88 | 68.7500 |\n| Tatiana | SQL | 87 | 65.2500 |\n| Tatiana | Tuning | 83 | 68.7500 |\n+---------+--------+-------+-----------------+\n\nURL: https://mariadb.com/kb/en/avg/','','https://mariadb.com/kb/en/avg/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (174,16,'BIT_AND','Syntax\n------\n\nBIT_AND(expr) [over_clause]\n\nDescription\n-----------\n\nReturns the bitwise AND of all bits in expr. The calculation is performed with\n64-bit (BIGINT) precision. It is an aggregate function, and so can be used\nwith the GROUP BY clause.\n\nIf no rows match, BIT_AND will return a value with all bits set to 1. NULL\nvalues have no effect on the result unless all results are NULL, which is\ntreated as no match.\n\nBIT_AND can be used as a window function with the addition of the over_clause.\n\nExamples\n--------\n\nCREATE TABLE vals (x INT);\n\nINSERT INTO vals VALUES(111),(110),(100);\n\nSELECT BIT_AND(x), BIT_OR(x), BIT_XOR(x) FROM vals;\n+------------+-----------+------------+\n| BIT_AND(x) | BIT_OR(x) | BIT_XOR(x) |\n+------------+-----------+------------+\n| 100 | 111 | 101 |\n+------------+-----------+------------+\n\nAs an aggregate function:\n\nCREATE TABLE vals2 (category VARCHAR(1), x INT);\n\nINSERT INTO vals2 VALUES\n (\'a\',111),(\'a\',110),(\'a\',100),\n (\'b\',\'000\'),(\'b\',001),(\'b\',011);\n\nSELECT category, BIT_AND(x), BIT_OR(x), BIT_XOR(x) \n FROM vals GROUP BY category;\n+----------+------------+-----------+------------+\n| category | BIT_AND(x) | BIT_OR(x) | BIT_XOR(x) |\n+----------+------------+-----------+------------+\n| a | 100 | 111 | 101 |\n| b | 0 | 11 | 10 |\n+----------+------------+-----------+------------+\n\nNo match:\n\nSELECT BIT_AND(NULL);\n+----------------------+\n| BIT_AND(NULL) |\n+----------------------+\n| 18446744073709551615 |\n+----------------------+\n\nURL: https://mariadb.com/kb/en/bit_and/','','https://mariadb.com/kb/en/bit_and/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (175,16,'BIT_OR','Syntax\n------\n\nBIT_OR(expr) [over_clause]\n\nDescription\n-----------\n\nReturns the bitwise OR of all bits in expr. The calculation is performed with\n64-bit (BIGINT) precision. It is an aggregate function, and so can be used\nwith the GROUP BY clause.\n\nIf no rows match, BIT_OR will return a value with all bits set to 0. NULL\nvalues have no effect on the result unless all results are NULL, which is\ntreated as no match.\n\nBIT_OR can be used as a window function with the addition of the over_clause.\n\nExamples\n--------\n\nCREATE TABLE vals (x INT);\n\nINSERT INTO vals VALUES(111),(110),(100);\n\nSELECT BIT_AND(x), BIT_OR(x), BIT_XOR(x) FROM vals;\n+------------+-----------+------------+\n| BIT_AND(x) | BIT_OR(x) | BIT_XOR(x) |\n+------------+-----------+------------+\n| 100 | 111 | 101 |\n+------------+-----------+------------+\n\nAs an aggregate function:\n\nCREATE TABLE vals2 (category VARCHAR(1), x INT);\n\nINSERT INTO vals2 VALUES\n (\'a\',111),(\'a\',110),(\'a\',100),\n (\'b\',\'000\'),(\'b\',001),(\'b\',011);\n\nSELECT category, BIT_AND(x), BIT_OR(x), BIT_XOR(x) \n FROM vals GROUP BY category;\n+----------+------------+-----------+------------+\n| category | BIT_AND(x) | BIT_OR(x) | BIT_XOR(x) |\n+----------+------------+-----------+------------+\n| a | 100 | 111 | 101 |\n| b | 0 | 11 | 10 |\n+----------+------------+-----------+------------+\n\nNo match:\n\nSELECT BIT_OR(NULL);\n+--------------+\n| BIT_OR(NULL) |\n+--------------+\n| 0 |\n+--------------+\n\nURL: https://mariadb.com/kb/en/bit_or/','','https://mariadb.com/kb/en/bit_or/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (176,16,'BIT_XOR','Syntax\n------\n\nBIT_XOR(expr) [over_clause]\n\nDescription\n-----------\n\nReturns the bitwise XOR of all bits in expr. The calculation is performed with\n64-bit (BIGINT) precision. It is an aggregate function, and so can be used\nwith the GROUP BY clause.\n\nIf no rows match, BIT_XOR will return a value with all bits set to 0. NULL\nvalues have no effect on the result unless all results are NULL, which is\ntreated as no match.\n\nBIT_XOR can be used as a window function with the addition of the over_clause.\n\nExamples\n--------\n\nCREATE TABLE vals (x INT);\n\nINSERT INTO vals VALUES(111),(110),(100);\n\nSELECT BIT_AND(x), BIT_OR(x), BIT_XOR(x) FROM vals;\n+------------+-----------+------------+\n| BIT_AND(x) | BIT_OR(x) | BIT_XOR(x) |\n+------------+-----------+------------+\n| 100 | 111 | 101 |\n+------------+-----------+------------+\n\nAs an aggregate function:\n\nCREATE TABLE vals2 (category VARCHAR(1), x INT);\n\nINSERT INTO vals2 VALUES\n (\'a\',111),(\'a\',110),(\'a\',100),\n (\'b\',\'000\'),(\'b\',001),(\'b\',011);\n\nSELECT category, BIT_AND(x), BIT_OR(x), BIT_XOR(x) \n FROM vals GROUP BY category;\n+----------+------------+-----------+------------+\n| category | BIT_AND(x) | BIT_OR(x) | BIT_XOR(x) |\n+----------+------------+-----------+------------+\n| a | 100 | 111 | 101 |\n| b | 0 | 11 | 10 |\n+----------+------------+-----------+------------+\n\nNo match:\n\nSELECT BIT_XOR(NULL);\n+---------------+\n| BIT_XOR(NULL) |\n+---------------+\n| 0 |\n+---------------+\n\nURL: https://mariadb.com/kb/en/bit_xor/','','https://mariadb.com/kb/en/bit_xor/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (177,16,'COUNT','Syntax\n------\n\nCOUNT(expr)\n\nDescription\n-----------\n\nReturns a count of the number of non-NULL values of expr in the rows retrieved\nby a SELECT statement. The result is a BIGINT value. It is an aggregate\nfunction, and so can be used with the GROUP BY clause.\n\nCOUNT(*) counts the total number of rows in a table.\n\nCOUNT() returns 0 if there were no matching rows.\n\nCOUNT() can be used as a window function.\n\nExamples\n--------\n\nCREATE TABLE student (name CHAR(10), test CHAR(10), score TINYINT);\n\nINSERT INTO student VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87), (\'Tatiana\', \'Tuning\', 83);\n\nSELECT COUNT(*) FROM student;\n+----------+\n| COUNT(*) |\n+----------+\n| 8 |\n+----------+\n\nCOUNT(DISTINCT) example:\n\nSELECT COUNT(DISTINCT (name)) FROM student;\n+------------------------+\n| COUNT(DISTINCT (name)) |\n+------------------------+\n| 4 |\n+------------------------+\n\nAs a window function\n\nCREATE OR REPLACE TABLE student_test (name CHAR(10), test CHAR(10), score\nTINYINT);\n\nINSERT INTO student_test VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87);\n\nSELECT name, test, score, COUNT(score) OVER (PARTITION BY name) \n AS tests_written FROM student_test;\n+---------+--------+-------+---------------+\n| name | test | score | tests_written |\n+---------+--------+-------+---------------+\n| Chun | SQL | 75 | 2 |\n| Chun | Tuning | 73 | 2 |\n| Esben | SQL | 43 | 2 |\n| Esben | Tuning | 31 | 2 |\n| Kaolin | SQL | 56 | 2 |\n| Kaolin | Tuning | 88 | 2 |\n| Tatiana | SQL | 87 | 1 |\n+---------+--------+-------+---------------+\n\nURL: https://mariadb.com/kb/en/count/','','https://mariadb.com/kb/en/count/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (178,16,'COUNT DISTINCT','Syntax\n------\n\nCOUNT(DISTINCT expr,[expr...])\n\nDescription\n-----------\n\nReturns a count of the number of different non-NULL values.\n\nCOUNT(DISTINCT) returns 0 if there were no matching rows.\n\nAlthough, from MariaDB 10.2.0, COUNT can be used as a window function, COUNT\nDISTINCT cannot be.\n\nExamples\n--------\n\nCREATE TABLE student (name CHAR(10), test CHAR(10), score TINYINT);\n\nINSERT INTO student VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87), (\'Tatiana\', \'Tuning\', 83);\n\nSELECT COUNT(*) FROM student;\n+----------+\n| COUNT(*) |\n+----------+\n| 8 |\n+----------+\n\nSELECT COUNT(DISTINCT (name)) FROM student;\n+------------------------+\n| COUNT(DISTINCT (name)) |\n+------------------------+\n| 4 |\n+------------------------+\n\nURL: https://mariadb.com/kb/en/count-distinct/','','https://mariadb.com/kb/en/count-distinct/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (179,16,'GROUP_CONCAT','Syntax\n------\n\nGROUP_CONCAT(expr)\n\nDescription\n-----------\n\nThis function returns a string result with the concatenated non-NULL values\nfrom a group. It returns NULL if there are no non-NULL values.\n\nThe maximum returned length in bytes is determined by the group_concat_max_len\nserver system variable, which defaults to 1M (>= MariaDB 10.2.4) or 1K (<=\nMariaDB 10.2.3).\n\nIf group_concat_max_len <= 512, the return type is VARBINARY or VARCHAR;\notherwise, the return type is BLOB or TEXT. The choice between binary or\nnon-binary types depends from the input.\n\nThe full syntax is as follows:\n\nGROUP_CONCAT([DISTINCT] expr [,expr ...]\n [ORDER BY {unsigned_integer | col_name | expr}\n [ASC | DESC] [,col_name ...]]\n [SEPARATOR str_val]\n [LIMIT {[offset,] row_count | row_count OFFSET offset}])\n\nDISTINCT eliminates duplicate values from the output string.\n\nORDER BY determines the order of returned values.\n\nSEPARATOR specifies a separator between the values. The default separator is a\ncomma (,). It is possible to avoid using a separator by specifying an empty\nstring.\n\nLIMIT\n-----\n\nMariaDB starting with 10.3.3\n----------------------------\nUntil MariaDB 10.3.2, it was not possible to use the LIMIT clause with\nGROUP_CONCAT. This restriction was lifted in MariaDB 10.3.3.\n\nExamples\n--------\n\nSELECT student_name,\n GROUP_CONCAT(test_score)\n FROM student\n GROUP BY student_name;\n\nGet a readable list of MariaDB users from the mysql.user table:\n\nSELECT GROUP_CONCAT(DISTINCT User ORDER BY User SEPARATOR \'\\n\')\n FROM mysql.user;\n\nIn the former example, DISTINCT is used because the same user may occur more\nthan once. The new line (\\n) used as a SEPARATOR makes the results easier to\nread.\n\nGet a readable list of hosts from which each user can connect:\n\nSELECT User, GROUP_CONCAT(Host ORDER BY Host SEPARATOR \', \') \n FROM mysql.user GROUP BY User ORDER BY User;\n\nThe former example shows the difference between the GROUP_CONCAT\'s ORDER BY\n(which sorts the concatenated hosts), and the SELECT\'s ORDER BY (which sorts\nthe rows).\n\nFrom MariaDB 10.3.3, LIMIT can be used with GROUP_CONCAT, so, for example,\ngiven the following table:\n\nCREATE TABLE d (dd DATE, cc INT);\n\nINSERT INTO d VALUES (\'2017-01-01\',1);\nINSERT INTO d VALUES (\'2017-01-02\',2);\nINSERT INTO d VALUES (\'2017-01-04\',3);\n\nthe following query:\n\nSELECT SUBSTRING_INDEX(GROUP_CONCAT(CONCAT_WS(\":\",dd,cc) ORDER BY cc\nDESC),\",\",1) FROM d;\n+----------------------------------------------------------------------------+\n| SUBSTRING_INDEX(GROUP_CONCAT(CONCAT_WS(\":\",dd,cc) ORDER BY cc DESC),\",\",1) |\n+----------------------------------------------------------------------------+\n| 2017-01-04:3 |\n+----------------------------------------------------------------------------+\n\ncan be more simply rewritten as:\n\nSELECT GROUP_CONCAT(CONCAT_WS(\":\",dd,cc) ORDER BY cc DESC LIMIT 1) FROM d;\n+-------------------------------------------------------------+\n| GROUP_CONCAT(CONCAT_WS(\":\",dd,cc) ORDER BY cc DESC LIMIT 1) |\n+-------------------------------------------------------------+\n| 2017-01-04:3 |\n+-------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/group_concat/','','https://mariadb.com/kb/en/group_concat/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (180,16,'MAX','Syntax\n------\n\nMAX([DISTINCT] expr)\n\nDescription\n-----------\n\nReturns the largest, or maximum, value of expr. MAX() can also take a string\nargument in which case it returns the maximum string value. The DISTINCT\nkeyword can be used to find the maximum of the distinct values of expr,\nhowever, this produces the same result as omitting DISTINCT.\n\nNote that SET and ENUM fields are currently compared by their string value\nrather than their relative position in the set, so MAX() may produce a\ndifferent highest result than ORDER BY DESC.\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nMAX() can be used as a window function.\n\nMAX() returns NULL if there were no matching rows.\n\nExamples\n--------\n\nCREATE TABLE student (name CHAR(10), test CHAR(10), score TINYINT);\n\nINSERT INTO student VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87), (\'Tatiana\', \'Tuning\', 83);\n\nSELECT name, MAX(score) FROM student GROUP BY name;\n+---------+------------+\n| name | MAX(score) |\n+---------+------------+\n| Chun | 75 |\n| Esben | 43 |\n| Kaolin | 88 |\n| Tatiana | 87 |\n+---------+------------+\n\nMAX string:\n\nSELECT MAX(name) FROM student;\n+-----------+\n| MAX(name) |\n+-----------+\n| Tatiana |\n+-----------+\n\nBe careful to avoid this common mistake, not grouping correctly and returning\nmismatched data:\n\nSELECT name,test,MAX(SCORE) FROM student;\n+------+------+------------+\n| name | test | MAX(SCORE) |\n+------+------+------------+\n| Chun | SQL | 88 |\n+------+------+------------+\n\nDifference between ORDER BY DESC and MAX():\n\nCREATE TABLE student2(name CHAR(10),grade ENUM(\'b\',\'c\',\'a\'));\n\nINSERT INTO student2 VALUES(\'Chun\',\'b\'),(\'Esben\',\'c\'),(\'Kaolin\',\'a\');\n\nSELECT MAX(grade) FROM student2;\n+------------+\n| MAX(grade) |\n+------------+\n| c |\n+------------+\n\nSELECT grade FROM student2 ORDER BY grade DESC LIMIT 1;\n+-------+\n| grade |\n+-------+\n| a |\n+-------+\n\nAs a window function:\n\nCREATE OR REPLACE TABLE student_test (name CHAR(10), test CHAR(10), score\nTINYINT);\nINSERT INTO student_test VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87);\n\nSELECT name, test, score, MAX(score) \n OVER (PARTITION BY name) AS highest_score FROM student_test;\n+---------+--------+-------+---------------+\n| name | test | score | highest_score |\n+---------+--------+-------+---------------+\n| Chun | SQL | 75 | 75 |\n| Chun | Tuning | 73 | 75 |\n| Esben | SQL | 43 | 43 |\n| Esben | Tuning | 31 | 43 |\n| Kaolin | SQL | 56 | 88 |\n| Kaolin | Tuning | 88 | 88 |\n| Tatiana | SQL | 87 | 87 |\n+---------+--------+-------+---------------+\n\nURL: https://mariadb.com/kb/en/max/','','https://mariadb.com/kb/en/max/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (181,16,'MIN','Syntax\n------\n\nMIN([DISTINCT] expr)\n\nDescription\n-----------\n\nReturns the minimum value of expr. MIN() may take a string argument, in which\ncase it returns the minimum string value. The DISTINCT keyword can be used to\nfind the minimum of the distinct values of expr, however, this produces the\nsame result as omitting DISTINCT.\n\nNote that SET and ENUM fields are currently compared by their string value\nrather than their relative position in the set, so MIN() may produce a\ndifferent lowest result than ORDER BY ASC.\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nMIN() can be used as a window function.\n\nMIN() returns NULL if there were no matching rows.\n\nExamples\n--------\n\nCREATE TABLE student (name CHAR(10), test CHAR(10), score TINYINT);\n\nINSERT INTO student VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87), (\'Tatiana\', \'Tuning\', 83);\n\nSELECT name, MIN(score) FROM student GROUP BY name;\n+---------+------------+\n| name | MIN(score) |\n+---------+------------+\n| Chun | 73 |\n| Esben | 31 |\n| Kaolin | 56 |\n| Tatiana | 83 |\n+---------+------------+\n\nMIN() with a string:\n\nSELECT MIN(name) FROM student;\n+-----------+\n| MIN(name) |\n+-----------+\n| Chun |\n+-----------+\n\nBe careful to avoid this common mistake, not grouping correctly and returning\nmismatched data:\n\nSELECT name,test,MIN(score) FROM student;\n+------+------+------------+\n| name | test | MIN(score) |\n+------+------+------------+\n| Chun | SQL | 31 |\n+------+------+------------+\n\nDifference between ORDER BY ASC and MIN():\n\nCREATE TABLE student2(name CHAR(10),grade ENUM(\'b\',\'c\',\'a\'));\n\nINSERT INTO student2 VALUES(\'Chun\',\'b\'),(\'Esben\',\'c\'),(\'Kaolin\',\'a\');\n\nSELECT MIN(grade) FROM student2;\n+------------+\n| MIN(grade) |\n+------------+\n| a |\n+------------+\n\nSELECT grade FROM student2 ORDER BY grade ASC LIMIT 1;\n+-------+\n| grade |\n+-------+\n| b |\n+-------+\n\nAs a window function:\n\nCREATE OR REPLACE TABLE student_test (name CHAR(10), test CHAR(10), score\nTINYINT);\nINSERT INTO student_test VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87);\n\nSELECT name, test, score, MIN(score) \n OVER (PARTITION BY name) AS lowest_score FROM student_test;\n+---------+--------+-------+--------------+\n| name | test | score | lowest_score |\n+---------+--------+-------+--------------+\n| Chun | SQL | 75 | 73 |\n| Chun | Tuning | 73 | 73 |\n| Esben | SQL | 43 | 31 |\n| Esben | Tuning | 31 | 31 |\n| Kaolin | SQL | 56 | 56 |\n| Kaolin | Tuning | 88 | 56 |\n| Tatiana | SQL | 87 | 87 |\n+---------+--------+-------+--------------+\n\nURL: https://mariadb.com/kb/en/min/','','https://mariadb.com/kb/en/min/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (182,16,'STD','Syntax\n------\n\nSTD(expr)\n\nDescription\n-----------\n\nReturns the population standard deviation of expr. This is an extension to\nstandard SQL. The standard SQL function STDDEV_POP() can be used instead.\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nSTD() can be used as a window function.\n\nThis function returns NULL if there were no matching rows.\n\nExamples\n--------\n\nAs an aggregate function:\n\nCREATE OR REPLACE TABLE stats (category VARCHAR(2), x INT);\n\nINSERT INTO stats VALUES \n (\'a\',1),(\'a\',2),(\'a\',3),\n (\'b\',11),(\'b\',12),(\'b\',20),(\'b\',30),(\'b\',60);\n\nSELECT category, STDDEV_POP(x), STDDEV_SAMP(x), VAR_POP(x) \n FROM stats GROUP BY category;\n+----------+---------------+----------------+------------+\n| category | STDDEV_POP(x) | STDDEV_SAMP(x) | VAR_POP(x) |\n+----------+---------------+----------------+------------+\n| a | 0.8165 | 1.0000 | 0.6667 |\n| b | 18.0400 | 20.1693 | 325.4400 |\n+----------+---------------+----------------+------------+\n\nAs a window function:\n\nCREATE OR REPLACE TABLE student_test (name CHAR(10), test CHAR(10), score\nTINYINT);\n\nINSERT INTO student_test VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87);\n\nSELECT name, test, score, STDDEV_POP(score) \n OVER (PARTITION BY test) AS stddev_results FROM student_test;\n+---------+--------+-------+----------------+\n| name | test | score | stddev_results |\n+---------+--------+-------+----------------+\n| Chun | SQL | 75 | 16.9466 |\n| Chun | Tuning | 73 | 24.1247 |\n| Esben | SQL | 43 | 16.9466 |\n| Esben | Tuning | 31 | 24.1247 |\n| Kaolin | SQL | 56 | 16.9466 |\n| Kaolin | Tuning | 88 | 24.1247 |\n| Tatiana | SQL | 87 | 16.9466 |\n+---------+--------+-------+----------------+\n\nURL: https://mariadb.com/kb/en/std/','','https://mariadb.com/kb/en/std/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (183,16,'STDDEV','Syntax\n------\n\nSTDDEV(expr)\n\nDescription\n-----------\n\nReturns the population standard deviation of expr. This function is provided\nfor compatibility with Oracle. The standard SQL function STDDEV_POP() can be\nused instead.\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nSTDDEV() can be used as a window function.\n\nThis function returns NULL if there were no matching rows.\n\nExamples\n--------\n\nAs an aggregate function:\n\nCREATE OR REPLACE TABLE stats (category VARCHAR(2), x INT);\n\nINSERT INTO stats VALUES \n (\'a\',1),(\'a\',2),(\'a\',3),\n (\'b\',11),(\'b\',12),(\'b\',20),(\'b\',30),(\'b\',60);\n\nSELECT category, STDDEV_POP(x), STDDEV_SAMP(x), VAR_POP(x) \n FROM stats GROUP BY category;\n+----------+---------------+----------------+------------+\n| category | STDDEV_POP(x) | STDDEV_SAMP(x) | VAR_POP(x) |\n+----------+---------------+----------------+------------+\n| a | 0.8165 | 1.0000 | 0.6667 |\n| b | 18.0400 | 20.1693 | 325.4400 |\n+----------+---------------+----------------+------------+\n\nAs a window function:\n\nCREATE OR REPLACE TABLE student_test (name CHAR(10), test CHAR(10), score\nTINYINT);\n\nINSERT INTO student_test VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87);\n\nSELECT name, test, score, STDDEV_POP(score) \n OVER (PARTITION BY test) AS stddev_results FROM student_test;\n+---------+--------+-------+----------------+\n| name | test | score | stddev_results |\n+---------+--------+-------+----------------+\n| Chun | SQL | 75 | 16.9466 |\n| Chun | Tuning | 73 | 24.1247 |\n| Esben | SQL | 43 | 16.9466 |\n| Esben | Tuning | 31 | 24.1247 |\n| Kaolin | SQL | 56 | 16.9466 |\n| Kaolin | Tuning | 88 | 24.1247 |\n| Tatiana | SQL | 87 | 16.9466 |\n+---------+--------+-------+----------------+\n\nURL: https://mariadb.com/kb/en/stddev/','','https://mariadb.com/kb/en/stddev/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (184,16,'STDDEV_POP','Syntax\n------\n\nSTDDEV_POP(expr)\n\nDescription\n-----------\n\nReturns the population standard deviation of expr (the square root of\nVAR_POP()). You can also use STD() or STDDEV(), which are equivalent but not\nstandard SQL.\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nSTDDEV_POP() can be used as a window function.\n\nSTDDEV_POP() returns NULL if there were no matching rows.\n\nExamples\n--------\n\nAs an aggregate function:\n\nCREATE OR REPLACE TABLE stats (category VARCHAR(2), x INT);\n\nINSERT INTO stats VALUES \n (\'a\',1),(\'a\',2),(\'a\',3),\n (\'b\',11),(\'b\',12),(\'b\',20),(\'b\',30),(\'b\',60);\n\nSELECT category, STDDEV_POP(x), STDDEV_SAMP(x), VAR_POP(x) \n FROM stats GROUP BY category;\n+----------+---------------+----------------+------------+\n| category | STDDEV_POP(x) | STDDEV_SAMP(x) | VAR_POP(x) |\n+----------+---------------+----------------+------------+\n| a | 0.8165 | 1.0000 | 0.6667 |\n| b | 18.0400 | 20.1693 | 325.4400 |\n+----------+---------------+----------------+------------+\n\nAs a window function:\n\nCREATE OR REPLACE TABLE student_test (name CHAR(10), test CHAR(10), score\nTINYINT);\n\nINSERT INTO student_test VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87);\n\nSELECT name, test, score, STDDEV_POP(score) \n OVER (PARTITION BY test) AS stddev_results FROM student_test;\n+---------+--------+-------+----------------+\n| name | test | score | stddev_results |\n+---------+--------+-------+----------------+\n| Chun | SQL | 75 | 16.9466 |\n| Chun | Tuning | 73 | 24.1247 |\n| Esben | SQL | 43 | 16.9466 |\n| Esben | Tuning | 31 | 24.1247 |\n| Kaolin | SQL | 56 | 16.9466 |\n| Kaolin | Tuning | 88 | 24.1247 |\n| Tatiana | SQL | 87 | 16.9466 |\n+---------+--------+-------+----------------+\n\nURL: https://mariadb.com/kb/en/stddev_pop/','','https://mariadb.com/kb/en/stddev_pop/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (185,16,'STDDEV_SAMP','Syntax\n------\n\nSTDDEV_SAMP(expr)\n\nDescription\n-----------\n\nReturns the sample standard deviation of expr (the square root of VAR_SAMP()).\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nSTDDEV_SAMP() can be used as a window function.\n\nSTDDEV_SAMP() returns NULL if there were no matching rows.\n\nURL: https://mariadb.com/kb/en/stddev_samp/','','https://mariadb.com/kb/en/stddev_samp/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (186,16,'SUM','Syntax\n------\n\nSUM([DISTINCT] expr)\n\nDescription\n-----------\n\nReturns the sum of expr. If the return set has no rows, SUM() returns NULL.\nThe DISTINCT keyword can be used to sum only the distinct values of expr.\n\nSUM() can be used as a window function, although not with the DISTINCT\nspecifier.\n\nExamples\n--------\n\nCREATE TABLE sales (sales_value INT);\nINSERT INTO sales VALUES(10),(20),(20),(40);\n\nSELECT SUM(sales_value) FROM sales;\n+------------------+\n| SUM(sales_value) |\n+------------------+\n| 90 |\n+------------------+\n\nSELECT SUM(DISTINCT(sales_value)) FROM sales;\n+----------------------------+\n| SUM(DISTINCT(sales_value)) |\n+----------------------------+\n| 70 |\n+----------------------------+\n\nCommonly, SUM is used with a GROUP BY clause:\n\nCREATE TABLE sales (name CHAR(10), month CHAR(10), units INT);\n\nINSERT INTO sales VALUES \n (\'Chun\', \'Jan\', 75), (\'Chun\', \'Feb\', 73),\n (\'Esben\', \'Jan\', 43), (\'Esben\', \'Feb\', 31),\n (\'Kaolin\', \'Jan\', 56), (\'Kaolin\', \'Feb\', 88),\n (\'Tatiana\', \'Jan\', 87), (\'Tatiana\', \'Feb\', 83);\n\nSELECT name, SUM(units) FROM sales GROUP BY name;\n+---------+------------+\n| name | SUM(units) |\n+---------+------------+\n| Chun | 148 |\n| Esben | 74 |\n| Kaolin | 144 |\n| Tatiana | 170 |\n+---------+------------+\n\nThe GROUP BY clause is required when using an aggregate function along with\nregular column data, otherwise the result will be a mismatch, as in the\nfollowing common type of mistake:\n\nSELECT name,SUM(units) FROM sales\n;+------+------------+\n| name | SUM(units) |\n+------+------------+\n| Chun | 536 |\n+------+------------+\n\nAs a window function:\n\nCREATE OR REPLACE TABLE student_test (name CHAR(10), test CHAR(10), score\nTINYINT);\nINSERT INTO student_test VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87);\n\nSELECT name, test, score, SUM(score) OVER (PARTITION BY name) AS total_score\nFROM student_test;\n+---------+--------+-------+-------------+\n| name | test | score | total_score |\n+---------+--------+-------+-------------+\n| Chun | SQL | 75 | 148 |\n| Chun | Tuning | 73 | 148 |\n| Esben | SQL | 43 | 74 |\n| Esben | Tuning | 31 | 74 |\n| Kaolin | SQL | 56 | 144 |\n| Kaolin | Tuning | 88 | 144 |\n| Tatiana | SQL | 87 | 87 |\n+---------+--------+-------+-------------+\n\nURL: https://mariadb.com/kb/en/sum/','','https://mariadb.com/kb/en/sum/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (187,16,'VARIANCE','Syntax\n------\n\nVARIANCE(expr)\n\nDescription\n-----------\n\nReturns the population standard variance of expr. This is an extension to\nstandard SQL. The standard SQL function VAR_POP() can be used instead.\n\nVariance is calculated by\n\n* working out the mean for the set\n* for each number, subtracting the mean and squaring the result\n* calculate the average of the resulting differences\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nVARIANCE() can be used as a window function.\n\nVARIANCE() returns NULL if there were no matching rows.\n\nExamples\n--------\n\nCREATE TABLE v(i tinyint);\n\nINSERT INTO v VALUES(101),(99);\n\nSELECT VARIANCE(i) FROM v;\n+-------------+\n| VARIANCE(i) |\n+-------------+\n| 1.0000 |\n+-------------+\n\nINSERT INTO v VALUES(120),(80);\n\nSELECT VARIANCE(i) FROM v;\n+-------------+\n| VARIANCE(i) |\n+-------------+\n| 200.5000 |\n+-------------+\n\nAs an aggregate function:\n\nCREATE OR REPLACE TABLE stats (category VARCHAR(2), x INT);\n\nINSERT INTO stats VALUES \n (\'a\',1),(\'a\',2),(\'a\',3),\n (\'b\',11),(\'b\',12),(\'b\',20),(\'b\',30),(\'b\',60);\n\nSELECT category, STDDEV_POP(x), STDDEV_SAMP(x), VAR_POP(x) \n FROM stats GROUP BY category;\n+----------+---------------+----------------+------------+\n| category | STDDEV_POP(x) | STDDEV_SAMP(x) | VAR_POP(x) |\n+----------+---------------+----------------+------------+\n| a | 0.8165 | 1.0000 | 0.6667 |\n| b | 18.0400 | 20.1693 | 325.4400 |\n+----------+---------------+----------------+------------+\n\nAs a window function:\n\nCREATE OR REPLACE TABLE student_test (name CHAR(10), test CHAR(10), score\nTINYINT);\n\nINSERT INTO student_test VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87);\n\nSELECT name, test, score, VAR_POP(score) \n OVER (PARTITION BY test) AS variance_results FROM student_test;\n+---------+--------+-------+------------------+\n| name | test | score | variance_results |\n+---------+--------+-------+------------------+\n| Chun | SQL | 75 | 287.1875 |\n| Chun | Tuning | 73 | 582.0000 |\n| Esben | SQL | 43 | 287.1875 |\n| Esben | Tuning | 31 | 582.0000 |\n| Kaolin | SQL | 56 | 287.1875 |\n| Kaolin | Tuning | 88 | 582.0000 |\n| Tatiana | SQL | 87 | 287.1875 |\n+---------+--------+-------+------------------+\n\nURL: https://mariadb.com/kb/en/variance/','','https://mariadb.com/kb/en/variance/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (188,16,'VAR_POP','Syntax\n------\n\nVAR_POP(expr)\n\nDescription\n-----------\n\nReturns the population standard variance of expr. It considers rows as the\nwhole population, not as a sample, so it has the number of rows as the\ndenominator. You can also use VARIANCE(), which is equivalent but is not\nstandard SQL.\n\nVariance is calculated by\n\n* working out the mean for the set\n* for each number, subtracting the mean and squaring the result\n* calculate the average of the resulting differences\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nVAR_POP() can be used as a window function.\n\nVAR_POP() returns NULL if there were no matching rows.\n\nExamples\n--------\n\nCREATE TABLE v(i tinyint);\n\nINSERT INTO v VALUES(101),(99);\n\nSELECT VAR_POP(i) FROM v;\n+------------+\n| VAR_POP(i) |\n+------------+\n| 1.0000 |\n+------------+\n\nINSERT INTO v VALUES(120),(80);\n\nSELECT VAR_POP(i) FROM v;\n+------------+\n| VAR_POP(i) |\n+------------+\n| 200.5000 |\n+------------+\n\nAs an aggregate function:\n\nCREATE OR REPLACE TABLE stats (category VARCHAR(2), x INT);\n\nINSERT INTO stats VALUES \n (\'a\',1),(\'a\',2),(\'a\',3),\n (\'b\',11),(\'b\',12),(\'b\',20),(\'b\',30),(\'b\',60);\n\nSELECT category, STDDEV_POP(x), STDDEV_SAMP(x), VAR_POP(x) \n FROM stats GROUP BY category;\n+----------+---------------+----------------+------------+\n| category | STDDEV_POP(x) | STDDEV_SAMP(x) | VAR_POP(x) |\n+----------+---------------+----------------+------------+\n| a | 0.8165 | 1.0000 | 0.6667 |\n| b | 18.0400 | 20.1693 | 325.4400 |\n+----------+---------------+----------------+------------+\n\nAs a window function:\n\nCREATE OR REPLACE TABLE student_test (name CHAR(10), test CHAR(10), score\nTINYINT);\n\nINSERT INTO student_test VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87);\n\nSELECT name, test, score, VAR_POP(score) \n OVER (PARTITION BY test) AS variance_results FROM student_test;\n+---------+--------+-------+------------------+\n| name | test | score | variance_results |\n+---------+--------+-------+------------------+\n| Chun | SQL | 75 | 287.1875 |\n| Esben | SQL | 43 | 287.1875 |\n| Kaolin | SQL | 56 | 287.1875 |\n| Tatiana | SQL | 87 | 287.1875 |\n| Chun | Tuning | 73 | 582.0000 |\n| Esben | Tuning | 31 | 582.0000 |\n| Kaolin | Tuning | 88 | 582.0000 |\n+---------+--------+-------+------------------+\n\nURL: https://mariadb.com/kb/en/var_pop/','','https://mariadb.com/kb/en/var_pop/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (189,16,'VAR_SAMP','Syntax\n------\n\nVAR_SAMP(expr)\n\nDescription\n-----------\n\nReturns the sample variance of expr. That is, the denominator is the number of\nrows minus one.\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nVAR_SAMP() can be used as a window function.\n\nVAR_SAMP() returns NULL if there were no matching rows.\n\nExamples\n--------\n\nAs an aggregate function:\n\nCREATE OR REPLACE TABLE stats (category VARCHAR(2), x INT);\n\nINSERT INTO stats VALUES \n (\'a\',1),(\'a\',2),(\'a\',3),\n (\'b\',11),(\'b\',12),(\'b\',20),(\'b\',30),(\'b\',60);\n\nSELECT category, STDDEV_POP(x), STDDEV_SAMP(x), VAR_POP(x) \n FROM stats GROUP BY category;\n+----------+---------------+----------------+------------+\n| category | STDDEV_POP(x) | STDDEV_SAMP(x) | VAR_POP(x) |\n+----------+---------------+----------------+------------+\n| a | 0.8165 | 1.0000 | 0.6667 |\n| b | 18.0400 | 20.1693 | 325.4400 |\n+----------+---------------+----------------+------------+\n\nAs a window function:\n\nCREATE OR REPLACE TABLE student_test (name CHAR(10), test CHAR(10), score\nTINYINT);\n\nINSERT INTO student_test VALUES \n (\'Chun\', \'SQL\', 75), (\'Chun\', \'Tuning\', 73),\n (\'Esben\', \'SQL\', 43), (\'Esben\', \'Tuning\', 31),\n (\'Kaolin\', \'SQL\', 56), (\'Kaolin\', \'Tuning\', 88),\n (\'Tatiana\', \'SQL\', 87);\n\nSELECT name, test, score, VAR_SAMP(score) \n OVER (PARTITION BY test) AS variance_results FROM student_test;\n+---------+--------+-------+------------------+\n| name | test | score | variance_results |\n+---------+--------+-------+------------------+\n| Chun | SQL | 75 | 382.9167 |\n| Chun | Tuning | 73 | 873.0000 |\n| Esben | SQL | 43 | 382.9167 |\n| Esben | Tuning | 31 | 873.0000 |\n| Kaolin | SQL | 56 | 382.9167 |\n| Kaolin | Tuning | 88 | 873.0000 |\n| Tatiana | SQL | 87 | 382.9167 |\n+---------+--------+-------+------------------+\n\nURL: https://mariadb.com/kb/en/var_samp/','','https://mariadb.com/kb/en/var_samp/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (190,17,'BENCHMARK','Syntax\n------\n\nBENCHMARK(count,expr)\n\nDescription\n-----------\n\nThe BENCHMARK() function executes the expression expr repeatedly count times.\nIt may be used to time how quickly MariaDB processes the expression. The\nresult value is always 0. The intended use is from within the mysql client,\nwhich reports query execution times.\n\nExamples\n--------\n\nSELECT BENCHMARK(1000000,ENCODE(\'hello\',\'goodbye\'));\n+----------------------------------------------+\n| BENCHMARK(1000000,ENCODE(\'hello\',\'goodbye\')) |\n+----------------------------------------------+\n| 0 |\n+----------------------------------------------+\n1 row in set (0.21 sec)\n\nURL: https://mariadb.com/kb/en/benchmark/','','https://mariadb.com/kb/en/benchmark/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (191,17,'BINLOG_GTID_POS','Syntax\n------\n\nBINLOG_GTID_POS(binlog_filename,binlog_offset)\n\nDescription\n-----------\n\nThe BINLOG_GTID_POS() function takes as input an old-style binary log position\nin the form of a file name and a file offset. It looks up the position in the\ncurrent binlog, and returns a string representation of the corresponding GTID\nposition. If the position is not found in the current binlog, NULL is returned.\n\nExamples\n--------\n\nSELECT BINLOG_GTID_POS(\"master-bin.000001\", 600);\n\nURL: https://mariadb.com/kb/en/binlog_gtid_pos/','','https://mariadb.com/kb/en/binlog_gtid_pos/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (192,17,'CHARSET','Syntax\n------\n\nCHARSET(str)\n\nDescription\n-----------\n\nReturns the character set of the string argument. If str is not a string, it\nis considered as a binary string (so the function returns \'binary\'). This\napplies to NULL, too. The return value is a string in the utf8 character set.\n\nExamples\n--------\n\nSELECT CHARSET(\'abc\');\n+----------------+\n| CHARSET(\'abc\') |\n+----------------+\n| latin1 |\n+----------------+\n\nSELECT CHARSET(CONVERT(\'abc\' USING utf8));\n+------------------------------------+\n| CHARSET(CONVERT(\'abc\' USING utf8)) |\n+------------------------------------+\n| utf8 |\n+------------------------------------+\n\nSELECT CHARSET(USER());\n+-----------------+\n| CHARSET(USER()) |\n+-----------------+\n| utf8 |\n+-----------------+\n\nURL: https://mariadb.com/kb/en/charset/','','https://mariadb.com/kb/en/charset/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (193,17,'COERCIBILITY','Syntax\n------\n\nCOERCIBILITY(str)\n\nDescription\n-----------\n\nReturns the collation coercibility value of the string argument. Coercibility\ndefines what will be converted to what in case of collation conflict, with an\nexpression with higher coercibility being converted to the collation of an\nexpression with lower coercibility.\n\n+-----------------------------+---------------------------+------------------+\n| Coercibility | Description | Example |\n+-----------------------------+---------------------------+------------------+\n| 0 | Explicit | Value using a |\n| | | COLLATE clause |\n+-----------------------------+---------------------------+------------------+\n| 1 | No collation | Concatenated |\n| | | strings using |\n| | | different |\n| | | collations |\n+-----------------------------+---------------------------+------------------+\n| 2 | Implicit | Column value |\n+-----------------------------+---------------------------+------------------+\n| 3 | Constant | USER() return |\n| | | value |\n+-----------------------------+---------------------------+------------------+\n| 4 | Coercible | Literal string |\n+-----------------------------+---------------------------+------------------+\n| 5 | Ignorable | NULL or derived |\n| | | from NULL |\n+-----------------------------+---------------------------+------------------+\n\nExamples\n--------\n\nSELECT COERCIBILITY(\'abc\' COLLATE latin1_swedish_ci);\n+-----------------------------------------------+\n| COERCIBILITY(\'abc\' COLLATE latin1_swedish_ci) |\n+-----------------------------------------------+\n| 0 |\n+-----------------------------------------------+\n\nSELECT COERCIBILITY(USER());\n+----------------------+\n| COERCIBILITY(USER()) |\n+----------------------+\n| 3 |\n+----------------------+\n\nSELECT COERCIBILITY(\'abc\');\n+---------------------+\n| COERCIBILITY(\'abc\') |\n+---------------------+\n| 4 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/coercibility/','','https://mariadb.com/kb/en/coercibility/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (194,17,'COLLATION','Syntax\n------\n\nCOLLATION(str)\n\nDescription\n-----------\n\nReturns the collation of the string argument. If str is not a string, it is\nconsidered as a binary string (so the function returns \'binary\'). This applies\nto NULL, too. The return value is a string in the utf8 character set.\n\nSee Character Sets and Collations.\n\nExamples\n--------\n\nSELECT COLLATION(\'abc\');\n+-------------------+\n| COLLATION(\'abc\') |\n+-------------------+\n| latin1_swedish_ci |\n+-------------------+\n\nSELECT COLLATION(_utf8\'abc\');\n+-----------------------+\n| COLLATION(_utf8\'abc\') |\n+-----------------------+\n| utf8_general_ci |\n+-----------------------+\n\nURL: https://mariadb.com/kb/en/collation/','','https://mariadb.com/kb/en/collation/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (195,17,'CONNECTION_ID','Syntax\n------\n\nCONNECTION_ID()\n\nDescription\n-----------\n\nReturns the connection ID for the connection. Every connection (including\nevents) has an ID that is unique among the set of currently connected clients.\n\nUntil MariaDB 10.3.1, returns MYSQL_TYPE_LONGLONG, or bigint(10), in all\ncases. From MariaDB 10.3.1, returns MYSQL_TYPE_LONG, or int(10), when the\nresult would fit within 32-bits.\n\nExamples\n--------\n\nSELECT CONNECTION_ID();\n+-----------------+\n| CONNECTION_ID() |\n+-----------------+\n| 3 |\n+-----------------+\n\nURL: https://mariadb.com/kb/en/connection_id/','','https://mariadb.com/kb/en/connection_id/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (196,17,'CURRENT_ROLE','Syntax\n------\n\nCURRENT_ROLE, CURRENT_ROLE()\n\nDescription\n-----------\n\nReturns the current role name. This determines your access privileges. The\nreturn value is a string in the utf8 character set.\n\nIf there is no current role, NULL is returned.\n\nThe output of SELECT CURRENT_ROLE is equivalent to the contents of the\nENABLED_ROLES Information Schema table.\n\nUSER() returns the combination of user and host used to login. CURRENT_USER()\nreturns the account used to determine current connection\'s privileges.\n\nExamples\n--------\n\nSELECT CURRENT_ROLE;\n+--------------+\n| CURRENT_ROLE |\n+--------------+\n| NULL |\n+--------------+\n\nSET ROLE staff;\n\nSELECT CURRENT_ROLE;\n+--------------+\n| CURRENT_ROLE |\n+--------------+\n| staff |\n+--------------+\n\nURL: https://mariadb.com/kb/en/current_role/','','https://mariadb.com/kb/en/current_role/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (197,17,'CURRENT_USER','Syntax\n------\n\nCURRENT_USER, CURRENT_USER()\n\nDescription\n-----------\n\nReturns the user name and host name combination for the MariaDB account that\nthe server used to authenticate the current client. This account determines\nyour access privileges. The return value is a string in the utf8 character set.\n\nThe value of CURRENT_USER() can differ from the value of USER().\nCURRENT_ROLE() returns the current active role.\n\nExamples\n--------\n\nshell> mysql --user=\"anonymous\"\n\nselect user(),current_user();\n+---------------------+----------------+\n| user() | current_user() |\n+---------------------+----------------+\n| anonymous@localhost | @localhost |\n+---------------------+----------------+\n\nWhen calling CURRENT_USER() in a stored procedure, it returns the owner of the\nstored procedure, as defined with DEFINER.\n\nURL: https://mariadb.com/kb/en/current_user/','','https://mariadb.com/kb/en/current_user/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (198,17,'DATABASE','Syntax\n------\n\nDATABASE()\n\nDescription\n-----------\n\nReturns the default (current) database name as a string in the utf8 character\nset. If there is no default database, DATABASE() returns NULL. Within a stored\nroutine, the default database is the database that the routine is associated\nwith, which is not necessarily the same as the database that is the default in\nthe calling context.\n\nSCHEMA() is a synonym for DATABASE().\n\nTo select a default database, the USE statement can be run. Another way to set\nthe default database is specifying its name at mysql command line client\nstartup.\n\nExamples\n--------\n\nSELECT DATABASE();\n+------------+\n| DATABASE() |\n+------------+\n| NULL |\n+------------+\n\nUSE test;\nDatabase changed\n\nSELECT DATABASE();\n+------------+\n| DATABASE() |\n+------------+\n| test |\n+------------+\n\nURL: https://mariadb.com/kb/en/database/','','https://mariadb.com/kb/en/database/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (199,17,'DECODE_HISTOGRAM','Syntax\n------\n\nDECODE_HISTOGRAM(hist_type,histogram)\n\nDescription\n-----------\n\nReturns a string of comma separated numeric values corresponding to a\nprobability distribution represented by the histogram of type hist_type\n(SINGLE_PREC_HB or DOUBLE_PREC_HB). The hist_type and histogram would be\ncommonly used from the mysql.column_stats table.\n\nSee Histogram Based Statistics for details.\n\nExamples\n--------\n\nCREATE TABLE origin (\n i INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n v INT UNSIGNED NOT NULL\n);\n\nINSERT INTO origin(v) VALUES \n (1),(2),(3),(4),(5),(10),(20),\n (30),(40),(50),(60),(70),(80),\n (90),(100),(200),(400),(800);\n\nSET histogram_size=10,histogram_type=SINGLE_PREC_HB;\n\nANALYZE TABLE origin PERSISTENT FOR ALL;\n+-------------+---------+----------+-----------------------------------------+\n| Table | Op | Msg_type | Msg_text |\n+-------------+---------+----------+-----------------------------------------+\n| test.origin | analyze | status | Engine-independent statistics collected |\n| test.origin | analyze | status | OK |\n+-------------+---------+----------+-----------------------------------------+\n\nSELECT db_name,table_name,column_name,hist_type,\n hex(histogram),decode_histogram(hist_type,histogram)\n FROM mysql.column_stats WHERE db_name=\'test\' and table_name=\'origin\';\n+---------+------------+-------------+----------------+----------------------+-\n-----------------------------------------------------------------+\n| db_name | table_name | column_name | hist_type | hex(histogram) |\ndecode_histogram(hist_type,histogram) |\n+---------+------------+-------------+----------------+----------------------+-\n-----------------------------------------------------------------+\n| test | origin | i | SINGLE_PREC_HB | 0F2D3C5A7887A5C3D2F0 |\n0.059,0.118,0.059,0.118,0.118,0.059,0.118,0.118,0.059,0.118,0.059 |\n| test | origin | v | SINGLE_PREC_HB | 000001060C0F161C1F7F |\n0.000,0.000,0.004,0.020,0.024,0.012,0.027,0.024,0.012,0.376,0.502 |\n+---------+------------+-------------+----------------+----------------------+-\n-----------------------------------------------------------------+\n\nSET histogram_size=20,histogram_type=DOUBLE_PREC_HB;\n\nANALYZE TABLE origin PERSISTENT FOR ALL;\n+-------------+---------+----------+-----------------------------------------+\n| Table | Op | Msg_type | Msg_text |\n+-------------+---------+----------+-----------------------------------------+\n| test.origin | analyze | status | Engine-independent statistics collected |\n| test.origin | analyze | status | OK |\n+-------------+---------+----------+-----------------------------------------+\n\nSELECT db_name,table_name,column_name,\n hist_type,hex(histogram),decode_histogram(hist_type,histogram)\n FROM mysql.column_stats WHERE db_name=\'test\' and table_name=\'origin\';\n+---------+------------+-------------+----------------+------------------------\n-----------------+-------------------------------------------------------------\n---------------------------+\n| db_name | table_name | column_name | hist_type | hex(histogram) \n | decode_histogram(hist_type,histogram)\n |\n+---------+------------+-------------+----------------+------------------------\n-----------------+-------------------------------------------------------------\n---------------------------+\n| test | origin | i | DOUBLE_PREC_HB |\n0F0F2D2D3C3C5A5A78788787A5A5C3C3D2D2F0F0 |\n0.05882,0.11765,0.05882,0.11765,0.11765,0.05882,0.11765,0.11765,0.05882,0.11765\n0.05882 |\n| test | origin | v | DOUBLE_PREC_HB |\n5200F600480116067E0CB30F1B16831CB81FD67F |\n0.00125,0.00250,0.00125,0.01877,0.02502,0.01253,0.02502,0.02502,0.01253,0.37546\n0.50063 |\n\nURL: https://mariadb.com/kb/en/decode_histogram/','','https://mariadb.com/kb/en/decode_histogram/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (200,17,'DEFAULT','Syntax\n------\n\nDEFAULT(col_name)\n\nDescription\n-----------\n\nReturns the default value for a table column. If the column has no default\nvalue (and is not NULLABLE - NULLABLE fields have a NULL default), an error is\nreturned.\n\nFor integer columns using AUTO_INCREMENT, 0 is returned.\n\nWhen using DEFAULT as a value to set in an INSERT or UPDATE statement, you can\nuse the bare keyword DEFAULT without the parentheses and argument to refer to\nthe column in context. You can only use DEFAULT as a bare keyword if you are\nusing it alone without a surrounding expression or function.\n\nExamples\n--------\n\nSelect only non-default values for a column:\n\nSELECT i FROM t WHERE i != DEFAULT(i);\n\nUpdate values to be one greater than the default value:\n\nUPDATE t SET i = DEFAULT(i)+1 WHERE i < 100;\n\nWhen referring to the default value exactly in UPDATE or INSERT, you can omit\nthe argument:\n\nINSERT INTO t (i) VALUES (DEFAULT);\nUPDATE t SET i = DEFAULT WHERE i < 100;\n\nCREATE OR REPLACE TABLE t (\n i INT NOT NULL AUTO_INCREMENT,\n j INT NOT NULL,\n k INT DEFAULT 3,\n l INT NOT NULL DEFAULT 4,\n m INT,\n PRIMARY KEY (i)\n);\n\nDESC t;\n+-------+---------+------+-----+---------+----------------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+---------+------+-----+---------+----------------+\n| i | int(11) | NO | PRI | NULL | auto_increment |\n| j | int(11) | NO | | NULL | |\n| k | int(11) | YES | | 3 | |\n| l | int(11) | NO | | 4 | |\n| m | int(11) | YES | | NULL | |\n+-------+---------+------+-----+---------+----------------+\n\nINSERT INTO t (j) VALUES (1);\nINSERT INTO t (j,m) VALUES (2,2);\nINSERT INTO t (j,l,m) VALUES (3,3,3);\n\nSELECT * FROM t;\n+---+---+------+---+------+\n| i | j | k | l | m |\n+---+---+------+---+------+\n| 1 | 1 | 3 | 4 | NULL |\n| 2 | 2 | 3 | 4 | 2 |\n| 3 | 3 | 3 | 3 | 3 |\n+---+---+------+---+------+\n\nSELECT DEFAULT(i), DEFAULT(k), DEFAULT (l), DEFAULT(m) FROM t;\n+------------+------------+-------------+------------+\n| DEFAULT(i) | DEFAULT(k) | DEFAULT (l) | DEFAULT(m) |\n+------------+------------+-------------+------------+\n| 0 | 3 | 4 | NULL |\n| 0 | 3 | 4 | NULL |\n| 0 | 3 | 4 | NULL |\n+------------+------------+-------------+------------+\n\nSELECT DEFAULT(i), DEFAULT(k), DEFAULT (l), DEFAULT(m), DEFAULT(j) FROM t;\nERROR 1364 (HY000): Field \'j\' doesn\'t have a default value\n\nSELECT * FROM t WHERE i = DEFAULT(i);\nEmpty set (0.001 sec)\n\nSELECT * FROM t WHERE j = DEFAULT(j);\nERROR 1364 (HY000): Field \'j\' doesn\'t have a default value\n\nSELECT * FROM t WHERE k = DEFAULT(k);\n+---+---+------+---+------+\n| i | j | k | l | m |\n+---+---+------+---+------+\n| 1 | 1 | 3 | 4 | NULL |\n| 2 | 2 | 3 | 4 | 2 |\n| 3 | 3 | 3 | 3 | 3 |\n+---+---+------+---+------+\n\nSELECT * FROM t WHERE l = DEFAULT(l);\n+---+---+------+---+------+\n| i | j | k | l | m |\n+---+---+------+---+------+\n| 1 | 1 | 3 | 4 | NULL |\n| 2 | 2 | 3 | 4 | 2 |\n+---+---+------+---+------+\n\nSELECT * FROM t WHERE m = DEFAULT(m);\nEmpty set (0.001 sec)\n\nSELECT * FROM t WHERE m <=> DEFAULT(m);\n+---+---+------+---+------+\n| i | j | k | l | m |\n+---+---+------+---+------+\n| 1 | 1 | 3 | 4 | NULL |\n+---+---+------+---+------+\n\nURL: https://mariadb.com/kb/en/default/','','https://mariadb.com/kb/en/default/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (201,17,'FOUND_ROWS','Syntax\n------\n\nFOUND_ROWS()\n\nDescription\n-----------\n\nA SELECT statement may include a LIMIT clause to restrict the number of rows\nthe server returns to the client. In some cases, it is desirable to know how\nmany rows the statement would have returned without the LIMIT, but without\nrunning the statement again. To obtain this row count, include a\nSQL_CALC_FOUND_ROWS option in the SELECT statement, and then invoke\nFOUND_ROWS() afterwards.\n\nYou can also use FOUND_ROWS() to obtain the number of rows returned by a\nSELECT which does not contain a LIMIT clause. In this case you don\'t need to\nuse the SQL_CALC_FOUND_ROWS option. This can be useful for example in a stored\nprocedure.\n\nAlso, this function works with some other statements which return a resultset,\nincluding SHOW, DESC and HELP. For DELETE ... RETURNING you should use\nROW_COUNT(). It also works as a prepared statement, or after executing a\nprepared statement.\n\nStatements which don\'t return any results don\'t affect FOUND_ROWS() - the\nprevious value will still be returned.\n\nWarning: When used after a CALL statement, this function returns the number of\nrows selected by the last query in the procedure, not by the whole procedure.\n\nStatements using the FOUND_ROWS() function are not safe for replication.\n\nExamples\n--------\n\nSHOW ENGINES\\G\n*************************** 1. row ***************************\n Engine: CSV\n Support: YES\n Comment: Stores tables as CSV files\nTransactions: NO\n XA: NO\n Savepoints: NO\n*************************** 2. row ***************************\n Engine: MRG_MyISAM\n Support: YES\n Comment: Collection of identical MyISAM tables\nTransactions: NO\n XA: NO\n Savepoints: NO\n\n...\n\n*************************** 8. row ***************************\n Engine: PERFORMANCE_SCHEMA\n Support: YES\n Comment: Performance Schema\nTransactions: NO\n XA: NO\n Savepoints: NO\n8 rows in set (0.000 sec)\n\nSELECT FOUND_ROWS();\n+--------------+\n| FOUND_ROWS() |\n+--------------+\n| 8 |\n+--------------+\n\nSELECT SQL_CALC_FOUND_ROWS * FROM tbl_name WHERE id > 100 LIMIT 10;\n\nSELECT FOUND_ROWS();\n+--------------+\n| FOUND_ROWS() |\n+--------------+\n| 23 |\n+--------------+\n\nURL: https://mariadb.com/kb/en/found_rows/','','https://mariadb.com/kb/en/found_rows/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (202,17,'LAST_INSERT_ID','Syntax\n------\n\nLAST_INSERT_ID(), LAST_INSERT_ID(expr)\n\nDescription\n-----------\n\nLAST_INSERT_ID() (no arguments) returns the first automatically generated\nvalue successfully inserted for an AUTO_INCREMENT column as a result of the\nmost recently executed INSERT statement. The value of LAST_INSERT_ID() remains\nunchanged if no rows are successfully inserted.\n\nIf one gives an argument to LAST_INSERT_ID(), then it will return the value of\nthe expression and the next call to LAST_INSERT_ID() will return the same\nvalue. The value will also be sent to the client and can be accessed by the\nmysql_insert_id function.\n\nFor example, after inserting a row that generates an AUTO_INCREMENT value, you\ncan get the value like this:\n\nSELECT LAST_INSERT_ID();\n+------------------+\n| LAST_INSERT_ID() |\n+------------------+\n| 9 |\n+------------------+\n\nYou can also use LAST_INSERT_ID() to delete the last inserted row:\n\nDELETE FROM product WHERE id = LAST_INSERT_ID();\n\nIf no rows were successfully inserted, LAST_INSERT_ID() returns 0.\n\nThe value of LAST_INSERT_ID() will be consistent across all versions if all\nrows in the INSERT or UPDATE statement were successful.\n\nThe currently executing statement does not affect the value of\nLAST_INSERT_ID(). Suppose that you generate an AUTO_INCREMENT value with one\nstatement, and then refer to LAST_INSERT_ID() in a multiple-row INSERT\nstatement that inserts rows into a table with its own AUTO_INCREMENT column.\nThe value of LAST_INSERT_ID() will remain stable in the second statement; its\nvalue for the second and later rows is not affected by the earlier row\ninsertions. (However, if you mix references to LAST_INSERT_ID() and\nLAST_INSERT_ID(expr), the effect is undefined.)\n\nIf the previous statement returned an error, the value of LAST_INSERT_ID() is\nundefined. For transactional tables, if the statement is rolled back due to an\nerror, the value of LAST_INSERT_ID() is left undefined. For manual ROLLBACK,\nthe value of LAST_INSERT_ID() is not restored to that before the transaction;\nit remains as it was at the point of the ROLLBACK.\n\nWithin the body of a stored routine (procedure or function) or a trigger, the\nvalue of LAST_INSERT_ID() changes the same way as for statements executed\noutside the body of these kinds of objects. The effect of a stored routine or\ntrigger upon the value of LAST_INSERT_ID() that is seen by following\nstatements depends on the kind of routine:\n\n* If a stored procedure executes statements that change the value of\nLAST_INSERT_ID(), the new value will be seen by statements that follow the\nprocedure call.\n\n* For stored functions and triggers that change the value, the value is\nrestored when the function or trigger ends, so following statements will not\nsee a changed value.\n\nExamples\n--------\n\nCREATE TABLE t (\n id INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY,\n f VARCHAR(1))\nENGINE = InnoDB;\n\nINSERT INTO t(f) VALUES(\'a\');\n\nSELECT LAST_INSERT_ID();\n+------------------+\n| LAST_INSERT_ID() |\n+------------------+\n| 1 |\n+------------------+\n\nINSERT INTO t(f) VALUES(\'b\');\n\nINSERT INTO t(f) VALUES(\'c\');\n\nSELECT LAST_INSERT_ID();\n+------------------+\n| LAST_INSERT_ID() |\n+------------------+\n| 3 |\n+------------------+\n\nINSERT INTO t(f) VALUES(\'d\'),(\'e\');\n\nSELECT LAST_INSERT_ID();\n+------------------+\n| LAST_INSERT_ID() |\n+------------------+\n| 4 |\n+------------------+\n\nSELECT * FROM t;\n+----+------+\n| id | f |\n+----+------+\n| 1 | a |\n| 2 | b |\n| 3 | c |\n| 4 | d |\n| 5 | e |\n+----+------+\n\nSELECT LAST_INSERT_ID(12);\n+--------------------+\n| LAST_INSERT_ID(12) |\n+--------------------+\n| 12 |\n+--------------------+\n\nSELECT LAST_INSERT_ID();\n+------------------+\n| LAST_INSERT_ID() |\n+------------------+\n| 12 |\n+------------------+\n\nINSERT INTO t(f) VALUES(\'f\');\n\nSELECT LAST_INSERT_ID();\n+------------------+\n| LAST_INSERT_ID() |\n+------------------+\n| 6 |\n+------------------+\n\nSELECT * FROM t;\n+----+------+\n| id | f |\n+----+------+\n| 1 | a |\n| 2 | b |\n| 3 | c |\n| 4 | d |\n| 5 | e |\n| 6 | f |\n+----+------+\n\nSELECT LAST_INSERT_ID(12);\n+--------------------+\n| LAST_INSERT_ID(12) |\n+--------------------+\n| 12 |\n+--------------------+\n\nINSERT INTO t(f) VALUES(\'g\');\n\nSELECT * FROM t;\n+----+------+\n| id | f |\n+----+------+\n| 1 | a |\n| 2 | b |\n| 3 | c |\n| 4 | d |\n| 5 | e |\n| 6 | f |\n| 7 | g |\n+----+------+\n\nURL: https://mariadb.com/kb/en/last_insert_id/','','https://mariadb.com/kb/en/last_insert_id/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (203,17,'LAST_VALUE','Syntax\n------\n\nLAST_VALUE(expr,[expr,...])\n\nLAST_VALUE(expr) OVER (\n [ PARTITION BY partition_expression ]\n [ ORDER BY order_list ]\n)\n\nDescription\n-----------\n\nLAST_VALUE() evaluates all expressions and returns the last.\n\nThis is useful together with setting user variables to a value with\n@var:=expr, for example when you want to get data of rows updated/deleted\nwithout having to do two queries against the table.\n\nLAST_VALUE can be used as a window function.\n\nReturns NULL if no last value exists.\n\nExamples\n--------\n\nCREATE TABLE t1 (a int, b int);\nINSERT INTO t1 VALUES(1,10),(2,20);\nDELETE FROM t1 WHERE a=1 AND last_value(@a:=a,@b:=b,1);\nSELECT @a,@b;\n+------+------+\n| @a | @b |\n+------+------+\n| 1 | 10 |\n+------+------+\n\nAs a window function:\n\nCREATE TABLE t1 (\n pk int primary key,\n a int,\n b int,\n c char(10),\n d decimal(10, 3),\n e real\n);\n\nINSERT INTO t1 VALUES\n( 1, 0, 1, \'one\', 0.1, 0.001),\n( 2, 0, 2, \'two\', 0.2, 0.002),\n( 3, 0, 3, \'three\', 0.3, 0.003),\n( 4, 1, 2, \'three\', 0.4, 0.004),\n( 5, 1, 1, \'two\', 0.5, 0.005),\n( 6, 1, 1, \'one\', 0.6, 0.006),\n( 7, 2, NULL, \'n_one\', 0.5, 0.007),\n( 8, 2, 1, \'n_two\', NULL, 0.008),\n( 9, 2, 2, NULL, 0.7, 0.009),\n(10, 2, 0, \'n_four\', 0.8, 0.010),\n(11, 2, 10, NULL, 0.9, NULL);\n\nSELECT pk, FIRST_VALUE(pk) OVER (ORDER BY pk) AS first_asc,\n LAST_VALUE(pk) OVER (ORDER BY pk) AS last_asc,\n FIRST_VALUE(pk) OVER (ORDER BY pk DESC) AS first_desc,\n LAST_VALUE(pk) OVER (ORDER BY pk DESC) AS last_desc\nFROM t1\nORDER BY pk DESC;\n\n+----+-----------+----------+------------+-----------+\n| pk | first_asc | last_asc | first_desc | last_desc |\n+----+-----------+----------+------------+-----------+\n| 11 | 1 | 11 | 11 | 11 |\n| 10 | 1 | 10 | 11 | 10 |\n| 9 | 1 | 9 | 11 | 9 |\n| 8 | 1 | 8 | 11 | 8 |\n| 7 | 1 | 7 | 11 | 7 |\n| 6 | 1 | 6 | 11 | 6 |\n| 5 | 1 | 5 | 11 | 5 |\n| 4 | 1 | 4 | 11 | 4 |\n| 3 | 1 | 3 | 11 | 3 |\n| 2 | 1 | 2 | 11 | 2 |\n| 1 | 1 | 1 | 11 | 1 |\n+----+-----------+----------+------------+-----------+\n\nCREATE OR REPLACE TABLE t1 (i int);\nINSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);\n\nSELECT i,\n FIRST_VALUE(i) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW and 1 FOLLOWING) AS\nf_1f,\n LAST_VALUE(i) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW and 1 FOLLOWING) AS\nl_1f,\n FIRST_VALUE(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS\nf_1p1f,\n LAST_VALUE(i) OVER (ORDER BY i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS\nf_1p1f,\n FIRST_VALUE(i) OVER (ORDER BY i ROWS BETWEEN 2 PRECEDING AND 1 PRECEDING) AS\nf_2p1p,\n LAST_VALUE(i) OVER (ORDER BY i ROWS BETWEEN 2 PRECEDING AND 1 PRECEDING) AS\nf_2p1p,\n FIRST_VALUE(i) OVER (ORDER BY i ROWS BETWEEN 1 FOLLOWING AND 2 FOLLOWING) AS\nf_1f2f,\n LAST_VALUE(i) OVER (ORDER BY i ROWS BETWEEN 1 FOLLOWING AND 2 FOLLOWING) AS\nf_1f2f\nFROM t1;\n\n+------+------+------+--------+--------+--------+--------+--------+--------+\n| i | f_1f | l_1f | f_1p1f | f_1p1f | f_2p1p | f_2p1p | f_1f2f | f_1f2f |\n+------+------+------+--------+--------+--------+--------+--------+--------+\n| 1 | 1 | 2 | 1 | 2 | NULL | NULL | 2 | 3 |\n| 2 | 2 | 3 | 1 | 3 | 1 | 1 | 3 | 4 |\n| 3 | 3 | 4 | 2 | 4 | 1 | 2 | 4 | 5 |\n| 4 | 4 | 5 | 3 | 5 | 2 | 3 | 5 | 6 |\n| 5 | 5 | 6 | 4 | 6 | 3 | 4 | 6 | 7 |\n| 6 | 6 | 7 | 5 | 7 | 4 | 5 | 7 | 8 |\n| 7 | 7 | 8 | 6 | 8 | 5 | 6 | 8 | 9 |\n| 8 | 8 | 9 | 7 | 9 | 6 | 7 | 9 | 10 |\n| 9 | 9 | 10 | 8 | 10 | 7 | 8 | 10 | 10 |\n| 10 | 10 | 10 | 9 | 10 | 8 | 9 | NULL | NULL |\n+------+------+------+--------+--------+--------+--------+--------+--------+\n\nURL: https://mariadb.com/kb/en/last_value/','','https://mariadb.com/kb/en/last_value/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (204,17,'PROCEDURE ANALYSE','Syntax\n------\n\nanalyse([max_elements[,max_memory]])\n\nDescription\n-----------\n\nThis procedure is defined in the sql/sql_analyse.cc file. It examines the\nresult from a query and returns an analysis of the results that suggests\noptimal data types for each column. To obtain this analysis, append PROCEDURE\nANALYSE to the end of a SELECT statement:\n\nSELECT ... FROM ... WHERE ... PROCEDURE ANALYSE([max_elements,[max_memory]])\n\nFor example:\n\nSELECT col1, col2 FROM table1 PROCEDURE ANALYSE(10, 2000);\n\nThe results show some statistics for the values returned by the query, and\npropose an optimal data type for the columns. This can be helpful for checking\nyour existing tables, or after importing new data. You may need to try\ndifferent settings for the arguments so that PROCEDURE ANALYSE() does not\nsuggest the ENUM data type when it is not appropriate.\n\nThe arguments are optional and are used as follows:\n\n* max_elements (default 256) is the maximum number of distinct values that\nanalyse notices per column. This is used by analyse to check whether the\noptimal data type should be of type ENUM; if there are more than max_elements\ndistinct values, then ENUM is not a suggested type.\n* max_memory (default 8192) is the maximum amount of memory that analyse\nshould allocate per column while trying to find all distinct values.\n\nURL: https://mariadb.com/kb/en/procedure-analyse/','','https://mariadb.com/kb/en/procedure-analyse/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (205,17,'ROWNUM','MariaDB starting with 10.6.1\n----------------------------\nFrom MariaDB 10.6.1, the ROWNUM() function is supported.\n\nSyntax\n------\n\nROWNUM()\n\nIn Oracle mode one can just use ROWNUM, without the parentheses.\n\nDescription\n-----------\n\nROWNUM() returns the current number of accepted rows in the current context.\nIt main purpose is to emulate the ROWNUM pseudo column in Oracle. For MariaDB\nnative applications, we recommend the usage of LIMIT, as it is easier to use\nand gives more predictable results than the usage of ROWNUM().\n\nThe main difference between using LIMIT and ROWNUM() to limit the rows in the\nresult is that LIMIT works on the result set while ROWNUM works on the number\nof accepted rows (before any ORDER or GROUP BY clauses).\n\nThe following queries will return the same results:\n\nSELECT * from t1 LIMIT 10;\nSELECT * from t1 WHERE ROWNUM() <= 10;\n\nWhile the following may return different results based on in which orders the\nrows are found:\n\nSELECT * from t1 ORDER BY a LIMIT 10;\nSELECT * from t1 ORDER BY a WHERE ROWNUM() <= 10;\n\nThe recommended way to use ROWNUM to limit the number of returned rows and get\npredictable results is to have the query in a subquery and test for ROWNUM()\nin the outer query:\n\nSELECT * FROM (select * from t1 ORDER BY a) WHERE ROWNUM() <= 10;\n\nROWNUM() can be used in the following contexts:\n\n* SELECT\n* INSERT\n* UPDATE\n* DELETE\n* LOAD DATA INFILE\n\nUsed in other contexts, ROWNUM() will return 0.\n\nExamples\n--------\n\nINSERT INTO t1 VALUES (1,ROWNUM()),(2,ROWNUM()),(3,ROWNUM());\n\nINSERT INTO t1 VALUES (1),(2) returning a, ROWNUM();\n\nUPDATE t1 SET row_num_column=ROWNUM();\n\nDELETE FROM t1 WHERE a < 10 AND ROWNUM() < 2;\n\nLOAD DATA INFILE \'filename\' into table t1 fields terminated by \',\' \n lines terminated by \"\\r\\n\" (a,b) set c=ROWNUM();\n\nOptimizations\n-------------\n\nIn many cases where ROWNUM() is used, MariaDB will use the same optimizations\nit uses with LIMIT.\n\nLIMIT optimization is possible when using ROWNUM in the following manner:\n\n* When one is in a top level WHERE clause comparing ROWNUM() with a numerical\nconstant using any of the following expressions:\nROWNUM() < number\nROWNUM() <= number\nROWNUM() = 1\nROWNUM() can be also be the right argument to the comparison function.\n\nIn the above cases, LIMIT optimization can be done in the following cases:\n\n* For the current sub query when the ROWNUM comparison is done on the top\nlevel:\n\nSELECT * from t1 WHERE ROWNUM() <= 2 AND t1.a > 0\n\n* For an inner sub query, when the upper level has only a ROWNUM() comparison\nin the WHERE clause:\n\nSELECT * from (select * from t1) as t WHERE ROWNUM() <= 2\n\nOther Changes Related to ROWNUM\n-------------------------------\n\nWhen ROWNUM() is used anywhere in a query, the optimization to ignore ORDER BY\nin subqueries are disabled.\n\nThis was done to get the following common Oracle query to work as expected:\n\nselect * from (select * from t1 order by a desc) as t where rownum() <= 2;\n\nBy default MariaDB ignores any ORDER BY in subqueries both because the SQL\nstandard defines results sets in subqueries to be un-ordered and because of\nperformance reasons (especially when using views in subqueries). See MDEV-3926\n\"Wrong result with GROUP BY ... WITH ROLLUP\" for a discussion of this topic.\n\nOther Considerations\n--------------------\n\nWhile MariaDB tries to emulate Oracle\'s usage of ROWNUM() as closely as\npossible, there are cases where the result is different:\n\n* When the optimizer finds rows in a different order (because of different\nstorage methods or optimization). This may also happen in Oracle if one adds\nor deletes an index, in which case the rows may be found in a different order.\n\nNote that usage of ROWNUM() in functions or stored procedures will use their\nown context, not the caller\'s context.\n\nURL: https://mariadb.com/kb/en/rownum/','','https://mariadb.com/kb/en/rownum/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (206,17,'ROW_COUNT','Syntax\n------\n\nROW_COUNT()\n\nDescription\n-----------\n\nROW_COUNT() returns the number of rows updated, inserted or deleted by the\npreceding statement. This is the same as the row count that the mysql client\ndisplays and the value from the mysql_affected_rows() C API function.\n\nGenerally:\n\n* For statements which return a result set (such as SELECT, SHOW, DESC or\nHELP), returns -1, even when the result set is empty. This is also true for\nadministrative statements, such as OPTIMIZE.\n* For DML statements other than SELECT and for ALTER TABLE, returns the number\nof affected rows.\n* For DDL statements (including TRUNCATE) and for other statements which don\'t\nreturn any result set (such as USE, DO, SIGNAL or DEALLOCATE PREPARE), returns\n0.\n\nFor UPDATE, affected rows is by default the number of rows that were actually\nchanged. If the CLIENT_FOUND_ROWS flag to mysql_real_connect() is specified\nwhen connecting to mysqld, affected rows is instead the number of rows matched\nby the WHERE clause.\n\nFor REPLACE, deleted rows are also counted. So, if REPLACE deletes a row and\nadds a new row, ROW_COUNT() returns 2.\n\nFor INSERT ... ON DUPLICATE KEY, updated rows are counted twice. So, if INSERT\nadds a new rows and modifies another row, ROW_COUNT() returns 3.\n\nROW_COUNT() does not take into account rows that are not directly\ndeleted/updated by the last statement. This means that rows deleted by foreign\nkeys or triggers are not counted.\n\nWarning: You can use ROW_COUNT() with prepared statements, but you need to\ncall it after EXECUTE, not after DEALLOCATE PREPARE, because the row count for\nallocate prepare is always 0.\n\nWarning: When used after a CALL statement, this function returns the number of\nrows affected by the last statement in the procedure, not by the whole\nprocedure.\n\nWarning: After INSERT DELAYED, ROW_COUNT() returns the number of the rows you\ntried to insert, not the number of the successful writes.\n\nThis information can also be found in the diagnostics area.\n\nStatements using the ROW_COUNT() function are not safe for replication.\n\nExamples\n--------\n\nCREATE TABLE t (A INT);\n\nINSERT INTO t VALUES(1),(2),(3);\n\nSELECT ROW_COUNT();\n+-------------+\n| ROW_COUNT() |\n+-------------+\n| 3 |\n+-------------+\n\nDELETE FROM t WHERE A IN(1,2);\n\nSELECT ROW_COUNT(); \n+-------------+\n| ROW_COUNT() |\n+-------------+\n| 2 |\n+-------------+\n\nExample with prepared statements:\n\nSET @q = \'INSERT INTO t VALUES(1),(2),(3);\';\n\nPREPARE stmt FROM @q;\n\nEXECUTE stmt;\nQuery OK, 3 rows affected (0.39 sec)\nRecords: 3 Duplicates: 0 Warnings: 0\n\nSELECT ROW_COUNT();\n+-------------+\n| ROW_COUNT() |\n+-------------+\n| 3 |\n+-------------+\n\nURL: https://mariadb.com/kb/en/row_count/','','https://mariadb.com/kb/en/row_count/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (207,17,'SCHEMA','Syntax\n------\n\nSCHEMA()\n\nDescription\n-----------\n\nThis function is a synonym for DATABASE().\n\nURL: https://mariadb.com/kb/en/schema/','','https://mariadb.com/kb/en/schema/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (208,17,'SESSION_USER','Syntax\n------\n\nSESSION_USER()\n\nDescription\n-----------\n\nSESSION_USER() is a synonym for USER().\n\nURL: https://mariadb.com/kb/en/session_user/','','https://mariadb.com/kb/en/session_user/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (209,17,'SYSTEM_USER','Syntax\n------\n\nSYSTEM_USER()\n\nDescription\n-----------\n\nSYSTEM_USER() is a synonym for USER().\n\nURL: https://mariadb.com/kb/en/system_user/','','https://mariadb.com/kb/en/system_user/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (210,17,'USER','Syntax\n------\n\nUSER()\n\nDescription\n-----------\n\nReturns the current MariaDB user name and host name, given when authenticating\nto MariaDB, as a string in the utf8 character set.\n\nNote that the value of USER() may differ from the value of CURRENT_USER(),\nwhich is the user used to authenticate the current client. CURRENT_ROLE()\nreturns the current active role.\n\nSYSTEM_USER() and SESSION_USER are synonyms for USER().\n\nStatements using the USER() function or one of its synonyms are not safe for\nstatement level replication.\n\nExamples\n--------\n\nshell> mysql --user=\"anonymous\"\n\nSELECT USER(),CURRENT_USER();\n+---------------------+----------------+\n| USER() | CURRENT_USER() |\n+---------------------+----------------+\n| anonymous@localhost | @localhost |\n+---------------------+----------------+\n\nTo select only the IP address, use SUBSTRING_INDEX(),\n\nSELECT SUBSTRING_INDEX(USER(), \'@\', -1);\n+----------------------------------+\n| SUBSTRING_INDEX(USER(), \'@\', -1) |\n+----------------------------------+\n| 192.168.0.101 |\n+----------------------------------+\n\nURL: https://mariadb.com/kb/en/user/','','https://mariadb.com/kb/en/user/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (211,17,'VERSION','Syntax\n------\n\nVERSION()\n\nDescription\n-----------\n\nReturns a string that indicates the MariaDB server version. The string uses\nthe utf8 character set.\n\nExamples\n--------\n\nSELECT VERSION();\n+----------------+\n| VERSION() |\n+----------------+\n| 10.4.7-MariaDB |\n+----------------+\n\nThe VERSION() string may have one or more of the following suffixes:\n\n+---------------------------+------------------------------------------------+\n| Suffix | Description |\n+---------------------------+------------------------------------------------+\n| -embedded | The server is an embedded server (libmysqld). |\n+---------------------------+------------------------------------------------+\n| -log | General logging, slow logging or binary |\n| | (replication) logging is enabled. |\n+---------------------------+------------------------------------------------+\n| -debug | The server is compiled for debugging. |\n+---------------------------+------------------------------------------------+\n| -valgrind | The server is compiled to be instrumented |\n| | with valgrind. |\n+---------------------------+------------------------------------------------+\n\nChanging the Version String\n---------------------------\n\nSome old legacy code may break because they are parsing the VERSION string and\nexpecting a MySQL string or a simple version string like Joomla til API17, see\nMDEV-7780.\n\nFrom MariaDB 10.2, one can fool these applications by setting the version\nstring from the command line or the my.cnf files with --version=....\n\nURL: https://mariadb.com/kb/en/version/','','https://mariadb.com/kb/en/version/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (212,18,'Assignment Operator (:=)','Syntax\n------\n\nvar_name := expr\n\nDescription\n-----------\n\nAssignment operator for assigning a value. The value on the right is assigned\nto the variable on left.\n\nUnlike the = operator, := can always be used to assign a value to a variable.\n\nThis operator works with both user-defined variables and local variables.\n\nWhen assigning the same value to several variables, LAST_VALUE() can be useful.\n\nExamples\n--------\n\nSELECT @x := 10;\n+----------+\n| @x := 10 |\n+----------+\n| 10 |\n+----------+\n\nSELECT @x, @y := @x;\n+------+----------+\n| @x | @y := @x |\n+------+----------+\n| 10 | 10 |\n+------+----------+\n\nURL: https://mariadb.com/kb/en/assignment-operator/','','https://mariadb.com/kb/en/assignment-operator/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (213,18,'Assignment Operator (=)','Syntax\n------\n\nidentifier = expr\n\nDescription\n-----------\n\nThe equal sign is used as both an assignment operator in certain contexts, and\nas a comparison operator. When used as assignment operator, the value on the\nright is assigned to the variable (or column, in some contexts) on the left.\n\nSince its use can be ambiguous, unlike the := assignment operator, the =\nassignment operator cannot be used in all contexts, and is only valid as part\nof a SET statement, or the SET clause of an UPDATE statement\n\nThis operator works with both user-defined variables and local variables.\n\nExamples\n--------\n\nUPDATE table_name SET x = 2 WHERE x > 100;\n\nSET @x = 1, @y := 2;\n\nURL: https://mariadb.com/kb/en/assignment-operators-assignment-operator/','','https://mariadb.com/kb/en/assignment-operators-assignment-operator/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (214,19,'Not Equal Operator','Syntax\n------\n\n<>, !=\n\nDescription\n-----------\n\nNot equal operator. Evaluates both SQL expressions and returns 1 if they are\nnot equal and 0 if they are equal, or NULL if either expression is NULL. If\nthe expressions return different data types, (for instance, a number and a\nstring), performs type conversion.\n\nWhen used in row comparisons these two queries return the same results:\n\nSELECT (t1.a, t1.b) != (t2.x, t2.y) \nFROM t1 INNER JOIN t2;\n\nSELECT (t1.a != t2.x) OR (t1.b != t2.y)\nFROM t1 INNER JOIN t2;\n\nExamples\n--------\n\nSELECT \'.01\' <> \'0.01\';\n+-----------------+\n| \'.01\' <> \'0.01\' |\n+-----------------+\n| 1 |\n+-----------------+\n\nSELECT .01 <> \'0.01\';\n+---------------+\n| .01 <> \'0.01\' |\n+---------------+\n| 0 |\n+---------------+\n\nSELECT \'zapp\' <> \'zappp\';\n+-------------------+\n| \'zapp\' <> \'zappp\' |\n+-------------------+\n| 1 |\n+-------------------+\n\nURL: https://mariadb.com/kb/en/not-equal/','','https://mariadb.com/kb/en/not-equal/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (215,19,'<','Syntax\n------\n\n<\n\nDescription\n-----------\n\nLess than operator. Evaluates both SQL expressions and returns 1 if the left\nvalue is less than the right value and 0 if it is not, or NULL if either\nexpression is NULL. If the expressions return different data types, (for\ninstance, a number and a string), performs type conversion.\n\nWhen used in row comparisons these two queries return the same results:\n\nSELECT (t1.a, t1.b) < (t2.x, t2.y) \nFROM t1 INNER JOIN t2;\n\nSELECT (t1.a < t2.x) OR ((t1.a = t2.x) AND (t1.b < t2.y))\nFROM t1 INNER JOIN t2;\n\nExamples\n--------\n\nSELECT 2 < 2;\n+-------+\n| 2 < 2 |\n+-------+\n| 0 |\n+-------+\n\nType conversion:\n\nSELECT 3<\'4\';\n+-------+\n| 3<\'4\' |\n+-------+\n| 1 |\n+-------+\n\nCase insensitivity - see Character Sets and Collations:\n\nSELECT \'a\'<\'A\';\n+---------+\n| \'a\'<\'A\' |\n+---------+\n| 0 |\n+---------+\n\nURL: https://mariadb.com/kb/en/less-than/','','https://mariadb.com/kb/en/less-than/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (216,19,'<=','Syntax\n------\n\n<=\n\nDescription\n-----------\n\nLess than or equal operator. Evaluates both SQL expressions and returns 1 if\nthe left value is less than or equal to the right value and 0 if it is not, or\nNULL if either expression is NULL. If the expressions return different data\ntypes, (for instance, a number and a string), performs type conversion.\n\nWhen used in row comparisons these two queries return the same results:\n\nSELECT (t1.a, t1.b) <= (t2.x, t2.y) \nFROM t1 INNER JOIN t2;\n\nSELECT (t1.a < t2.x) OR ((t1.a = t2.x) AND (t1.b <= t2.y))\nFROM t1 INNER JOIN t2;\n\nExamples\n--------\n\nSELECT 0.1 <= 2;\n+----------+\n| 0.1 <= 2 |\n+----------+\n| 1 |\n+----------+\n\nSELECT \'a\'<=\'A\';\n+----------+\n| \'a\'<=\'A\' |\n+----------+\n| 1 |\n+----------+\n\nURL: https://mariadb.com/kb/en/less-than-or-equal/','','https://mariadb.com/kb/en/less-than-or-equal/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (217,19,'<=>','Syntax\n------\n\n<=>\n\nDescription\n-----------\n\nNULL-safe equal operator. It performs an equality comparison like the =\noperator, but returns 1 rather than NULL if both operands are NULL, and 0\nrather than NULL if one operand is NULL.\n\na <=> b is equivalent to a = b OR (a IS NULL AND b IS NULL).\n\nWhen used in row comparisons these two queries return the same results:\n\nSELECT (t1.a, t1.b) <=> (t2.x, t2.y) \nFROM t1 INNER JOIN t2;\n\nSELECT (t1.a <=> t2.x) AND (t1.b <=> t2.y)\nFROM t1 INNER JOIN t2;\n\nSee also NULL Values in MariaDB.\n\nExamples\n--------\n\nSELECT 1 <=> 1, NULL <=> NULL, 1 <=> NULL;\n+---------+---------------+------------+\n| 1 <=> 1 | NULL <=> NULL | 1 <=> NULL |\n+---------+---------------+------------+\n| 1 | 1 | 0 |\n+---------+---------------+------------+\n\nSELECT 1 = 1, NULL = NULL, 1 = NULL;\n+-------+-------------+----------+\n| 1 = 1 | NULL = NULL | 1 = NULL |\n+-------+-------------+----------+\n| 1 | NULL | NULL |\n+-------+-------------+----------+\n\nURL: https://mariadb.com/kb/en/null-safe-equal/','','https://mariadb.com/kb/en/null-safe-equal/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (218,19,'=','Syntax\n------\n\nleft_expr = right_expr\n\nDescription\n-----------\n\nEqual operator. Evaluates both SQL expressions and returns 1 if they are\nequal, 0 if they are not equal, or NULL if either expression is NULL. If the\nexpressions return different data types (for example, a number and a string),\na type conversion is performed.\n\nWhen used in row comparisons these two queries are synonymous and return the\nsame results:\n\nSELECT (t1.a, t1.b) = (t2.x, t2.y) FROM t1 INNER JOIN t2;\n\nSELECT (t1.a = t2.x) AND (t1.b = t2.y) FROM t1 INNER JOIN t2;\n\nTo perform a NULL-safe comparison, use the <=> operator.\n\n= can also be used as an assignment operator.\n\nExamples\n--------\n\nSELECT 1 = 0;\n+-------+\n| 1 = 0 |\n+-------+\n| 0 |\n+-------+\n\nSELECT \'0\' = 0;\n+---------+\n| \'0\' = 0 |\n+---------+\n| 1 |\n+---------+\n\nSELECT \'0.0\' = 0;\n+-----------+\n| \'0.0\' = 0 |\n+-----------+\n| 1 |\n+-----------+\n\nSELECT \'0.01\' = 0;\n+------------+\n| \'0.01\' = 0 |\n+------------+\n| 0 |\n+------------+\n\nSELECT \'.01\' = 0.01;\n+--------------+\n| \'.01\' = 0.01 |\n+--------------+\n| 1 |\n+--------------+\n\nSELECT (5 * 2) = CONCAT(\'1\', \'0\');\n+----------------------------+\n| (5 * 2) = CONCAT(\'1\', \'0\') |\n+----------------------------+\n| 1 |\n+----------------------------+\n\nSELECT 1 = NULL;\n+----------+\n| 1 = NULL |\n+----------+\n| NULL |\n+----------+\n\nSELECT NULL = NULL;\n+-------------+\n| NULL = NULL |\n+-------------+\n| NULL |\n+-------------+\n\nURL: https://mariadb.com/kb/en/equal/','','https://mariadb.com/kb/en/equal/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (219,19,'>','Syntax\n------\n\n>\n\nDescription\n-----------\n\nGreater than operator. Evaluates both SQL expressions and returns 1 if the\nleft value is greater than the right value and 0 if it is not, or NULL if\neither expression is NULL. If the expressions return different data types,\n(for instance, a number and a string), performs type conversion.\n\nWhen used in row comparisons these two queries return the same results:\n\nSELECT (t1.a, t1.b) > (t2.x, t2.y) \nFROM t1 INNER JOIN t2;\n\nSELECT (t1.a > t2.x) OR ((t1.a = t2.x) AND (t1.b > t2.y))\nFROM t1 INNER JOIN t2;\n\nExamples\n--------\n\nSELECT 2 > 2;\n+-------+\n| 2 > 2 |\n+-------+\n| 0 |\n+-------+\n\nSELECT \'b\' > \'a\';\n+-----------+\n| \'b\' > \'a\' |\n+-----------+\n| 1 |\n+-----------+\n\nURL: https://mariadb.com/kb/en/greater-than/','','https://mariadb.com/kb/en/greater-than/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (220,19,'>=','Syntax\n------\n\n>=\n\nDescription\n-----------\n\nGreater than or equal operator. Evaluates both SQL expressions and returns 1\nif the left value is greater than or equal to the right value and 0 if it is\nnot, or NULL if either expression is NULL. If the expressions return different\ndata types, (for instance, a number and a string), performs type conversion.\n\nWhen used in row comparisons these two queries return the same results:\n\nSELECT (t1.a, t1.b) >= (t2.x, t2.y) \nFROM t1 INNER JOIN t2;\n\nSELECT (t1.a > t2.x) OR ((t1.a = t2.x) AND (t1.b >= t2.y))\nFROM t1 INNER JOIN t2;\n\nExamples\n--------\n\nSELECT 2 >= 2;\n+--------+\n| 2 >= 2 |\n+--------+\n| 1 |\n+--------+\n\nSELECT \'A\' >= \'a\';\n+------------+\n| \'A\' >= \'a\' |\n+------------+\n| 1 |\n+------------+\n\nURL: https://mariadb.com/kb/en/greater-than-or-equal/','','https://mariadb.com/kb/en/greater-than-or-equal/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (221,19,'BETWEEN AND','Syntax\n------\n\nexpr BETWEEN min AND max\n\nDescription\n-----------\n\nIf expr is greater than or equal to min and expr is less than or equal to max,\nBETWEEN returns 1, otherwise it returns 0. This is equivalent to the\nexpression (min <= expr AND expr <= max) if all the arguments are of the same\ntype. Otherwise type conversion takes place according to the rules described\nat Type Conversion, but applied to all the three arguments.\n\nExamples\n--------\n\nSELECT 1 BETWEEN 2 AND 3;\n+-------------------+\n| 1 BETWEEN 2 AND 3 |\n+-------------------+\n| 0 |\n+-------------------+\n\nSELECT \'b\' BETWEEN \'a\' AND \'c\';\n+-------------------------+\n| \'b\' BETWEEN \'a\' AND \'c\' |\n+-------------------------+\n| 1 |\n+-------------------------+\n\nSELECT 2 BETWEEN 2 AND \'3\';\n+---------------------+\n| 2 BETWEEN 2 AND \'3\' |\n+---------------------+\n| 1 |\n+---------------------+\n\nSELECT 2 BETWEEN 2 AND \'x-3\';\n+-----------------------+\n| 2 BETWEEN 2 AND \'x-3\' |\n+-----------------------+\n| 0 |\n+-----------------------+\n1 row in set, 1 warning (0.00 sec)\n\nWarning (Code 1292): Truncated incorrect DOUBLE value: \'x-3\'\n\nNULL:\n\nSELECT 1 BETWEEN 1 AND NULL;\n+----------------------+\n| 1 BETWEEN 1 AND NULL |\n+----------------------+\n| NULL |\n+----------------------+\n\nDATE, DATETIME and TIMESTAMP examples. Omitting the time component compares\nagainst 00:00, so later times on the same date are not returned:\n\nCREATE TABLE `x` (\n a date ,\n b datetime,\n c timestamp\n)\n\nINSERT INTO x VALUES \n (\'2018-11-11\', \'2018-11-11 05:15\', \'2018-11-11 05:15\'), \n (\'2018-11-12\', \'2018-11-12 05:15\', \'2018-11-12 05:15\');\n\nSELECT * FROM x WHERE a BETWEEN \'2018-11-11\' AND \'2018-11-12\';\n+------------+---------------------+---------------------+\n| a | b | c |\n+------------+---------------------+---------------------+\n| 2018-11-11 | 2018-11-11 05:15:00 | 2018-11-11 05:15:00 |\n| 2018-11-12 | 2018-11-12 05:15:00 | 2018-11-12 05:15:00 |\n+------------+---------------------+---------------------+\n\nSELECT * FROM x WHERE b BETWEEN \'2018-11-11\' AND \'2018-11-12\';\n+------------+---------------------+---------------------+\n| a | b | c |\n+------------+---------------------+---------------------+\n| 2018-11-11 | 2018-11-11 05:15:00 | 2018-11-11 05:15:00 |\n+------------+---------------------+---------------------+\n\nSELECT * FROM x WHERE c BETWEEN \'2018-11-11\' AND \'2018-11-12\';\n+------------+---------------------+---------------------+\n| a | b | c |\n+------------+---------------------+---------------------+\n| 2018-11-11 | 2018-11-11 05:15:00 | 2018-11-11 05:15:00 |\n+------------+---------------------+---------------------+\n\nURL: https://mariadb.com/kb/en/between-and/','','https://mariadb.com/kb/en/between-and/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (222,19,'COALESCE','Syntax\n------\n\nCOALESCE(value,...)\n\nDescription\n-----------\n\nReturns the first non-NULL value in the list, or NULL if there are no non-NULL\nvalues. At least one parameter must be passed.\n\nThe function is useful when substituting a default value for null values when\ndisplaying data.\n\nSee also NULL Values in MariaDB.\n\nExamples\n--------\n\nSELECT COALESCE(NULL,1);\n+------------------+\n| COALESCE(NULL,1) |\n+------------------+\n| 1 |\n+------------------+\n\nSELECT COALESCE(NULL,NULL,NULL);\n+--------------------------+\n| COALESCE(NULL,NULL,NULL) |\n+--------------------------+\n| NULL |\n+--------------------------+\n\nWhen two arguments are given, COALESCE() is the same as IFNULL():\n\nSET @a=NULL, @b=1;\n\nSELECT COALESCE(@a, @b), IFNULL(@a, @b);\n+------------------+----------------+\n| COALESCE(@a, @b) | IFNULL(@a, @b) |\n+------------------+----------------+\n| 1 | 1 |\n+------------------+----------------+\n\nHex type confusion:\n\nCREATE TABLE t1 (a INT, b VARCHAR(10));\nINSERT INTO t1 VALUES (0x31, 0x61),(COALESCE(0x31), COALESCE(0x61));\n\nSELECT * FROM t1;\n+------+------+\n| a | b |\n+------+------+\n| 49 | a |\n| 1 | a |\n+------+------+\n\nThe reason for the differing results above is that when 0x31 is inserted\ndirectly to the column, it\'s treated as a number (see Hexadecimal Literals),\nwhile when 0x31 is passed to COALESCE(), it\'s treated as a string, because:\n\n* HEX values have a string data type by default.\n* COALESCE() has the same data type as the argument.\n\nSubstituting zero for NULL (in this case when the aggregate function returns\nNULL after finding no rows):\n\nSELECT SUM(score) FROM student;\n+------------+\n| SUM(score) |\n+------------+\n| NULL |\n+------------+\n\nSELECT COALESCE(SUM(score),0) FROM student;\n+------------------------+\n| COALESCE(SUM(score),0) |\n+------------------------+\n| 0 |\n+------------------------+\n\nURL: https://mariadb.com/kb/en/coalesce/','','https://mariadb.com/kb/en/coalesce/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (223,19,'GREATEST','Syntax\n------\n\nGREATEST(value1,value2,...)\n\nDescription\n-----------\n\nWith two or more arguments, returns the largest (maximum-valued) argument. The\narguments are compared using the same rules as for LEAST().\n\nExamples\n--------\n\nSELECT GREATEST(2,0);\n+---------------+\n| GREATEST(2,0) |\n+---------------+\n| 2 |\n+---------------+\n\nSELECT GREATEST(34.0,3.0,5.0,767.0);\n+------------------------------+\n| GREATEST(34.0,3.0,5.0,767.0) |\n+------------------------------+\n| 767.0 |\n+------------------------------+\n\nSELECT GREATEST(\'B\',\'A\',\'C\');\n+-----------------------+\n| GREATEST(\'B\',\'A\',\'C\') |\n+-----------------------+\n| C |\n+-----------------------+\n\nURL: https://mariadb.com/kb/en/greatest/','','https://mariadb.com/kb/en/greatest/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (224,19,'IN','Syntax\n------\n\nexpr IN (value,...)\n\nDescription\n-----------\n\nReturns 1 if expr is equal to any of the values in the IN list, else returns\n0. If all values are constants, they are evaluated according to the type of\nexpr and sorted. The search for the item then is done using a binary search.\nThis means IN is very quick if the IN value list consists entirely of\nconstants. Otherwise, type conversion takes place according to the rules\ndescribed at Type Conversion, but applied to all the arguments.\n\nIf expr is NULL, IN always returns NULL. If at least one of the values in the\nlist is NULL, and one of the comparisons is true, the result is 1. If at least\none of the values in the list is NULL and none of the comparisons is true, the\nresult is NULL.\n\nExamples\n--------\n\nSELECT 2 IN (0,3,5,7);\n+----------------+\n| 2 IN (0,3,5,7) |\n+----------------+\n| 0 |\n+----------------+\n\nSELECT \'wefwf\' IN (\'wee\',\'wefwf\',\'weg\');\n+----------------------------------+\n| \'wefwf\' IN (\'wee\',\'wefwf\',\'weg\') |\n+----------------------------------+\n| 1 |\n+----------------------------------+\n\nType conversion:\n\nSELECT 1 IN (\'1\', \'2\', \'3\');\n+----------------------+\n| 1 IN (\'1\', \'2\', \'3\') |\n+----------------------+\n| 1 |\n+----------------------+\n\nSELECT NULL IN (1, 2, 3);\n+-------------------+\n| NULL IN (1, 2, 3) |\n+-------------------+\n| NULL |\n+-------------------+\n\nSELECT 1 IN (1, 2, NULL);\n+-------------------+\n| 1 IN (1, 2, NULL) |\n+-------------------+\n| 1 |\n+-------------------+\n\nSELECT 5 IN (1, 2, NULL);\n+-------------------+\n| 5 IN (1, 2, NULL) |\n+-------------------+\n| NULL |\n+-------------------+\n\nURL: https://mariadb.com/kb/en/in/','','https://mariadb.com/kb/en/in/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (225,19,'INTERVAL','Syntax\n------\n\nINTERVAL(N,N1,N2,N3,...)\n\nDescription\n-----------\n\nReturns the index of the last argument that is less than the first argument or\nis NULL.\n\nReturns 0 if N < N1, 1 if N < N2, 2 if N < N3 and so on or -1 if N is NULL.\nAll arguments are treated as integers. It is required that N1 < N2 < N3 < ...\n< Nn for this function to work correctly. This is because a fast binary search\nis used.\n\nExamples\n--------\n\nSELECT INTERVAL(23, 1, 15, 17, 30, 44, 200);\n+--------------------------------------+\n| INTERVAL(23, 1, 15, 17, 30, 44, 200) |\n+--------------------------------------+\n| 3 |\n+--------------------------------------+\n\nSELECT INTERVAL(10, 1, 10, 100, 1000);\n+--------------------------------+\n| INTERVAL(10, 1, 10, 100, 1000) |\n+--------------------------------+\n| 2 |\n+--------------------------------+\n\nSELECT INTERVAL(22, 23, 30, 44, 200);\n+-------------------------------+\n| INTERVAL(22, 23, 30, 44, 200) |\n+-------------------------------+\n| 0 |\n+-------------------------------+\n\nSELECT INTERVAL(10, 2, NULL);\n+-----------------------+\n| INTERVAL(10, 2, NULL) |\n+-----------------------+\n| 2 |\n+-----------------------+\n\nURL: https://mariadb.com/kb/en/interval/','','https://mariadb.com/kb/en/interval/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (226,19,'IS','Syntax\n------\n\nIS boolean_value\n\nDescription\n-----------\n\nTests a value against a boolean value, where boolean_value can be TRUE, FALSE,\nor UNKNOWN.\n\nThere is an important difference between using IS TRUE or comparing a value\nwith TRUE using =. When using =, only 1 equals to TRUE. But when using IS\nTRUE, all values which are logically true (like a number > 1) return TRUE.\n\nExamples\n--------\n\nSELECT 1 IS TRUE, 0 IS FALSE, NULL IS UNKNOWN;\n+-----------+------------+-----------------+\n| 1 IS TRUE | 0 IS FALSE | NULL IS UNKNOWN |\n+-----------+------------+-----------------+\n| 1 | 1 | 1 |\n+-----------+------------+-----------------+\n\nDifference between = and IS TRUE:\n\nSELECT 2 = TRUE, 2 IS TRUE;\n+----------+-----------+\n| 2 = TRUE | 2 IS TRUE |\n+----------+-----------+\n| 0 | 1 |\n+----------+-----------+\n\nURL: https://mariadb.com/kb/en/is/','','https://mariadb.com/kb/en/is/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (227,19,'IS NOT','Syntax\n------\n\nIS NOT boolean_value\n\nDescription\n-----------\n\nTests a value against a boolean value, where boolean_value can be TRUE, FALSE,\nor UNKNOWN.\n\nExamples\n--------\n\nSELECT 1 IS NOT UNKNOWN, 0 IS NOT UNKNOWN, NULL IS NOT UNKNOWN;\n+------------------+------------------+---------------------+\n| 1 IS NOT UNKNOWN | 0 IS NOT UNKNOWN | NULL IS NOT UNKNOWN |\n+------------------+------------------+---------------------+\n| 1 | 1 | 0 |\n+------------------+------------------+---------------------+\n\nSELECT NULL IS NOT TRUE, NULL IS NOT FALSE;\n+------------------+-------------------+\n| NULL IS NOT TRUE | NULL IS NOT FALSE |\n+------------------+-------------------+\n| 1 | 1 |\n+------------------+-------------------+\n\nURL: https://mariadb.com/kb/en/is-not/','','https://mariadb.com/kb/en/is-not/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (228,19,'IS NOT NULL','Syntax\n------\n\nIS NOT NULL\n\nDescription\n-----------\n\nTests whether a value is not NULL. See also NULL Values in MariaDB.\n\nExamples\n--------\n\nSELECT 1 IS NOT NULL, 0 IS NOT NULL, NULL IS NOT NULL;\n+---------------+---------------+------------------+\n| 1 IS NOT NULL | 0 IS NOT NULL | NULL IS NOT NULL |\n+---------------+---------------+------------------+\n| 1 | 1 | 0 |\n+---------------+---------------+------------------+\n\nURL: https://mariadb.com/kb/en/is-not-null/','','https://mariadb.com/kb/en/is-not-null/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (229,19,'IS NULL','Syntax\n------\n\nIS NULL\n\nDescription\n-----------\n\nTests whether a value is NULL. See also NULL Values in MariaDB.\n\nExamples\n--------\n\nSELECT 1 IS NULL, 0 IS NULL, NULL IS NULL;\n+-----------+-----------+--------------+\n| 1 IS NULL | 0 IS NULL | NULL IS NULL |\n+-----------+-----------+--------------+\n| 0 | 0 | 1 |\n+-----------+-----------+--------------+\n\nCompatibility\n-------------\n\nSome ODBC applications use the syntax auto_increment_field IS NOT NULL to find\nthe latest row that was inserted with an autogenerated key value. If your\napplications need this, you can set the sql_auto_is_null variable to 1.\n\nSET @@sql_auto_is_null=1;\nCREATE TABLE t1 (auto_increment_column INT NOT NULL AUTO_INCREMENT PRIMARY\nKEY);\nINSERT INTO t1 VALUES (NULL);\nSELECT * FROM t1 WHERE auto_increment_column IS NULL;\n\n+-----------------------+\n| auto_increment_column |\n+-----------------------+\n| 1 |\n+-----------------------+\n\nURL: https://mariadb.com/kb/en/is-null/','','https://mariadb.com/kb/en/is-null/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (230,19,'ISNULL','Syntax\n------\n\nISNULL(expr)\n\nDescription\n-----------\n\nIf expr is NULL, ISNULL() returns 1, otherwise it returns 0.\n\nSee also NULL Values in MariaDB.\n\nExamples\n--------\n\nSELECT ISNULL(1+1);\n+-------------+\n| ISNULL(1+1) |\n+-------------+\n| 0 |\n+-------------+\n\nSELECT ISNULL(1/0);\n+-------------+\n| ISNULL(1/0) |\n+-------------+\n| 1 |\n+-------------+\n\nURL: https://mariadb.com/kb/en/isnull/','','https://mariadb.com/kb/en/isnull/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (231,19,'LEAST','Syntax\n------\n\nLEAST(value1,value2,...)\n\nDescription\n-----------\n\nWith two or more arguments, returns the smallest (minimum-valued) argument.\nThe arguments are compared using the following rules:\n\n* If the return value is used in an INTEGER context or all arguments are\ninteger-valued, they are compared as integers.\n* If the return value is used in a REAL context or all arguments are\nreal-valued, they are compared as reals.\n* If any argument is a case-sensitive string, the arguments are compared as\ncase-sensitive strings.\n* In all other cases, the arguments are compared as case-insensitive strings.\n\nLEAST() returns NULL if any argument is NULL.\n\nExamples\n--------\n\nSELECT LEAST(2,0);\n+------------+\n| LEAST(2,0) |\n+------------+\n| 0 |\n+------------+\n\nSELECT LEAST(34.0,3.0,5.0,767.0);\n+---------------------------+\n| LEAST(34.0,3.0,5.0,767.0) |\n+---------------------------+\n| 3.0 |\n+---------------------------+\n\nSELECT LEAST(\'B\',\'A\',\'C\');\n+--------------------+\n| LEAST(\'B\',\'A\',\'C\') |\n+--------------------+\n| A |\n+--------------------+\n\nURL: https://mariadb.com/kb/en/least/','','https://mariadb.com/kb/en/least/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (232,19,'NOT BETWEEN','Syntax\n------\n\nexpr NOT BETWEEN min AND max\n\nDescription\n-----------\n\nThis is the same as NOT (expr BETWEEN min AND max).\n\nNote that the meaning of the alternative form NOT expr BETWEEN min AND max is\naffected by the HIGH_NOT_PRECEDENCE SQL_MODE flag.\n\nExamples\n--------\n\nSELECT 1 NOT BETWEEN 2 AND 3;\n+-----------------------+\n| 1 NOT BETWEEN 2 AND 3 |\n+-----------------------+\n| 1 |\n+-----------------------+\n\nSELECT \'b\' NOT BETWEEN \'a\' AND \'c\';\n+-----------------------------+\n| \'b\' NOT BETWEEN \'a\' AND \'c\' |\n+-----------------------------+\n| 0 |\n+-----------------------------+\n\nNULL:\n\nSELECT 1 NOT BETWEEN 1 AND NULL;\n+--------------------------+\n| 1 NOT BETWEEN 1 AND NULL |\n+--------------------------+\n| NULL |\n+--------------------------+\n\nURL: https://mariadb.com/kb/en/not-between/','','https://mariadb.com/kb/en/not-between/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (233,19,'NOT IN','Syntax\n------\n\nexpr NOT IN (value,...)\n\nDescription\n-----------\n\nThis is the same as NOT (expr IN (value,...)).\n\nExamples\n--------\n\nSELECT 2 NOT IN (0,3,5,7);\n+--------------------+\n| 2 NOT IN (0,3,5,7) |\n+--------------------+\n| 1 |\n+--------------------+\n\nSELECT \'wefwf\' NOT IN (\'wee\',\'wefwf\',\'weg\');\n+--------------------------------------+\n| \'wefwf\' NOT IN (\'wee\',\'wefwf\',\'weg\') |\n+--------------------------------------+\n| 0 |\n+--------------------------------------+\n\nSELECT 1 NOT IN (\'1\', \'2\', \'3\');\n+--------------------------+\n| 1 NOT IN (\'1\', \'2\', \'3\') |\n+--------------------------+\n| 0 |\n+--------------------------+\n\nNULL:\n\nSELECT NULL NOT IN (1, 2, 3);\n+-----------------------+\n| NULL NOT IN (1, 2, 3) |\n+-----------------------+\n| NULL |\n+-----------------------+\n\nSELECT 1 NOT IN (1, 2, NULL);\n+-----------------------+\n| 1 NOT IN (1, 2, NULL) |\n+-----------------------+\n| 0 |\n+-----------------------+\n\nSELECT 5 NOT IN (1, 2, NULL);\n+-----------------------+\n| 5 NOT IN (1, 2, NULL) |\n+-----------------------+\n| NULL |\n+-----------------------+\n\nURL: https://mariadb.com/kb/en/not-in/','','https://mariadb.com/kb/en/not-in/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (234,20,'&','Syntax\n------\n\n&\n\nDescription\n-----------\n\nBitwise AND. Converts the values to binary and compares bits. Only if both the\ncorresponding bits are 1 is the resulting bit also 1.\n\nSee also bitwise OR.\n\nExamples\n--------\n\nSELECT 2&1;\n+-----+\n| 2&1 |\n+-----+\n| 0 |\n+-----+\n\nSELECT 3&1;\n+-----+\n| 3&1 |\n+-----+\n| 1 |\n+-----+\n\nSELECT 29 & 15;\n+---------+\n| 29 & 15 |\n+---------+\n| 13 |\n+---------+\n\nURL: https://mariadb.com/kb/en/bitwise_and/','','https://mariadb.com/kb/en/bitwise_and/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (235,20,'<<','Syntax\n------\n\nvalue1 << value2\n\nDescription\n-----------\n\nConverts a longlong (BIGINT) number (value1) to binary and shifts value2 units\nto the left.\n\nExamples\n--------\n\nSELECT 1 << 2;\n+--------+\n| 1 << 2 |\n+--------+\n| 4 |\n+--------+\n\nURL: https://mariadb.com/kb/en/shift-left/','','https://mariadb.com/kb/en/shift-left/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (236,20,'>>','Syntax\n------\n\nvalue1 >> value2\n\nDescription\n-----------\n\nConverts a longlong (BIGINT) number (value1) to binary and shifts value2 units\nto the right.\n\nExamples\n--------\n\nSELECT 4 >> 2;\n+--------+\n| 4 >> 2 |\n+--------+\n| 1 |\n+--------+\n\nURL: https://mariadb.com/kb/en/shift-right/','','https://mariadb.com/kb/en/shift-right/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (237,20,'BIT_COUNT','Syntax\n------\n\nBIT_COUNT(N)\n\nDescription\n-----------\n\nReturns the number of bits that are set in the argument N.\n\nExamples\n--------\n\nSELECT BIT_COUNT(29), BIT_COUNT(b\'101010\');\n+---------------+----------------------+\n| BIT_COUNT(29) | BIT_COUNT(b\'101010\') |\n+---------------+----------------------+\n| 4 | 3 |\n+---------------+----------------------+\n\nURL: https://mariadb.com/kb/en/bit_count/','','https://mariadb.com/kb/en/bit_count/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (238,20,'^','Syntax\n------\n\n^\n\nDescription\n-----------\n\nBitwise XOR. Converts the values to binary and compares bits. If one (and only\none) of the corresponding bits is 1 is the resulting bit also 1.\n\nExamples\n--------\n\nSELECT 1 ^ 1;\n+-------+\n| 1 ^ 1 |\n+-------+\n| 0 |\n+-------+\n\nSELECT 1 ^ 0;\n+-------+\n| 1 ^ 0 |\n+-------+\n| 1 |\n+-------+\n\nSELECT 11 ^ 3;\n+--------+\n| 11 ^ 3 |\n+--------+\n| 8 |\n+--------+\n\nURL: https://mariadb.com/kb/en/bitwise-xor/','','https://mariadb.com/kb/en/bitwise-xor/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (239,20,'|','Syntax\n------\n\n|\n\nDescription\n-----------\n\nBitwise OR. Converts the values to binary and compares bits. If either of the\ncorresponding bits has a value of 1, the resulting bit is also 1.\n\nSee also bitwise AND.\n\nExamples\n--------\n\nSELECT 2|1;\n+-----+\n| 2|1 |\n+-----+\n| 3 |\n+-----+\n\nSELECT 29 | 15;\n+---------+\n| 29 | 15 |\n+---------+\n| 31 |\n+---------+\n\nURL: https://mariadb.com/kb/en/bitwise-or/','','https://mariadb.com/kb/en/bitwise-or/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (240,20,'~','Syntax\n------\n\n~\n\nDescription\n-----------\n\nBitwise NOT. Converts the value to 4 bytes binary and inverts all bits.\n\nExamples\n--------\n\nSELECT 3 & ~1;\n+--------+\n| 3 & ~1 |\n+--------+\n| 2 |\n+--------+\n\nSELECT 5 & ~1;\n+--------+\n| 5 & ~1 |\n+--------+\n| 4 |\n+--------+\n\nURL: https://mariadb.com/kb/en/bitwise-not/','','https://mariadb.com/kb/en/bitwise-not/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (241,20,'Parentheses','Parentheses are sometimes called precedence operators - this means that they\ncan be used to change the other operator\'s precedence in an expression. The\nexpressions that are written between parentheses are computed before the\nexpressions that are written outside. Parentheses must always contain an\nexpression (that is, they cannot be empty), and can be nested.\n\nFor example, the following expressions could return different results:\n\n* NOT a OR b\n* NOT (a OR b)\n\nIn the first case, NOT applies to a, so if a is FALSE or b is TRUE, the\nexpression returns TRUE. In the second case, NOT applies to the result of a OR\nb, so if at least one of a or b is TRUE, the expression is TRUE.\n\nWhen the precedence of operators is not intuitive, you can use parentheses to\nmake it immediately clear for whoever reads the statement.\n\nThe precedence of the NOT operator can also be affected by the\nHIGH_NOT_PRECEDENCE SQL_MODE flag.\n\nOther uses\n----------\n\nParentheses must always be used to enclose subqueries.\n\nParentheses can also be used in a JOIN statement between multiple tables to\ndetermine which tables must be joined first.\n\nAlso, parentheses are used to enclose the list of parameters to be passed to\nbuilt-in functions, user-defined functions and stored routines. However, when\nno parameter is passed to a stored procedure, parentheses are optional. For\nbuiltin functions and user-defined functions, spaces are not allowed between\nthe function name and the open parenthesis, unless the IGNORE_SPACE SQL_MODE\nis set. For stored routines (and for functions if IGNORE_SPACE is set) spaces\nare allowed before the open parenthesis, including tab characters and new line\ncharacters.\n\nSyntax errors\n-------------\n\nIf there are more open parentheses than closed parentheses, the error usually\nlooks like this:\n\nERROR 1064 (42000): You have an error in your SQL syntax; check the manual that\ncorresponds to your MariaDB server version for the right syntax to use near \'\'\na\nt line 1\n\nNote the empty string.\n\nIf there are more closed parentheses than open parentheses, the error usually\nlooks like this:\n\nERROR 1064 (42000): You have an error in your SQL syntax; check the manual that\ncorresponds to your MariaDB server version for the right syntax to use near \')\'\nat line 1\n\nNote the quoted closed parenthesis.\n\nURL: https://mariadb.com/kb/en/parentheses/','','https://mariadb.com/kb/en/parentheses/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (242,20,'TRUE FALSE','Description\n-----------\n\nThe constants TRUE and FALSE evaluate to 1 and 0, respectively. The constant\nnames can be written in any lettercase.\n\nExamples\n--------\n\nSELECT TRUE, true, FALSE, false;\n+------+------+-------+-------+\n| TRUE | TRUE | FALSE | FALSE |\n+------+------+-------+-------+\n| 1 | 1 | 0 | 0 |\n+------+------+-------+-------+\n\nURL: https://mariadb.com/kb/en/true-false/','','https://mariadb.com/kb/en/true-false/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (243,21,'ANALYZE TABLE','Syntax\n------\n\nANALYZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name [,tbl_name ...] \n [PERSISTENT FOR [ALL|COLUMNS ([col_name [,col_name ...]])]\n [INDEXES ([index_name [,index_name ...]])]]\n\nDescription\n-----------\n\nANALYZE TABLE analyzes and stores the key distribution for a table (index\nstatistics). This statement works with MyISAM, Aria and InnoDB tables. During\nthe analysis, InnoDB will allow reads/writes, and MyISAM/Aria reads/inserts.\nFor MyISAM tables, this statement is equivalent to using myisamchk --analyze.\n\nFor more information on how the analysis works within InnoDB, see InnoDB\nLimitations.\n\nMariaDB uses the stored key distribution to decide the order in which tables\nshould be joined when you perform a join on something other than a constant.\nIn addition, key distributions can be used when deciding which indexes to use\nfor a specific table within a query.\n\nThis statement requires SELECT and INSERT privileges for the table.\n\nBy default, ANALYZE TABLE statements are written to the binary log and will be\nreplicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure the\nstatement is not written to the binary log.\n\nFrom MariaDB 10.3.19, ANALYZE TABLE statements are not logged to the binary\nlog if read_only is set. See also Read-Only Replicas.\n\nANALYZE TABLE is also supported for partitioned tables. You can use ALTER\nTABLE ... ANALYZE PARTITION to analyze one or more partitions.\n\nThe Aria storage engine supports progress reporting for the ANALYZE TABLE\nstatement.\n\nEngine-Independent Statistics\n-----------------------------\n\nANALYZE TABLE supports engine-independent statistics. See Engine-Independent\nTable Statistics: Collecting Statistics with the ANALYZE TABLE Statement for\nmore information.\n\nURL: https://mariadb.com/kb/en/analyze-table/','','https://mariadb.com/kb/en/analyze-table/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (244,21,'CHECK TABLE','Syntax\n------\n\nCHECK TABLE tbl_name [, tbl_name] ... [option] ...\n\noption = {FOR UPGRADE | QUICK | FAST | MEDIUM | EXTENDED | CHANGED}\n\nDescription\n-----------\n\nCHECK TABLE checks a table or tables for errors. CHECK TABLE works for\nArchive, Aria, CSV, InnoDB, and MyISAM tables. For Aria and MyISAM tables, the\nkey statistics are updated as well. For CSV, see also Checking and Repairing\nCSV Tables.\n\nAs an alternative, myisamchk is a commandline tool for checking MyISAM tables\nwhen the tables are not being accessed.\n\nFor checking dynamic columns integrity, COLUMN_CHECK() can be used.\n\nCHECK TABLE can also check views for problems, such as tables that are\nreferenced in the view definition that no longer exist.\n\nCHECK TABLE is also supported for partitioned tables. You can use ALTER TABLE\n... CHECK PARTITION to check one or more partitions.\n\nThe meaning of the different options are as follows - note that this can vary\na bit between storage engines:\n\n+-----+----------------------------------------------------------------------+\n| FOR | Do a very quick check if the storage format for the table has |\n| UPG | changed so that one needs to do a REPAIR. This is only needed when |\n| ADE | one upgrades between major versions of MariaDB or MySQL. This is |\n| | usually done by running mysql_upgrade. |\n+-----+----------------------------------------------------------------------+\n| FAS | Only check tables that has not been closed properly or are marked |\n| | as corrupt. Only supported by the MyISAM and Aria engines. For |\n| | other engines the table is checked normally |\n+-----+----------------------------------------------------------------------+\n| CHA | Check only tables that has changed since last REPAIR / CHECK. Only |\n| GED | supported by the MyISAM and Aria engines. For other engines the |\n| | table is checked normally. |\n+-----+----------------------------------------------------------------------+\n| QUI | Do a fast check. For MyISAM and Aria engine this means we skip |\n| K | checking the delete link chain which may take some time. |\n+-----+----------------------------------------------------------------------+\n| MED | Scan also the data files. Checks integrity between data and index |\n| UM | files with checksums. In most cases this should find all possible |\n| | errors. |\n+-----+----------------------------------------------------------------------+\n| EXT | Does a full check to verify every possible error. For MyISAM and |\n| NDE | Aria we verify for each row that all it keys exists and points to |\n| | the row. This may take a long time on big tables! |\n+-----+----------------------------------------------------------------------+\n\nFor most cases running CHECK TABLE without options or MEDIUM should be good\nenough.\n\nThe Aria storage engine supports progress reporting for this statement.\n\nIf you want to know if two tables are identical, take a look at CHECKSUM TABLE.\n\nInnoDB\n------\n\nIf CHECK TABLE finds an error in an InnoDB table, MariaDB might shutdown to\nprevent the error propagation. In this case, the problem will be reported in\nthe error log. Otherwise the table or an index might be marked as corrupted,\nto prevent use. This does not happen with some minor problems, like a wrong\nnumber of entries in a secondary index. Those problems are reported in the\noutput of CHECK TABLE.\n\nEach tablespace contains a header with metadata. This header is not checked by\nthis statement.\n\nDuring the execution of CHECK TABLE, other threads may be blocked.\n\nURL: https://mariadb.com/kb/en/check-table/','','https://mariadb.com/kb/en/check-table/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (245,21,'CHECK VIEW','Syntax\n------\n\nCHECK VIEW view_name\n\nDescription\n-----------\n\nThe CHECK VIEW statement was introduced in MariaDB 10.0.18 to assist with\nfixing MDEV-6916, an issue introduced in MariaDB 5.2 where the view algorithms\nwere swapped. It checks whether the view algorithm is correct. It is run as\npart of mysql_upgrade, and should not normally be required in regular use.\n\nURL: https://mariadb.com/kb/en/check-view/','','https://mariadb.com/kb/en/check-view/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (246,21,'CHECKSUM TABLE','Syntax\n------\n\nCHECKSUM TABLE tbl_name [, tbl_name] ... [ QUICK | EXTENDED ]\n\nDescription\n-----------\n\nCHECKSUM TABLE reports a table checksum. This is very useful if you want to\nknow if two tables are the same (for example on a master and slave).\n\nWith QUICK, the live table checksum is reported if it is available, or NULL\notherwise. This is very fast. A live checksum is enabled by specifying the\nCHECKSUM=1 table option when you create the table; currently, this is\nsupported only for Aria and MyISAM tables.\n\nWith EXTENDED, the entire table is read row by row and the checksum is\ncalculated. This can be very slow for large tables.\n\nIf neither QUICK nor EXTENDED is specified, MariaDB returns a live checksum if\nthe table storage engine supports it and scans the table otherwise.\n\nCHECKSUM TABLE requires the SELECT privilege for the table.\n\nFor a nonexistent table, CHECKSUM TABLE returns NULL and generates a warning.\n\nThe table row format affects the checksum value. If the row format changes,\nthe checksum will change. This means that when a table created with a\nMariaDB/MySQL version is upgraded to another version, the checksum value will\nprobably change.\n\nTwo identical tables should always match to the same checksum value; however,\nalso for non-identical tables there is a very slight chance that they will\nreturn the same value as the hashing algorithm is not completely\ncollision-free.\n\nDifferences Between MariaDB and MySQL\n-------------------------------------\n\nCHECKSUM TABLE may give a different result as MariaDB doesn\'t ignore NULLs in\nthe columns as MySQL 5.1 does (Later MySQL versions should calculate checksums\nthe same way as MariaDB). You can get the \'old style\' checksum in MariaDB by\nstarting mysqld with the --old option. Note however that that the MyISAM and\nAria storage engines in MariaDB are using the new checksum internally, so if\nyou are using --old, the CHECKSUM command will be slower as it needs to\ncalculate the checksum row by row. Starting from MariaDB Server 10.9, --old is\ndeprecated and will be removed in a future release. Set --old-mode or OLD_MODE\nto COMPAT_5_1_CHECKSUM to get \'old style\' checksum.\n\nURL: https://mariadb.com/kb/en/checksum-table/','','https://mariadb.com/kb/en/checksum-table/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (247,21,'OPTIMIZE TABLE','Syntax\n------\n\nOPTIMIZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE\n tbl_name [, tbl_name] ...\n [WAIT n | NOWAIT]\n\nDescription\n-----------\n\nOPTIMIZE TABLE has two main functions. It can either be used to defragment\ntables, or to update the InnoDB fulltext index.\n\nMariaDB starting with 10.3.0\n----------------------------\n\nWAIT/NOWAIT\n-----------\n\nSet the lock wait timeout. See WAIT and NOWAIT.\n\nDefragmenting\n-------------\n\nOPTIMIZE TABLE works for InnoDB (before MariaDB 10.1.1, only if the\ninnodb_file_per_table server system variable is set), Aria, MyISAM and ARCHIVE\ntables, and should be used if you have deleted a large part of a table or if\nyou have made many changes to a table with variable-length rows (tables that\nhave VARCHAR, VARBINARY, BLOB, or TEXT columns). Deleted rows are maintained\nin a linked list and subsequent INSERT operations reuse old row positions.\n\nThis statement requires SELECT and INSERT privileges for the table.\n\nBy default, OPTIMIZE TABLE statements are written to the binary log and will\nbe replicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure\nthe statement is not written to the binary log.\n\nFrom MariaDB 10.3.19, OPTIMIZE TABLE statements are not logged to the binary\nlog if read_only is set. See also Read-Only Replicas.\n\nOPTIMIZE TABLE is also supported for partitioned tables. You can use ALTER\nTABLE ... OPTIMIZE PARTITION to optimize one or more partitions.\n\nYou can use OPTIMIZE TABLE to reclaim the unused space and to defragment the\ndata file. With other storage engines, OPTIMIZE TABLE does nothing by default,\nand returns this message: \" The storage engine for the table doesn\'t support\noptimize\". However, if the server has been started with the --skip-new option,\nOPTIMIZE TABLE is linked to ALTER TABLE, and recreates the table. This\noperation frees the unused space and updates index statistics.\n\nThe Aria storage engine supports progress reporting for this statement.\n\nIf a MyISAM table is fragmented, concurrent inserts will not be performed\nuntil an OPTIMIZE TABLE statement is executed on that table, unless the\nconcurrent_insert server system variable is set to ALWAYS.\n\nUpdating an InnoDB fulltext index\n---------------------------------\n\nWhen rows are added or deleted to an InnoDB fulltext index, the index is not\nimmediately re-organized, as this can be an expensive operation. Change\nstatistics are stored in a separate location . The fulltext index is only\nfully re-organized when an OPTIMIZE TABLE statement is run.\n\nBy default, an OPTIMIZE TABLE will defragment a table. In order to use it to\nupdate fulltext index statistics, the innodb_optimize_fulltext_only system\nvariable must be set to 1. This is intended to be a temporary setting, and\nshould be reset to 0 once the fulltext index has been re-organized.\n\nSince fulltext re-organization can take a long time, the\ninnodb_ft_num_word_optimize variable limits the re-organization to a number of\nwords (2000 by default). You can run multiple OPTIMIZE statements to fully\nre-organize the index.\n\nDefragmenting InnoDB tablespaces\n--------------------------------\n\nMariaDB 10.1.1 merged the Facebook/Kakao defragmentation patch, allowing one\nto use OPTIMIZE TABLE to defragment InnoDB tablespaces. For this functionality\nto be enabled, the innodb_defragment system variable must be enabled. No new\ntables are created and there is no need to copy data from old tables to new\ntables. Instead, this feature loads n pages (determined by\ninnodb-defragment-n-pages) and tries to move records so that pages would be\nfull of records and then frees pages that are fully empty after the operation.\nNote that tablespace files (including ibdata1) will not shrink as the result\nof defragmentation, but one will get better memory utilization in the InnoDB\nbuffer pool as there are fewer data pages in use.\n\nSee Defragmenting InnoDB Tablespaces for more details.\n\nURL: https://mariadb.com/kb/en/optimize-table/','','https://mariadb.com/kb/en/optimize-table/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (248,21,'REPAIR TABLE','Syntax\n------\n\nREPAIR [NO_WRITE_TO_BINLOG | LOCAL] TABLE\n tbl_name [, tbl_name] ...\n [QUICK] [EXTENDED] [USE_FRM]\n\nDescription\n-----------\n\nREPAIR TABLE repairs a possibly corrupted table. By default, it has the same\neffect as\n\nmyisamchk --recover tbl_name\n\nor\n\naria_chk --recover tbl_name\n\nSee aria_chk and myisamchk for more.\n\nREPAIR TABLE works for Archive, Aria, CSV and MyISAM tables. For InnoDB, see\nrecovery modes. For CSV, see also Checking and Repairing CSV Tables. For\nArchive, this statement also improves compression. If the storage engine does\nnot support this statement, a warning is issued.\n\nThis statement requires SELECT and INSERT privileges for the table.\n\nBy default, REPAIR TABLE statements are written to the binary log and will be\nreplicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure the\nstatement is not written to the binary log.\n\nFrom MariaDB 10.3.19, REPAIR TABLE statements are not logged to the binary log\nif read_only is set. See also Read-Only Replicas.\n\nWhen an index is recreated, the storage engine may use a configurable buffer\nin the process. Incrementing the buffer speeds up the index creation. Aria and\nMyISAM allocate a buffer whose size is defined by aria_sort_buffer_size or\nmyisam_sort_buffer_size, also used for ALTER TABLE.\n\nREPAIR TABLE is also supported for partitioned tables. However, the USE_FRM\noption cannot be used with this statement on a partitioned table.\n\nALTER TABLE ... REPAIR PARTITION can be used to repair one or more partitions.\n\nThe Aria storage engine supports progress reporting for this statement.\n\nURL: https://mariadb.com/kb/en/repair-table/','','https://mariadb.com/kb/en/repair-table/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (249,21,'REPAIR VIEW','Syntax\n------\n\nREPAIR [NO_WRITE_TO_BINLOG | LOCAL] VIEW view_name[, view_name] ... [FROM\nMYSQL]\n\nDescription\n-----------\n\nThe REPAIR VIEW statement was introduced to assist with fixing MDEV-6916, an\nissue introduced in MariaDB 5.2 where the view algorithms were swapped\ncompared to their MySQL on disk representation. It checks whether the view\nalgorithm is correct. It is run as part of mysql_upgrade, and should not\nnormally be required in regular use.\n\nBy default it corrects the checksum and if necessary adds the mariadb-version\nfield. If the optional FROM MYSQL clause is used, and no mariadb-version field\nis present, the MERGE and TEMPTABLE algorithms are toggled.\n\nBy default, REPAIR VIEW statements are written to the binary log and will be\nreplicated. The NO_WRITE_TO_BINLOG keyword (LOCAL is an alias) will ensure the\nstatement is not written to the binary log.\n\nURL: https://mariadb.com/kb/en/repair-view/','','https://mariadb.com/kb/en/repair-view/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (250,22,'mysql.func Table','The mysql.func table stores information about user-defined functions (UDFs)\ncreated with the CREATE FUNCTION UDF statement.\n\nMariaDB starting with 10.4\n--------------------------\nIn MariaDB 10.4 and later, this table uses the Aria storage engine.\n\nMariaDB until 10.3\n------------------\nIn MariaDB 10.3 and before, this table uses the MyISAM storage engine.\n\nThe mysql.func table contains the following fields:\n\n+----------+---------+---------+-------+--------------+---------------------+\n| Field | Type | Null | Key | Default | Description |\n+----------+---------+---------+-------+--------------+---------------------+\n| name | char(64 | NO | PRI | | UDF name |\n| | | | | | |\n+----------+---------+---------+-------+--------------+---------------------+\n| ret | tinyint | NO | | 0 | |\n| | 1) | | | | |\n+----------+---------+---------+-------+--------------+---------------------+\n| dl | char(12 | NO | | | Shared library name |\n| | ) | | | | |\n+----------+---------+---------+-------+--------------+---------------------+\n| type | enum(\'f | NO | | NULL | Type, either |\n| | nction\' | | | | function or |\n| | \'aggreg | | | | aggregate. |\n| | te\') | | | | Aggregate |\n| | | | | | functions are |\n| | | | | | summary functions |\n| | | | | | such as SUM() and |\n| | | | | | AVG(). |\n+----------+---------+---------+-------+--------------+---------------------+\n\nExample\n-------\n\nSELECT * FROM mysql.func;\n+------------------------------+-----+--------------+-----------+\n| name | ret | dl | type |\n+------------------------------+-----+--------------+-----------+\n| spider_direct_sql | 2 | ha_spider.so | function |\n| spider_bg_direct_sql | 2 | ha_spider.so | aggregate |\n| spider_ping_table | 2 | ha_spider.so | function |\n| spider_copy_tables | 2 | ha_spider.so | function |\n| spider_flush_table_mon_cache | 2 | ha_spider.so | function |\n+------------------------------+-----+--------------+-----------+\n\nURL: https://mariadb.com/kb/en/mysqlfunc-table/','','https://mariadb.com/kb/en/mysqlfunc-table/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (251,22,'CREATE FUNCTION UDF','Syntax\n------\n\nCREATE [OR REPLACE] [AGGREGATE] FUNCTION [IF NOT EXISTS] function_name\n RETURNS {STRING|INTEGER|REAL|DECIMAL}\n SONAME shared_library_name\n\nDescription\n-----------\n\nA user-defined function (UDF) is a way to extend MariaDB with a new function\nthat works like a native (built-in) MariaDB function such as ABS() or CONCAT().\n\nfunction_name is the name that should be used in SQL statements to invoke the\nfunction.\n\nTo create a function, you must have the INSERT privilege for the mysql\ndatabase. This is necessary becauseCREATE FUNCTION adds a row to the\nmysql.func system table that records the function\'s name, type, and shared\nlibrary name. If you do not have this table, you should run the mysql_upgrade\ncommand to create it.\n\nUDFs need to be written in C, C++ or another language that uses C calling\nconventions, MariaDB needs to have been dynamically compiled, and your\noperating system must support dynamic loading.\n\nFor an example, see sql/udf_example.cc in the source tree. For a collection of\nexisting UDFs see http://www.mysqludf.org/.\n\nStatements making use of user-defined functions are not safe for replication.\n\nFor creating a stored function as opposed to a user-defined function, see\nCREATE FUNCTION.\n\nFor valid identifiers to use as function names, see Identifier Names.\n\nRETURNS\n-------\n\nThe RETURNS clause indicates the type of the function\'s return value, and can\nbe one of STRING, INTEGER, REAL or DECIMAL. DECIMAL functions currently return\nstring values and should be written like STRING functions.\n\nshared_library_name\n-------------------\n\nshared_library_name is the basename of the shared object file that contains\nthe code that implements the function. The file must be located in the plugin\ndirectory. This directory is given by the value of the plugin_dir system\nvariable. Note that before MariaDB/MySQL 5.1, the shared object could be\nlocated in any directory that was searched by your system\'s dynamic linker.\n\nAGGREGATE\n---------\n\nAggregate functions are summary functions such as SUM() and AVG().\n\nMariaDB starting with 10.4\n--------------------------\nAggregate UDF functions can be used as window functions.\n\nOR REPLACE\n----------\n\nMariaDB starting with 10.1.3\n----------------------------\nThe OR REPLACE clause was added in MariaDB 10.1.3\n\nIf the optional OR REPLACE clause is used, it acts as a shortcut for:\n\nDROP FUNCTION IF EXISTS function_name;\nCREATE FUNCTION name ...;\n\nIF NOT EXISTS\n-------------\n\nMariaDB starting with 10.1.3\n----------------------------\nThe IF NOT EXISTS clause was added in MariaDB 10.1.3\n\nWhen the IF NOT EXISTS clause is used, MariaDB will return a warning instead\nof an error if the specified function already exists. Cannot be used together\nwith OR REPLACE.\n\nUpgrading a UDF\n---------------\n\nTo upgrade the UDF\'s shared library, first run a DROP FUNCTION statement, then\nupgrade the shared library and finally run the CREATE FUNCTION statement. If\nyou upgrade without following this process, you may crash the server.\n\nExamples\n--------\n\nCREATE FUNCTION jsoncontains_path RETURNS integer SONAME \'ha_connect.so\';\nQuery OK, 0 rows affected (0.00 sec)\n\nOR REPLACE and IF NOT EXISTS:\n\nCREATE FUNCTION jsoncontains_path RETURNS integer SONAME \'ha_connect.so\';\nERROR 1125 (HY000): Function \'jsoncontains_path\' already exists\n\nCREATE OR REPLACE FUNCTION jsoncontains_path RETURNS integer SONAME\n\'ha_connect.so\';\nQuery OK, 0 rows affected (0.00 sec)\n\nCREATE FUNCTION IF NOT EXISTS jsoncontains_path RETURNS integer SONAME\n\'ha_connect.so\';\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+---------------------------------------------+\n| Level | Code | Message |\n+-------+------+---------------------------------------------+\n| Note | 1125 | Function \'jsoncontains_path\' already exists |\n+-------+------+---------------------------------------------+\n\nURL: https://mariadb.com/kb/en/create-function-udf/','','https://mariadb.com/kb/en/create-function-udf/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (252,22,'DROP FUNCTION UDF','Syntax\n------\n\nDROP FUNCTION [IF EXISTS] function_name\n\nDescription\n-----------\n\nThis statement drops the user-defined function (UDF) named function_name.\n\nTo drop a function, you must have the DELETE privilege for the mysql database.\nThis is because DROP FUNCTION removes the row from the mysql.func system table\nthat records the function\'s name, type and shared library name.\n\nFor dropping a stored function, see DROP FUNCTION.\n\nUpgrading a UDF\n---------------\n\nTo upgrade the UDF\'s shared library, first run a DROP FUNCTION statement, then\nupgrade the shared library and finally run the CREATE FUNCTION statement. If\nyou upgrade without following this process, you may crash the server.\n\nExamples\n--------\n\nDROP FUNCTION jsoncontains_path;\n\nIF EXISTS:\n\nDROP FUNCTION jsoncontains_path;\nERROR 1305 (42000): FUNCTION test.jsoncontains_path does not exist\n\nDROP FUNCTION IF EXISTS jsoncontains_path;\nQuery OK, 0 rows affected, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+-------+------+------------------------------------------------+\n| Level | Code | Message |\n+-------+------+------------------------------------------------+\n| Note | 1305 | FUNCTION test.jsoncontains_path does not exist |\n+-------+------+------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/drop-function-udf/','','https://mariadb.com/kb/en/drop-function-udf/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (253,22,'Creating User-Defined Functions','User-defined functions allow MariaDB to be extended with a new function that\nworks like a native (built-in) MariaDB function such as ABS() or CONCAT().\nThere are alternative ways to add a new function: writing a native function\n(which requires modifying and compiling the server source code), or writing a\nstored function.\n\nStatements making use of user-defined functions are not safe for replication.\n\nFunctions are written in C or C++, and to make use of them, the operating\nsystem must support dynamic loading.\n\nEach new SQL function requires corresponding functions written in C/C++. In\nthe list below, at least the main function - x() - and one other, are\nrequired. x should be replaced by the name of the function you are creating.\n\nAll functions need to be thread-safe, so not global or static variables that\nchange can be allocated. Memory is allocated in x_init()/ and freed in\nx_deinit().\n\nSimple Functions\n----------------\n\nx()\n---\n\nRequired for all UDFs; this is where the results are calculated.\n\n+------------------------------------------+----------------------------------+\n| C/C++ type | SQL type |\n+------------------------------------------+----------------------------------+\n| char * | STRING |\n+------------------------------------------+----------------------------------+\n| long long | INTEGER |\n+------------------------------------------+----------------------------------+\n| double | REAL |\n+------------------------------------------+----------------------------------+\n\nDECIMAL functions return string values, and so should be written accordingly.\nIt is not possible to create ROW functions.\n\nx_init()\n--------\n\nInitialization function for x(). Can be used for the following:\n\n* Check the number of arguments to X() (the SQL equivalent).\n* Verify the argument types, or to force arguments to be of a particular type\nafter the function is called.\n* Specify whether the result can be NULL.\n* Specify the maximum result length.\n* For REAL functions, specify the maximum number of decimals for the result.\n* Allocate any required memory.\n\nx_deinit()\n----------\n\nDe-initialization function for x(). Used to de-allocate memory that was\nallocated in x_init().\n\nDescription\n-----------\n\nEach time the SQL function X() is called:\n\n* MariaDB will first call the C/C++ initialization function, x_init(),\nassuming it exists. All setup will be performed, and if it returns an error,\nthe SQL statement is aborted and no further functions are called.\n* If there is no x_init() function, or it has been called and did not return\nan error, x() is then called once per row.\n* After all rows have finished processing, x_deinit() is called, if present,\nto clean up by de-allocating any memory that was allocated in x_init().\n* See User-defined Functions Calling Sequences for more details on the\nfunctions.\n\nAggregate Functions\n-------------------\n\nThe following functions are required for aggregate functions, such as AVG()\nand SUM(). When using CREATE FUNCTION, the AGGREGATE keyword is required.\n\nx_clear()\n---------\n\nUsed to reset the current aggregate, but without inserting the argument as the\ninitial aggregate value for the new group.\n\nx_add()\n-------\n\nUsed to add the argument to the current aggregate.\n\nx_remove()\n----------\n\nStarting from MariaDB 10.4, improves the support of window functions (so it is\nnot obligatory to add it) and should remove the argument from the current\naggregate.\n\nDescription\n-----------\n\nEach time the aggregate SQL function X() is called:\n\n* MariaDB will first call the C/C++ initialization function, x_init(),\nassuming it exists. All setup will be performed, and if it returns an error,\nthe SQL statement is aborted and no further functions are called.\n* If there is no x_init() function, or it has been called and did not return\nan error, x() is then called once per row.\n* After all rows have finished processing, x_deinit() is called, if present,\nto clean up by de-allocating any memory that was allocated in x_init().\n\n* MariaDB will first call the C/C++ initialization function, x_init(),\nassuming it exists. All setup will be performed, and if it returns an error,\nthe SQL statement is aborted and no further functions are called.\n* The table is sorted according to the GROUP BY expression.\n* x_clear() is called for the first row of each new group.\n* x_add() is called once per row for each row in the same group.\n* x() is called when the group changes, or after the last row, to get the\naggregate result. \n* The latter three steps are repeated until all rows have been processed.\n* After all rows have finished processing, x_deinit() is called, if present,\nto clean up by de-allocating any memory that was allocated in x_init().\n\nExamples\n--------\n\nFor an example, see sql/udf_example.cc in the source tree. For a collection of\nexisting UDFs see https://github.com/mysqludf.\n\nURL: https://mariadb.com/kb/en/creating-user-defined-functions/','','https://mariadb.com/kb/en/creating-user-defined-functions/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (254,22,'User-Defined Functions Calling Sequences','The functions described in Creating User-defined Functions are expanded on\nthis page. They are declared as follows:\n\nSimple Functions\n----------------\n\nx()\n---\n\nIf x() returns an integer, it is declared as follows:\n\nlong long x(UDF_INIT *initid, UDF_ARGS *args,\n char *is_null, char *error);\n\nIf x() returns a string (DECIMAL functions also return string values), it is\ndeclared as follows:\n\nchar *x(UDF_INIT *initid, UDF_ARGS *args,\n char *result, unsigned long *length,\n char *is_null, char *error);\n\nIf x() returns a real, it is declared as follows:\n\ndouble x(UDF_INIT *initid, UDF_ARGS *args,\n char *is_null, char *error);\n\nx_init()\n--------\n\nmy_bool x_init(UDF_INIT *initid, UDF_ARGS *args, char *message);\n\nx_deinit()\n----------\n\nvoid x_deinit(UDF_INIT *initid);\n\nDescription\n-----------\n\ninitid is a parameter passed to all three functions that points to a UDF_INIT\nstructure, used for communicating information between the functions. Its\nstructure members are:\n\n* my_bool maybe_null\nmaybe_null should be set to 1 if x_init can return a NULL value, Defaults to 1\nif any arguments are declared maybe_null.\n\n* unsigned int decimals\nNumber of decimals after the decimal point. The default, if an explicit number\nof decimals is passed in the arguments to the main function, is the maximum\nnumber of decimals, so if 9.5, 9.55 and 9.555 are passed to the function, the\ndefault would be three (based on 9.555, the maximum). If there are no\nexplicit number of decimals, the default is set to 31, or one more than the\nmaximum for the DOUBLE, FLOAT and DECIMAL types. This default can be changed\nin the function to suit the actual calculation.\n\n* unsigned int max_length\nMaximum length of the result. For integers, the default is 21. For strings,\nthe length of the longest argument. For reals, the default is 13 plus the\nnumber of decimals indicated by initid->decimals. The length includes any\nsigns or decimal points. Can also be set to 65KB or 16MB in order to return a\nBLOB. The memory remains unallocated, but this is used to decide on the data\ntype to use if the data needs to be temporarily stored.\n\n* char *ptr\nA pointer for use as required by the function. Commonly, initid->ptr is used\nto communicate allocated memory, with x_init() allocating the memory and\nassigning it to this pointer, x() using it, and x_deinit() de-allocating it.\n\n* my_bool const_item\nShould be set to 1 in x_init() if x() always returns the same value, otherwise\n0.\n\nAggregate Functions\n-------------------\n\nx_clear()\n---------\n\nx_clear() is a required function for aggregate functions, and is declared as\nfollows:\n\nvoid x_clear(UDF_INIT *initid, char *is_null, char *error);\n\nIt is called when the summary results need to be reset, that is at the\nbeginning of each new group. but also to reset the values when there were no\nmatching rows.\n\nis_null is set to point to CHAR(0) before calling x_clear().\n\nIn the case of an error, you can store the value to which the error argument\npoints (a single-byte variable, not a string string buffer) in the variable.\n\nx_reset()\n---------\n\nx_reset() is declared as follows:\n\nvoid x_reset(UDF_INIT *initid, UDF_ARGS *args,\n char *is_null, char *error);\n\nIt is called on finding the first row in a new group. Should reset the summary\nvariables, and then use UDF_ARGS as the first value in the group\'s internal\nsummary value. The function is not required if the UDF interface uses\nx_clear().\n\nx_add()\n-------\n\nx_add() is declared as follows:\n\nvoid x_add(UDF_INIT *initid, UDF_ARGS *args,\n char *is_null, char *error);\n\nIt is called for all rows belonging to the same group, and should be used to\nadd the value in UDF_ARGS to the internal summary variable.\n\nx_remove()\n----------\n\nx_remove() was added in MariaDB 10.4 and is declared as follows (same as\nx_add()):\n\nvoid x_remove(UDF_INIT* initid, UDF_ARGS* args,\n char* is_null, char *error );\n\nIt adds more efficient support of aggregate UDFs as window functions.\nx_remove() should \"subtract\" the row (reverse x_add()). In MariaDB 10.4\naggregate UDFs will work as WINDOW functions without x_remove() but it will\nnot be so efficient.\n\nIf x_remove() supported (defined) detected automatically.\n\nURL: https://mariadb.com/kb/en/user-defined-functions-calling-sequences/','','https://mariadb.com/kb/en/user-defined-functions-calling-sequences/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (255,22,'User-Defined Functions Security','The MariaDB server imposes a number of limitations on user-defined functions\nfor security purposes.\n\n* The INSERT privilege for the mysql database is required to run CREATE\nFUNCTION, as a record will be added to the mysql.func-table.\n* The DELETE privilege for the mysql database is required to run DROP FUNCTION\nas the corresponding record will be removed from the mysql.func-table.\n* UDF object files can only be placed in the plugin directory, as specified by\nthe value of the plugin_dir system variable.\n* At least one symbol, beyond the required x() - corresponding to an SQL\nfunction X()) - is required. These can be x_init(), x_deinit(), xxx_reset(),\nx_clear() and x_add() functions (see Creating User-defined Functions). The\nallow-suspicious-udfs mysqld option (by default unset) provides a workaround,\npermitting only one symbol to be used. This is not recommended, as it opens\nthe possibility of loading shared objects that are not legitimate user-defined\nfunctions.\n\nURL: https://mariadb.com/kb/en/user-defined-functions-security/','','https://mariadb.com/kb/en/user-defined-functions-security/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (256,23,'Numeric Data Type Overview','There are a number of numeric data types:\n\n* TINYINT\n* BOOLEAN - Synonym for TINYINT(1)\n* INT1 - Synonym for TINYINT\n* SMALLINT\n* INT2 - Synonym for SMALLINT\n* MEDIUMINT\n* INT3 - Synonym for MEDIUMINT\n* INT, INTEGER\n* INT4 - Synonym for INT\n* BIGINT\n* INT8 - Synonym for BIGINT\n* DECIMAL, DEC, NUMERIC, FIXED\n* FLOAT\n* DOUBLE, DOUBLE PRECISION, REAL\n* BIT\n\nSee the specific articles for detailed information on each.\n\nSIGNED, UNSIGNED and ZEROFILL\n-----------------------------\n\nMost numeric types can be defined as SIGNED, UNSIGNED or ZEROFILL, for example:\n\nTINYINT[(M)] [SIGNED | UNSIGNED | ZEROFILL]\n\nIf SIGNED, or no attribute, is specified, a portion of the numeric type will\nbe reserved for the sign (plus or minus). For example, a TINYINT SIGNED can\nrange from -128 to 127.\n\nIf UNSIGNED is specified, no portion of the numeric type is reserved for the\nsign, so for integer types range can be larger. For example, a TINYINT\nUNSIGNED can range from 0 to 255. Floating point and fixed-point types also\ncan be UNSIGNED, but this only prevents negative values from being stored and\ndoesn\'t alter the range.\n\nIf ZEROFILL is specified, the column will be set to UNSIGNED and the spaces\nused by default to pad the field are replaced with zeros. ZEROFILL is ignored\nin expressions or as part of a UNION. ZEROFILL is a non-standard MySQL and\nMariaDB enhancement.\n\nNote that although the preferred syntax indicates that the attributes are\nexclusive, more than one attribute can be specified.\n\nUntil MariaDB 10.2.7 (MDEV-8659), any combination of the attributes could be\nused in any order, with duplicates. In this case:\n\n* the presence of ZEROFILL makes the column UNSIGNED ZEROFILL.\n* the presence of UNSIGNED makes the column UNSIGNED.\n\nFrom MariaDB 10.2.8, only the following combinations are supported:\n\n* SIGNED\n* UNSIGNED\n* ZEROFILL\n* UNSIGNED ZEROFILL\n* ZEROFILL UNSIGNED\n\nThe latter two should be replaced with simply ZEROFILL, but are still accepted\nby the parser.\n\nExamples\n--------\n\nCREATE TABLE zf (\n i1 TINYINT SIGNED,\n i2 TINYINT UNSIGNED,\n i3 TINYINT ZEROFILL\n);\n\nINSERT INTO zf VALUES (2,2,2);\n\nSELECT * FROM zf;\n+------+------+------+\n| i1 | i2 | i3 |\n+------+------+------+\n| 2 | 2 | 002 |\n+------+------+------+\n\nRange\n-----\n\nWhen attempting to add a value that is out of the valid range for the numeric\ntype, MariaDB will react depending on the strict SQL_MODE setting.\n\nIf strict_mode has been set (the default from MariaDB 10.2.4), MariaDB will\nreturn an error.\n\nIf strict_mode has not been set (the default until MariaDB 10.2.3), MariaDB\nwill adjust the number to fit in the field, returning a warning.\n\nExamples\n--------\n\nWith strict_mode set:\n\nSHOW VARIABLES LIKE \'sql_mode\';\n+---------------+--------------------------------------------------------------\n----------------------------+\n| Variable_name | Value \n |\n+---------------+--------------------------------------------------------------\n----------------------------+\n| sql_mode |\nSTRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SU\nSTITUTION |\n+---------------+--------------------------------------------------------------\n----------------------------+\n\nCREATE TABLE ranges (i1 TINYINT, i2 SMALLINT, i3 TINYINT UNSIGNED);\n\nINSERT INTO ranges VALUES (257,257,257);\nERROR 1264 (22003): Out of range value for column \'i1\' at row 1\n\nSELECT * FROM ranges;\nEmpty set (0.10 sec)\n\nWith strict_mode unset:\n\nSHOW VARIABLES LIKE \'sql_mode%\';\n+---------------+-------+\n| Variable_name | Value |\n+---------------+-------+\n| sql_mode | |\n+---------------+-------+\n\nCREATE TABLE ranges (i1 TINYINT, i2 SMALLINT, i3 TINYINT UNSIGNED);\n\nINSERT INTO ranges VALUES (257,257,257);\nQuery OK, 1 row affected, 2 warnings (0.00 sec)\n\nSHOW WARNINGS;\n+---------+------+---------------------------------------------+\n| Level | Code | Message |\n+---------+------+---------------------------------------------+\n| Warning | 1264 | Out of range value for column \'i1\' at row 1 |\n| Warning | 1264 | Out of range value for column \'i3\' at row 1 |\n+---------+------+---------------------------------------------+\n2 rows in set (0.00 sec)\n\nSELECT * FROM ranges;\n+------+------+------+\n| i1 | i2 | i3 |\n+------+------+------+\n| 127 | 257 | 255 |\n+------+------+------+\n\nAuto_increment\n--------------\n\nThe AUTO_INCREMENT attribute can be used to generate a unique identity for new\nrows. For more details, see auto_increment.\n\nURL: https://mariadb.com/kb/en/numeric-data-type-overview/','','https://mariadb.com/kb/en/numeric-data-type-overview/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (257,23,'TINYINT','Syntax\n------\n\nTINYINT[(M)] [SIGNED | UNSIGNED | ZEROFILL]\n\nDescription\n-----------\n\nA very small integer. The signed range is -128 to 127. The unsigned range is 0\nto 255. For details on the attributes, see Numeric Data Type Overview.\n\nINT1 is a synonym for TINYINT. BOOL and BOOLEAN are synonyms for TINYINT(1).\n\nExamples\n--------\n\nCREATE TABLE tinyints (a TINYINT,b TINYINT UNSIGNED,c TINYINT ZEROFILL);\n\nWith strict_mode set, the default from MariaDB 10.2.4:\n\nINSERT INTO tinyints VALUES (-10,-10,-10);\nERROR 1264 (22003): Out of range value for column \'b\' at row 1\n\nINSERT INTO tinyints VALUES (-10,10,-10);\nERROR 1264 (22003): Out of range value for column \'c\' at row 1\n\nINSERT INTO tinyints VALUES (-10,10,10);\n\nSELECT * FROM tinyints;\n+------+------+------+\n| a | b | c |\n+------+------+------+\n| -10 | 10 | 010 |\n+------+------+------+\n\nINSERT INTO tinyints VALUES (128,128,128);\nERROR 1264 (22003): Out of range value for column \'a\' at row 1\n\nINSERT INTO tinyints VALUES (127,128,128);\n\nSELECT * FROM tinyints;\n+------+------+------+\n| a | b | c |\n+------+------+------+\n| -10 | 10 | 010 |\n| 127 | 128 | 128 |\n+------+------+------+\n\nWith strict_mode unset, the default until MariaDB 10.2.3:\n\nINSERT INTO tinyints VALUES (-10,-10,-10);\nQuery OK, 1 row affected, 2 warnings (0.08 sec)\nWarning (Code 1264): Out of range value for column \'b\' at row 1\nWarning (Code 1264): Out of range value for column \'c\' at row 1\n\nINSERT INTO tinyints VALUES (-10,10,-10);\nQuery OK, 1 row affected, 1 warning (0.11 sec)\nWarning (Code 1264): Out of range value for column \'c\' at row 1\n\nINSERT INTO tinyints VALUES (-10,10,10);\n\nSELECT * FROM tinyints;\n+------+------+------+\n| a | b | c |\n+------+------+------+\n| -10 | 0 | 000 |\n| -10 | 10 | 000 |\n| -10 | 10 | 010 |\n+------+------+------+\n\nINSERT INTO tinyints VALUES (128,128,128);\nQuery OK, 1 row affected, 1 warning (0.19 sec)\nWarning (Code 1264): Out of range value for column \'a\' at row 1\n\nINSERT INTO tinyints VALUES (127,128,128);\n\nSELECT * FROM tinyints;\n+------+------+------+\n| a | b | c |\n+------+------+------+\n| -10 | 0 | 000 |\n| -10 | 10 | 000 |\n| -10 | 10 | 010 |\n| 127 | 128 | 128 |\n| 127 | 128 | 128 |\n+------+------+------+\n\nURL: https://mariadb.com/kb/en/tinyint/','','https://mariadb.com/kb/en/tinyint/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (258,23,'BOOLEAN','Syntax\n------\n\nBOOL, BOOLEAN\n\nDescription\n-----------\n\nThese types are synonyms for TINYINT(1). A value of zero is considered false.\nNon-zero values are considered true.\n\nHowever, the values TRUE and FALSE are merely aliases for 1 and 0. See Boolean\nLiterals, as well as the IS operator for testing values against a boolean.\n\nExamples\n--------\n\nCREATE TABLE boo (i BOOLEAN);\n\nDESC boo;\n+-------+------------+------+-----+---------+-------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+------------+------+-----+---------+-------+\n| i | tinyint(1) | YES | | NULL | |\n+-------+------------+------+-----+---------+-------+\n\nSELECT IF(0, \'true\', \'false\');\n+------------------------+\n| IF(0, \'true\', \'false\') |\n+------------------------+\n| false |\n+------------------------+\n\nSELECT IF(1, \'true\', \'false\');\n+------------------------+\n| IF(1, \'true\', \'false\') |\n+------------------------+\n| true |\n+------------------------+\n\nSELECT IF(2, \'true\', \'false\');\n+------------------------+\n| IF(2, \'true\', \'false\') |\n+------------------------+\n| true |\n+------------------------+\n\nTRUE and FALSE as aliases for 1 and 0:\n\nSELECT IF(0 = FALSE, \'true\', \'false\');\n\n+--------------------------------+\n| IF(0 = FALSE, \'true\', \'false\') |\n+--------------------------------+\n| true |\n+--------------------------------+\n\nSELECT IF(1 = TRUE, \'true\', \'false\');\n+-------------------------------+\n| IF(1 = TRUE, \'true\', \'false\') |\n+-------------------------------+\n| true |\n+-------------------------------+\n\nSELECT IF(2 = TRUE, \'true\', \'false\');\n+-------------------------------+\n| IF(2 = TRUE, \'true\', \'false\') |\n+-------------------------------+\n| false |\n+-------------------------------+\n\nSELECT IF(2 = FALSE, \'true\', \'false\');\n+--------------------------------+\n| IF(2 = FALSE, \'true\', \'false\') |\n+--------------------------------+\n| false |\n+--------------------------------+\n\nThe last two statements display the results shown because 2 is equal to\nneither 1 nor 0.\n\nURL: https://mariadb.com/kb/en/boolean/','','https://mariadb.com/kb/en/boolean/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (259,23,'SMALLINT','Syntax\n------\n\nSMALLINT[(M)] [SIGNED | UNSIGNED | ZEROFILL]\n\nDescription\n-----------\n\nA small integer. The signed range is -32768 to 32767. The unsigned range is 0\nto 65535.\n\nIf a column has been set to ZEROFILL, all values will be prepended by zeros so\nthat the SMALLINT value contains a number of M digits.\n\nNote: If the ZEROFILL attribute has been specified, the column will\nautomatically become UNSIGNED.\n\nINT2 is a synonym for SMALLINT.\n\nFor more details on the attributes, see Numeric Data Type Overview.\n\nExamples\n--------\n\nCREATE TABLE smallints (a SMALLINT,b SMALLINT UNSIGNED,c SMALLINT ZEROFILL);\n\nWith strict_mode set, the default from MariaDB 10.2.4:\n\nINSERT INTO smallints VALUES (-10,-10,-10);\nERROR 1264 (22003): Out of range value for column \'b\' at row 1\n\nINSERT INTO smallints VALUES (-10,10,-10);\nERROR 1264 (22003): Out of range value for column \'c\' at row 1\n\nINSERT INTO smallints VALUES (-10,10,10);\n\nINSERT INTO smallints VALUES (32768,32768,32768);\nERROR 1264 (22003): Out of range value for column \'a\' at row 1\n\nINSERT INTO smallints VALUES (32767,32768,32768);\n\nSELECT * FROM smallints;\n+-------+-------+-------+\n| a | b | c |\n+-------+-------+-------+\n| -10 | 10 | 00010 |\n| 32767 | 32768 | 32768 |\n+-------+-------+-------+\n\nWith strict_mode unset, the default until MariaDB 10.2.3:\n\nINSERT INTO smallints VALUES (-10,-10,-10);\nQuery OK, 1 row affected, 2 warnings (0.09 sec)\nWarning (Code 1264): Out of range value for column \'b\' at row 1\nWarning (Code 1264): Out of range value for column \'c\' at row 1\n\nINSERT INTO smallints VALUES (-10,10,-10);\nQuery OK, 1 row affected, 1 warning (0.08 sec)\nWarning (Code 1264): Out of range value for column \'c\' at row 1\n\nINSERT INTO smallints VALUES (-10,10,10);\n\nINSERT INTO smallints VALUES (32768,32768,32768);\nQuery OK, 1 row affected, 1 warning (0.04 sec)\nWarning (Code 1264): Out of range value for column \'a\' at row 1\n\nINSERT INTO smallints VALUES (32767,32768,32768);\n\nSELECT * FROM smallints;\n+-------+-------+-------+\n| a | b | c |\n+-------+-------+-------+\n| -10 | 0 | 00000 |\n| -10 | 10 | 00000 |\n| -10 | 10 | 00010 |\n| 32767 | 32768 | 32768 |\n| 32767 | 32768 | 32768 |\n+-------+-------+-------+\n\nURL: https://mariadb.com/kb/en/smallint/','','https://mariadb.com/kb/en/smallint/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (260,23,'MEDIUMINT','Syntax\n------\n\nMEDIUMINT[(M)] [SIGNED | UNSIGNED | ZEROFILL]\n\nDescription\n-----------\n\nA medium-sized integer. The signed range is -8388608 to 8388607. The unsigned\nrange is 0 to 16777215.\n\nZEROFILL pads the integer with zeroes and assumes UNSIGNED (even if UNSIGNED\nis not specified).\n\nINT3 is a synonym for MEDIUMINT.\n\nFor details on the attributes, see Numeric Data Type Overview.\n\nExamples\n--------\n\nCREATE TABLE mediumints (a MEDIUMINT,b MEDIUMINT UNSIGNED,c MEDIUMINT\nZEROFILL);\n\nDESCRIBE mediumints;\n+-------+--------------------------------+------+-----+---------+-------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+--------------------------------+------+-----+---------+-------+\n| a | mediumint(9) | YES | | NULL | |\n| b | mediumint(8) unsigned | YES | | NULL | |\n| c | mediumint(8) unsigned zerofill | YES | | NULL | |\n+-------+--------------------------------+------+-----+---------+-------+\n\nWith strict_mode set, the default from MariaDB 10.2.4:\n\nINSERT INTO mediumints VALUES (-10,-10,-10);\nERROR 1264 (22003): Out of range value for column \'b\' at row 1\n\nINSERT INTO mediumints VALUES (-10,10,-10);\nERROR 1264 (22003): Out of range value for column \'c\' at row 1\n\nINSERT INTO mediumints VALUES (-10,10,10);\n\nINSERT INTO mediumints VALUES (8388608,8388608,8388608);\nERROR 1264 (22003): Out of range value for column \'a\' at row 1\n\nINSERT INTO mediumints VALUES (8388607,8388608,8388608);\n\nSELECT * FROM mediumints;\n+---------+---------+----------+\n| a | b | c |\n+---------+---------+----------+\n| -10 | 10 | 00000010 |\n| 8388607 | 8388608 | 08388608 |\n+---------+---------+----------+\n\nWith strict_mode unset, the default until MariaDB 10.2.3:\n\nINSERT INTO mediumints VALUES (-10,-10,-10);\nQuery OK, 1 row affected, 2 warnings (0.05 sec)\nWarning (Code 1264): Out of range value for column \'b\' at row 1\nWarning (Code 1264): Out of range value for column \'c\' at row 1\n\nINSERT INTO mediumints VALUES (-10,10,-10);\nQuery OK, 1 row affected, 1 warning (0.08 sec)\nWarning (Code 1264): Out of range value for column \'c\' at row 1\n\nINSERT INTO mediumints VALUES (-10,10,10);\n\nINSERT INTO mediumints VALUES (8388608,8388608,8388608);\nQuery OK, 1 row affected, 1 warning (0.05 sec)\nWarning (Code 1264): Out of range value for column \'a\' at row 1\n\nINSERT INTO mediumints VALUES (8388607,8388608,8388608);\n\nSELECT * FROM mediumints;\n+---------+---------+----------+\n| a | b | c |\n+---------+---------+----------+\n| -10 | 0 | 00000000 |\n| -10 | 0 | 00000000 |\n| -10 | 10 | 00000000 |\n| -10 | 10 | 00000010 |\n| 8388607 | 8388608 | 08388608 |\n| 8388607 | 8388608 | 08388608 |\n+---------+---------+----------+\n\nURL: https://mariadb.com/kb/en/mediumint/','','https://mariadb.com/kb/en/mediumint/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (261,23,'INT','Syntax\n------\n\nINT[(M)] [SIGNED | UNSIGNED | ZEROFILL]\nINTEGER[(M)] [SIGNED | UNSIGNED | ZEROFILL]\n\nDescription\n-----------\n\nA normal-size integer. When marked UNSIGNED, it ranges from 0 to 4294967295,\notherwise its range is -2147483648 to 2147483647 (SIGNED is the default). If a\ncolumn has been set to ZEROFILL, all values will be prepended by zeros so that\nthe INT value contains a number of M digits. INTEGER is a synonym for INT.\n\nNote: If the ZEROFILL attribute has been specified, the column will\nautomatically become UNSIGNED.\n\nINT4 is a synonym for INT.\n\nFor details on the attributes, see Numeric Data Type Overview.\n\nExamples\n--------\n\nCREATE TABLE ints (a INT,b INT UNSIGNED,c INT ZEROFILL);\n\nWith strict_mode set, the default from MariaDB 10.2.4:\n\nINSERT INTO ints VALUES (-10,-10,-10);\nERROR 1264 (22003): Out of range value for column \'b\' at row 1\n\nINSERT INTO ints VALUES (-10,10,-10);\nERROR 1264 (22003): Out of range value for column \'c\' at row 1\n\nINSERT INTO ints VALUES (-10,10,10);\n\nINSERT INTO ints VALUES (2147483648,2147483648,2147483648);\nERROR 1264 (22003): Out of range value for column \'a\' at row 1\n\nINSERT INTO ints VALUES (2147483647,2147483648,2147483648);\n\nSELECT * FROM ints;\n+------------+------------+------------+\n| a | b | c |\n+------------+------------+------------+\n| -10 | 10 | 0000000010 |\n| 2147483647 | 2147483648 | 2147483648 |\n+------------+------------+------------+\n\nWith strict_mode unset, the default until MariaDB 10.2.3:\n\nINSERT INTO ints VALUES (-10,-10,-10);\nQuery OK, 1 row affected, 2 warnings (0.10 sec)\nWarning (Code 1264): Out of range value for column \'b\' at row 1\nWarning (Code 1264): Out of range value for column \'c\' at row 1\n\nINSERT INTO ints VALUES (-10,10,-10);\nQuery OK, 1 row affected, 1 warning (0.08 sec)\nWarning (Code 1264): Out of range value for column \'c\' at row 1\n\nINSERT INTO ints VALUES (-10,10,10);\n\nINSERT INTO ints VALUES (2147483648,2147483648,2147483648);\nQuery OK, 1 row affected, 1 warning (0.07 sec)\nWarning (Code 1264): Out of range value for column \'a\' at row 1\n\nINSERT INTO ints VALUES (2147483647,2147483648,2147483648);\n\nSELECT * FROM ints;\n+------------+------------+------------+\n| a | b | c |\n+------------+------------+------------+\n| -10 | 0 | 0000000000 |\n| -10 | 10 | 0000000000 |\n| -10 | 10 | 0000000010 |\n| 2147483647 | 2147483648 | 2147483648 |\n| 2147483647 | 2147483648 | 2147483648 |\n+------------+------------+------------+\n\nURL: https://mariadb.com/kb/en/int/','','https://mariadb.com/kb/en/int/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (262,23,'BIGINT','Syntax\n------\n\nBIGINT[(M)] [SIGNED | UNSIGNED | ZEROFILL]\n\nDescription\n-----------\n\nA large integer. The signed range is -9223372036854775808 to\n9223372036854775807. The unsigned range is 0 to 18446744073709551615.\n\nIf a column has been set to ZEROFILL, all values will be prepended by zeros so\nthat the BIGINT value contains a number of M digits.\n\nNote: If the ZEROFILL attribute has been specified, the column will\nautomatically become UNSIGNED.\n\nFor more details on the attributes, see Numeric Data Type Overview.\n\nSERIAL is an alias for:\n\nBIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE\n\nINT8 is a synonym for BIGINT.\n\nExamples\n--------\n\nCREATE TABLE bigints (a BIGINT,b BIGINT UNSIGNED,c BIGINT ZEROFILL);\n\nWith strict_mode set, the default from MariaDB 10.2.4:\n\nINSERT INTO bigints VALUES (-10,-10,-10);\nERROR 1264 (22003): Out of range value for column \'b\' at row 1\n\nINSERT INTO bigints VALUES (-10,10,-10);\nERROR 1264 (22003): Out of range value for column \'c\' at row 1\n\nINSERT INTO bigints VALUES (-10,10,10);\n\nINSERT INTO bigints VALUES\n(9223372036854775808,9223372036854775808,9223372036854775808);\nERROR 1264 (22003): Out of range value for column \'a\' at row 1\n\nINSERT INTO bigints VALUES\n(9223372036854775807,9223372036854775808,9223372036854775808);\n\nSELECT * FROM bigints;\n+---------------------+---------------------+----------------------+\n| a | b | c |\n+---------------------+---------------------+----------------------+\n| -10 | 10 | 00000000000000000010 |\n| 9223372036854775807 | 9223372036854775808 | 09223372036854775808 |\n+---------------------+---------------------+----------------------+\n\nWith strict_mode unset, the default until MariaDB 10.2.3:\n\nINSERT INTO bigints VALUES (-10,-10,-10);\nQuery OK, 1 row affected, 2 warnings (0.08 sec)\nWarning (Code 1264): Out of range value for column \'b\' at row 1\nWarning (Code 1264): Out of range value for column \'c\' at row 1\n\nINSERT INTO bigints VALUES (-10,10,-10);\nQuery OK, 1 row affected, 1 warning (0.08 sec)\nWarning (Code 1264): Out of range value for column \'c\' at row 1\n\nINSERT INTO bigints VALUES (-10,10,10);\n\nINSERT INTO bigints VALUES\n(9223372036854775808,9223372036854775808,9223372036854775808);\nQuery OK, 1 row affected, 1 warning (0.07 sec)\nWarning (Code 1264): Out of range value for column \'a\' at row 1\n\nINSERT INTO bigints VALUES\n(9223372036854775807,9223372036854775808,9223372036854775808);\n\nSELECT * FROM bigints;\n+---------------------+---------------------+----------------------+\n| a | b | c |\n+---------------------+---------------------+----------------------+\n| -10 | 0 | 00000000000000000000 |\n| -10 | 10 | 00000000000000000000 |\n| -10 | 10 | 00000000000000000010 |\n| 9223372036854775807 | 9223372036854775808 | 09223372036854775808 |\n| 9223372036854775807 | 9223372036854775808 | 09223372036854775808 |\n+---------------------+---------------------+----------------------+\n\nURL: https://mariadb.com/kb/en/bigint/','','https://mariadb.com/kb/en/bigint/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (263,23,'DECIMAL','Syntax\n------\n\nDECIMAL[(M[,D])] [SIGNED | UNSIGNED | ZEROFILL]\n\nDescription\n-----------\n\nA packed \"exact\" fixed-point number. M is the total number of digits (the\nprecision) and D is the number of digits after the decimal point (the scale).\n\n* The decimal point and (for negative numbers) the \"-\" sign are not\ncounted in M. \n* If D is 0, values have no decimal point or fractional\npart and on INSERT the value will be rounded to the nearest DECIMAL. \n* The maximum number of digits (M) for DECIMAL is 65. \n* The maximum number of supported decimals (D) is 30 before MariadB 10.2.1 and\n38 afterwards. \n* If D is omitted, the default is 0. If M is omitted, the default is 10.\n\nUNSIGNED, if specified, disallows negative values.\n\nZEROFILL, if specified, pads the number with zeros, up to the total number of\ndigits specified by M.\n\nAll basic calculations (+, -, *, /) with DECIMAL columns are done with a\nprecision of 65 digits.\n\nFor more details on the attributes, see Numeric Data Type Overview.\n\nDEC, NUMERIC and FIXED are synonyms, as well as NUMBER in Oracle mode from\nMariaDB 10.3.\n\nExamples\n--------\n\nCREATE TABLE t1 (d DECIMAL UNSIGNED ZEROFILL);\n\nINSERT INTO t1 VALUES (1),(2),(3),(4.0),(5.2),(5.7);\nQuery OK, 6 rows affected, 2 warnings (0.16 sec)\nRecords: 6 Duplicates: 0 Warnings: 2\n\nNote (Code 1265): Data truncated for column \'d\' at row 5\nNote (Code 1265): Data truncated for column \'d\' at row 6\n\nSELECT * FROM t1;\n+------------+\n| d |\n+------------+\n| 0000000001 |\n| 0000000002 |\n| 0000000003 |\n| 0000000004 |\n| 0000000005 |\n| 0000000006 |\n+------------+\n\nWith strict_mode set, the default from MariaDB 10.2.4:\n\nINSERT INTO t1 VALUES (-7);\nERROR 1264 (22003): Out of range value for column \'d\' at row 1\n\nWith strict_mode unset, the default until MariaDB 10.2.3:\n\nINSERT INTO t1 VALUES (-7);\nQuery OK, 1 row affected, 1 warning (0.02 sec)\nWarning (Code 1264): Out of range value for column \'d\' at row 1\n\nSELECT * FROM t1;\n+------------+\n| d |\n+------------+\n| 0000000001 |\n| 0000000002 |\n| 0000000003 |\n| 0000000004 |\n| 0000000005 |\n| 0000000006 |\n| 0000000000 |\n+------------+\n\nURL: https://mariadb.com/kb/en/decimal/','','https://mariadb.com/kb/en/decimal/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (264,23,'FLOAT','Syntax\n------\n\nFLOAT[(M,D)] [SIGNED | UNSIGNED | ZEROFILL]\n\nDescription\n-----------\n\nA small (single-precision) floating-point number (see DOUBLE for a\nregular-size floating point number). Allowable values are:\n\n* -3.402823466E+38 to -1.175494351E-38\n* 0\n* 1.175494351E-38 to 3.402823466E+38.\n\nThese are the theoretical limits, based on the IEEE standard. The actual range\nmight be slightly smaller depending on your hardware or operating system.\n\nM is the total number of digits and D is the number of digits following the\ndecimal point. If M and D are omitted, values are stored to the limits allowed\nby the hardware. A single-precision floating-point number is accurate to\napproximately 7 decimal places.\n\nUNSIGNED, if specified, disallows negative values.\n\nUsing FLOAT might give you some unexpected problems because all calculations\nin MariaDB are done with double precision. See Floating Point Accuracy.\n\nFor more details on the attributes, see Numeric Data Type Overview.\n\nURL: https://mariadb.com/kb/en/float/','','https://mariadb.com/kb/en/float/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (265,23,'DOUBLE','Syntax\n------\n\nDOUBLE[(M,D)] [SIGNED | UNSIGNED | ZEROFILL]\nDOUBLE PRECISION[(M,D)] [SIGNED | UNSIGNED | ZEROFILL]\nREAL[(M,D)] [SIGNED | UNSIGNED | ZEROFILL]\n\nDescription\n-----------\n\nA normal-size (double-precision) floating-point number (see FLOAT for a\nsingle-precision floating-point number).\n\nAllowable values are:\n\n* -1.7976931348623157E+308 to -2.2250738585072014E-308\n* 0\n* 2.2250738585072014E-308 to 1.7976931348623157E+308\n\nThese are the theoretical limits, based on the IEEE standard. The actual range\nmight be slightly smaller depending on your hardware or operating system.\n\nM is the total number of digits and D is the number of digits following the\ndecimal point. If M and D are omitted, values are stored to the limits allowed\nby the hardware. A double-precision floating-point number is accurate to\napproximately 15 decimal places.\n\nUNSIGNED, if specified, disallows negative values.\n\nZEROFILL, if specified, pads the number with zeros, up to the total number of\ndigits specified by M.\n\nREAL and DOUBLE PRECISION are synonyms, unless the REAL_AS_FLOAT SQL mode is\nenabled, in which case REAL is a synonym for FLOAT rather than DOUBLE.\n\nSee Floating Point Accuracy for issues when using floating-point numbers.\n\nFor more details on the attributes, see Numeric Data Type Overview.\n\nExamples\n--------\n\nCREATE TABLE t1 (d DOUBLE(5,0) zerofill);\n\nINSERT INTO t1 VALUES (1),(2),(3),(4);\n\nSELECT * FROM t1;\n+-------+\n| d |\n+-------+\n| 00001 |\n| 00002 |\n| 00003 |\n| 00004 |\n+-------+\n\nURL: https://mariadb.com/kb/en/double/','','https://mariadb.com/kb/en/double/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (266,23,'BIT','Syntax\n------\n\nBIT[(M)]\n\nDescription\n-----------\n\nA bit-field type. M indicates the number of bits per value, from 1 to 64. The\ndefault is 1 if M is omitted.\n\nBit values can be inserted with b\'value\' notation, where value is the bit\nvalue in 0\'s and 1\'s.\n\nBit fields are automatically zero-padded from the left to the full length of\nthe bit, so for example in a BIT(4) field, \'10\' is equivalent to \'0010\'.\n\nBits are returned as binary, so to display them, either add 0, or use a\nfunction such as HEX, OCT or BIN to convert them.\n\nExamples\n--------\n\nCREATE TABLE b ( b1 BIT(8) );\n\nWith strict_mode set, the default from MariaDB 10.2.4:\n\nINSERT INTO b VALUES (b\'11111111\');\n\nINSERT INTO b VALUES (b\'01010101\');\n\nINSERT INTO b VALUES (b\'1111111111111\');\nERROR 1406 (22001): Data too long for column \'b1\' at row 1\n\nSELECT b1+0, HEX(b1), OCT(b1), BIN(b1) FROM b;\n+------+---------+---------+----------+\n| b1+0 | HEX(b1) | OCT(b1) | BIN(b1) |\n+------+---------+---------+----------+\n| 255 | FF | 377 | 11111111 |\n| 85 | 55 | 125 | 1010101 |\n+------+---------+---------+----------+\n\nWith strict_mode unset, the default until MariaDB 10.2.3:\n\nINSERT INTO b VALUES (b\'11111111\'),(b\'01010101\'),(b\'1111111111111\');\nQuery OK, 3 rows affected, 1 warning (0.10 sec)\nRecords: 3 Duplicates: 0 Warnings: 1\n\nSHOW WARNINGS;\n+---------+------+---------------------------------------------+\n| Level | Code | Message |\n+---------+------+---------------------------------------------+\n| Warning | 1264 | Out of range value for column \'b1\' at row 3 |\n+---------+------+---------------------------------------------+\n\nSELECT b1+0, HEX(b1), OCT(b1), BIN(b1) FROM b;\n+------+---------+---------+----------+\n| b1+0 | HEX(b1) | OCT(b1) | BIN(b1) |\n+------+---------+---------+----------+\n| 255 | FF | 377 | 11111111 |\n| 85 | 55 | 125 | 1010101 |\n| 255 | FF | 377 | 11111111 |\n+------+---------+---------+----------+\n\nURL: https://mariadb.com/kb/en/bit/','','https://mariadb.com/kb/en/bit/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (267,23,'Floating-point Accuracy','Due to their nature, not all floating-point numbers can be stored with exact\nprecision. Hardware architecture, the CPU or even the compiler version and\noptimization level may affect the precision.\n\nIf you are comparing DOUBLEs or FLOATs with numeric decimals, it is not safe\nto use the equality operator.\n\nSometimes, changing a floating-point number from single-precision (FLOAT) to\ndouble-precision (DOUBLE) will fix the problem.\n\nExample\n-------\n\nf1, f2 and f3 have seemingly identical values across each row, but due to\nfloating point accuracy, the results may be unexpected.\n\nCREATE TABLE fpn (id INT, f1 FLOAT, f2 DOUBLE, f3 DECIMAL (10,3));\nINSERT INTO fpn VALUES (1,2,2,2),(2,0.1,0.1,0.1);\n\nSELECT * FROM fpn WHERE f1*f1 = f2*f2;\n+------+------+------+-------+\n| id | f1 | f2 | f3 |\n+------+------+------+-------+\n| 1 | 2 | 2 | 2.000 |\n+------+------+------+-------+\n\nThe reason why only one instead of two rows was returned becomes clear when we\nsee how the floating point squares were evaluated.\n\nSELECT f1*f1, f2*f2, f3*f3 FROM fpn;\n+----------------------+----------------------+----------+\n| f1*f1 | f2*f2 | f3*f3 |\n+----------------------+----------------------+----------+\n| 4 | 4 | 4.000000 |\n| 0.010000000298023226 | 0.010000000000000002 | 0.010000 |\n+----------------------+----------------------+----------+\n\nURL: https://mariadb.com/kb/en/floating-point-accuracy/','','https://mariadb.com/kb/en/floating-point-accuracy/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (268,23,'BINARY','This page describes the BINARY data type. For details about the operator, see\nBinary Operator.\n\nSyntax\n------\n\nBINARY(M)\n\nDescription\n-----------\n\nThe BINARY type is similar to the CHAR type, but stores binary byte strings\nrather than non-binary character strings. M represents the column length in\nbytes.\n\nIt contains no character set, and comparison and sorting are based on the\nnumeric value of the bytes.\n\nIf the maximum length is exceeded, and SQL strict mode is not enabled , the\nextra characters will be dropped with a warning. If strict mode is enabled, an\nerror will occur.\n\nBINARY values are right-padded with 0x00 (the zero byte) to the specified\nlength when inserted. The padding is not removed on select, so this needs to\nbe taken into account when sorting and comparing, where all bytes are\nsignificant. The zero byte, 0x00 is less than a space for comparison purposes.\n\nExamples\n--------\n\nInserting too many characters, first with strict mode off, then with it on:\n\nCREATE TABLE bins (a BINARY(10));\n\nINSERT INTO bins VALUES(\'12345678901\');\nQuery OK, 1 row affected, 1 warning (0.04 sec)\n\nSELECT * FROM bins;\n+------------+\n| a |\n+------------+\n| 1234567890 |\n+------------+\n\nSET sql_mode=\'STRICT_ALL_TABLES\';\n\nINSERT INTO bins VALUES(\'12345678901\');\nERROR 1406 (22001): Data too long for column \'a\' at row 1\n\nSorting is performed with the byte value:\n\nTRUNCATE bins;\n\nINSERT INTO bins VALUES(\'A\'),(\'B\'),(\'a\'),(\'b\');\n\nSELECT * FROM bins ORDER BY a;\n+------+\n| a |\n+------+\n| A |\n| B |\n| a |\n| b |\n+------+\n\nUsing CAST to sort as a CHAR instead:\n\nSELECT * FROM bins ORDER BY CAST(a AS CHAR);\n+------+\n| a |\n+------+\n| a |\n| A |\n| b |\n| B |\n+------+\n\nThe field is a BINARY(10), so padding of two \'\\0\'s are inserted, causing\ncomparisons that don\'t take this into account to fail:\n\nTRUNCATE bins;\n\nINSERT INTO bins VALUES(\'12345678\');\n\nSELECT a = \'12345678\', a = \'12345678\\0\\0\' from bins;\n+----------------+--------------------+\n| a = \'12345678\' | a = \'12345678\\0\\0\' |\n+----------------+--------------------+\n| 0 | 1 |\n+----------------+--------------------+\n\nURL: https://mariadb.com/kb/en/binary/','','https://mariadb.com/kb/en/binary/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (269,23,'BLOB','Syntax\n------\n\nBLOB[(M)]\n\nDescription\n-----------\n\nA BLOB column with a maximum length of 65,535 (216 - 1) bytes. Each BLOB value\nis stored using a two-byte length prefix that indicates the number of bytes in\nthe value.\n\nAn optional length M can be given for this type. If this is done, MariaDB\ncreates the column as the smallest BLOB type large enough to hold values M\nbytes long.\n\nBLOBS can also be used to store dynamic columns.\n\nBefore MariaDB 10.2.1, BLOB and TEXT columns could not be assigned a DEFAULT\nvalue. This restriction was lifted in MariaDB 10.2.1.\n\nIndexing\n--------\n\nMariaDB starting with 10.4\n--------------------------\nFrom MariaDB 10.4, it is possible to set a unique index on a column that uses\nthe BLOB data type. In previous releases this was not possible, as the index\nwould only guarantee the uniqueness of a fixed number of characters.\n\nOracle Mode\n-----------\n\nMariaDB starting with 10.3\n--------------------------\nIn Oracle mode from MariaDB 10.3, BLOB is a synonym for LONGBLOB.\n\nURL: https://mariadb.com/kb/en/blob/','','https://mariadb.com/kb/en/blob/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (270,23,'BLOB and TEXT Data Types','Description\n-----------\n\nA BLOB is a binary large object that can hold a variable amount of data. The\nfour BLOB types are\n\n* TINYBLOB,\n* BLOB, \n* MEDIUMBLOB, and\n* LONGBLOB.\n\nThese differ only in the maximum length of the values they can hold.\n\nThe TEXT types are\n\n* TINYTEXT,\n* TEXT,\n* MEDIUMTEXT, and\n* LONGTEXT.\n* JSON (alias for LONGTEXT)\n\nThese correspond to the four BLOB types and have the same maximum lengths and\nstorage requirements.\n\nMariaDB starting with 10.2.1\n----------------------------\nStarting from MariaDB 10.2.1, BLOB and TEXT columns can have a DEFAULT value.\n\nMariaDB starting with 10.4.3\n----------------------------\nFrom MariaDB 10.4, it is possible to set a unique index on columns that use\nthe BLOB or TEXT data types.\n\nURL: https://mariadb.com/kb/en/blob-and-text-data-types/','','https://mariadb.com/kb/en/blob-and-text-data-types/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (271,23,'CHAR','This article covers the CHAR data type. See CHAR Function for the function.\n\nSyntax\n------\n\n[NATIONAL] CHAR[(M)] [CHARACTER SET charset_name] [COLLATE collation_name]\n\nDescription\n-----------\n\nA fixed-length string that is always right-padded with spaces to the specified\nlength when stored. M represents the column length in characters. The range of\nM is 0 to 255. If M is omitted, the length is 1.\n\nCHAR(0) columns can contain 2 values: an empty string or NULL. Such columns\ncannot be part of an index. The CONNECT storage engine does not support\nCHAR(0).\n\nNote: Trailing spaces are removed when CHAR values are retrieved unless the\nPAD_CHAR_TO_FULL_LENGTH SQL mode is enabled.\n\nBefore MariaDB 10.2, all collations were of type PADSPACE, meaning that CHAR\n(as well as VARCHAR and TEXT) values are compared without regard for trailing\nspaces. This does not apply to the LIKE pattern-matching operator, which takes\ninto account trailing spaces.\n\nIf a unique index consists of a column where trailing pad characters are\nstripped or ignored, inserts into that column where values differ only by the\nnumber of trailing pad characters will result in a duplicate-key error.\n\nExamples\n--------\n\nTrailing spaces:\n\nCREATE TABLE strtest (c CHAR(10));\nINSERT INTO strtest VALUES(\'Maria \');\n\nSELECT c=\'Maria\',c=\'Maria \' FROM strtest;\n+-----------+--------------+\n| c=\'Maria\' | c=\'Maria \' |\n+-----------+--------------+\n| 1 | 1 |\n+-----------+--------------+\n\nSELECT c LIKE \'Maria\',c LIKE \'Maria \' FROM strtest;\n+----------------+-------------------+\n| c LIKE \'Maria\' | c LIKE \'Maria \' |\n+----------------+-------------------+\n| 1 | 0 |\n+----------------+-------------------+\n\nNO PAD Collations\n-----------------\n\nNO PAD collations regard trailing spaces as normal characters. You can get a\nlist of all NO PAD collations by querying the Information Schema Collations\ntable, for example:\n\nSELECT collation_name FROM information_schema.collations \n WHERE collation_name LIKE \"%nopad%\";\n+------------------------------+\n| collation_name |\n+------------------------------+\n| big5_chinese_nopad_ci |\n| big5_nopad_bin |\n...\n\nURL: https://mariadb.com/kb/en/char/','','https://mariadb.com/kb/en/char/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (272,23,'CHAR BYTE','Description\n-----------\n\nThe CHAR BYTE data type is an alias for the BINARY data type. This is a\ncompatibility feature.\n\nURL: https://mariadb.com/kb/en/char-byte/','','https://mariadb.com/kb/en/char-byte/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (273,23,'ENUM','Syntax\n------\n\nENUM(\'value1\',\'value2\',...) [CHARACTER SET charset_name] [COLLATE\ncollation_name]\n\nDescription\n-----------\n\nAn enumeration. A string object that can have only one value, chosen from the\nlist of values \'value1\', \'value2\', ..., NULL or the special \'\' error value. In\ntheory, an ENUM column can have a maximum of 65,535 distinct values; in\npractice, the real maximum depends on many factors. ENUM values are\nrepresented internally as integers.\n\nTrailing spaces are automatically stripped from ENUM values on table creation.\n\nENUMs require relatively little storage space compared to strings, either one\nor two bytes depending on the number of enumeration values.\n\nNULL and empty values\n---------------------\n\nAn ENUM can also contain NULL and empty values. If the ENUM column is declared\nto permit NULL values, NULL becomes a valid value, as well as the default\nvalue (see below). If strict SQL Mode is not enabled, and an invalid value is\ninserted into an ENUM, a special empty string, with an index value of zero\n(see Numeric index, below), is inserted, with a warning. This may be\nconfusing, because the empty string is also a possible value, and the only\ndifference if that is this case its index is not 0. Inserting will fail with\nan error if strict mode is active.\n\nIf a DEFAULT clause is missing, the default value will be:\n\n* NULL if the column is nullable;\n* otherwise, the first value in the enumeration.\n\nNumeric index\n-------------\n\nENUM values are indexed numerically in the order they are defined, and sorting\nwill be performed in this numeric order. We suggest not using ENUM to store\nnumerals, as there is little to no storage space benefit, and it is easy to\nconfuse the enum integer with the enum numeral value by leaving out the quotes.\n\nAn ENUM defined as ENUM(\'apple\',\'orange\',\'pear\') would have the following\nindex values:\n\n+--------------------------------------+--------------------------------------+\n| Index | Value |\n+--------------------------------------+--------------------------------------+\n| NULL | NULL |\n+--------------------------------------+--------------------------------------+\n| 0 | \'\' |\n+--------------------------------------+--------------------------------------+\n| 1 | \'apple\' |\n+--------------------------------------+--------------------------------------+\n| 2 | \'orange\' |\n+--------------------------------------+--------------------------------------+\n| 3 | \'pear\' |\n+--------------------------------------+--------------------------------------+\n\nExamples\n--------\n\nCREATE TABLE fruits (\n id INT NOT NULL auto_increment PRIMARY KEY,\n fruit ENUM(\'apple\',\'orange\',\'pear\'),\n bushels INT);\n\nDESCRIBE fruits;\n+---------+-------------------------------+------+-----+---------+-------------\n--+\n| Field | Type | Null | Key | Default | Extra \n |\n+---------+-------------------------------+------+-----+---------+-------------\n--+\n| id | int(11) | NO | PRI | NULL |\nauto_increment |\n| fruit | enum(\'apple\',\'orange\',\'pear\') | YES | | NULL | \n |\n| bushels | int(11) | YES | | NULL | \n |\n+---------+-------------------------------+------+-----+---------+-------------\n--+\n\nINSERT INTO fruits\n (fruit,bushels) VALUES\n (\'pear\',20),\n (\'apple\',100),\n (\'orange\',25);\n\nINSERT INTO fruits\n (fruit,bushels) VALUES\n (\'avocado\',10);\nERROR 1265 (01000): Data truncated for column \'fruit\' at row 1\n\nSELECT * FROM fruits;\n+----+--------+---------+\n| id | fruit | bushels |\n+----+--------+---------+\n| 1 | pear | 20 |\n| 2 | apple | 100 |\n| 3 | orange | 25 |\n+----+--------+---------+\n\nSelecting by numeric index:\n\nSELECT * FROM fruits WHERE fruit=2;\n+----+--------+---------+\n| id | fruit | bushels |\n+----+--------+---------+\n| 3 | orange | 25 |\n+----+--------+---------+\n\nSorting is according to the index value:\n\nCREATE TABLE enums (a ENUM(\'2\',\'1\'));\n\nINSERT INTO enums VALUES (\'1\'),(\'2\');\n\nSELECT * FROM enums ORDER BY a ASC;\n+------+\n| a |\n+------+\n| 2 |\n| 1 |\n+------+\n\nIt\'s easy to get confused between returning the enum integer with the stored\nvalue, so we don\'t suggest using ENUM to store numerals. The first example\nreturns the 1st indexed field (\'2\' has an index value of 1, as it\'s defined\nfirst), while the second example returns the string value \'1\'.\n\nSELECT * FROM enums WHERE a=1;\n+------+\n| a |\n+------+\n| 2 |\n+------+\n\nSELECT * FROM enums WHERE a=\'1\';\n+------+\n| a |\n+------+\n| 1 |\n+------+\n\nURL: https://mariadb.com/kb/en/enum/','','https://mariadb.com/kb/en/enum/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (274,23,'INET6','MariaDB starting with 10.5.0\n----------------------------\nThe INET6 data type was added in MariaDB 10.5.0\n\nSyntax\n------\n\nINET6\n\nDescription\n-----------\n\nThe INET6 data type is intended for storage of IPv6 addresses, as well as IPv4\naddresses assuming conventional mapping of IPv4 addresses into IPv6 addresses.\n\nBoth short and long IPv6 notation are permitted, according to RFC-5952.\n\n* Values are stored as a 16-byte fixed length binary string, with most\nsignificant byte first.\n* Storage engines see INET6 as BINARY(16).\n* Clients see INET6 as CHAR(39) and get text representation on retrieval.\n\nThe IPv4-compatible notation is considered as deprecated. It is supported for\ncompatibility with the INET6_ATON function, which also understands this\nformat. It\'s recommended to use the mapped format to store IPv4 addresses in\nINET6.\n\nWhen an IPv4 mapped (or compatible) value is stored in INET6, it still\noccupies 16 bytes:\n\nRetrieval\n---------\n\nOn retrieval, in the client-server text protocol, INET6 values are converted\nto the short text representation, according to RFC-5952, that is with all\nleading zeroes in each group removed and with consequent zero groups\ncompressed.\n\nBesides creating one\'s own stored function, there is no a way to retrieve an\nINET6 value using long text representation.\n\nCasting\n-------\n\n* CAST from a character string to INET6 understands addresses in short or long\ntext notation (including IPv4 mapped and compatible addresses). NULL is\nreturned if the format is not understood.\n* CAST from a binary string to INET6 requires a 16-byte string as an argument.\nNULL is returned if the argument length is not equal to 16.\n* CAST from other data types to INET6 first converts data to a character\nstring, then CAST from character string to INET6 is applied.\n* CAST from INET6 to CHAR returns short text address notation.\n* CAST from INET6 to BINARY returns its 16-byte binary string representation.\n* CAST from INET6 to data types other than CHAR (e.g. SIGNED, UNSIGNED, TIME,\netc) returns an error.\n\nComparisons\n-----------\n\nAn INET6 expression can be compared to:\n\n* another INET6 expression\n* a character string expression with a text (short or long) address\nrepresentation:\n* a 16-byte binary string expression:\n\nAttempting to compare INET6 to an expression of any other data type returns an\nerror.\n\nMixing INET6 Values for Result\n------------------------------\n\nAn INET6 expression can be mixed for result (i.e. UNION, CASE..THEN, COALESCE\netc) with:\n\n* another INET6 expression. The resulting data type is INET6.\n* a character string in text (short or long) address representation. The\nresult data type is INET6. The character string counterpart is automatically\nconverted to INET6. If the string format is not understood, it\'s converted\nwith a warning to either NULL or to \'::\', depending on the NULL-ability of the\nresult.\n* a 16-byte binary string. The resulting data type is INET6. The binary string\ncounterpart is automatically converted to INET6. If the length of the binary\nstring is not equal to 16, it\'s converted with a warning to NULL or to \'::\'\ndepending on the NULL-ability of the result.\n\nAttempts to mix INET6 for result with other data types will return an error.\n\nMixing INET6 with other data types for LEAST and GREATEST, when mixing for\ncomparison and mixing for result are involved at the same time, uses the same\nrules with mixing for result, described in the previous paragraphs.\n\nFunctions and Operators\n-----------------------\n\n* HEX() with an INET6 argument returns a hexadecimal representation of the\nunderlying 16-byte binary string\n* Arithmetic operators (+,-,*,/,MOD,DIV) are not supported for INET6. This may\nchange in the future.\n* The INET6_ATON function now understands INET6 values as an argument\n* The prototypes of the IS_IPV4_COMPAT and IS_IPV4_MAPPED functions have\nchanged from a BINARY(16) to a INET6,\n* When the argument for these two functions is not INET6, automatic implicit\nCAST to INET6 is applied. As a consequence, both functions now understand\narguments in both text representation and binary(16) representation. Before\nMariaDB 10.5.0, these functions understood only binary(16) representation.\n\nPrepared Statement Parameters\n-----------------------------\n\nINET6 understands both text and binary(16) address representation in prepared\nstatement parameters (PREPARE..EXECUTE and EXECUTE IMMEDIATE statements).\n\nMigration between BINARY(16) and INET6\n---------------------------------------\n\nBefore MariaDB 10.5.0, you may have used BINARY(16) as a storage for IPv6\ninternet addresses, in combination with INET6_ATON and INET6_NTOA to\nrespectively insert and retrieve data.\n\nFrom 10.5, you can ALTER BINARY(16) columns storing IPv6 addresses to INET6.\nAfter such an alter, there is no a need to use INET6_ATON() and INET6_NTOA().\nAddresses can be inserted and retrieved directly.\n\nIt is also possible to convert INET6 columns to BINARY(16) and continue using\nthe data in combination with INET6_NTOA() and INET6_ATON().\n\nExamples\n--------\n\nCREATE TABLE t1 (a INET6);\n\nInserting using short text address notation:\n\nINSERT INTO t1 VALUES (\'2001:db8::ff00:42:8329\');\n\nLong text address notation:\n\nINSERT INTO t1 VALUES (\'2001:0db8:0000:0000:0000:ff00:0042:8329\');\n\n16-byte binary string notation:\n\nINSERT INTO t1 VALUES (0x20010DB8000000000000FF0000428329);\nINSERT INTO t1 VALUES (UNHEX(\'20010DB8000000000000FF0000428329\'));\n\nIPv4 addresses, using IPv4-mapped and IPv4-compatible notations:\n\nINSERT INTO t1 VALUES (\'::ffff:192.0.2.128\'); -- mapped\nINSERT INTO t1 VALUES (\'::192.0.2.128\'); -- compatible\n\nSELECT * FROM t1;\n+------------------------+\n| a |\n+------------------------+\n| 2001:db8::ff00:42:8329 |\n| 2001:db8::ff00:42:8329 |\n| 2001:db8::ff00:42:8329 |\n| 2001:db8::ff00:42:8329 |\n| ::ffff:192.0.2.128 |\n| ::192.0.2.128 |\n+------------------------+\n\nIPv4 mapped (or compatible) values still occupy 16 bytes:\n\nCREATE OR REPLACE TABLE t1 (a INET6);\n\nINSERT INTO t1 VALUES (\'::ffff:192.0.2.128\');\n\nSELECT * FROM t1;\n+--------------------+\n| a |\n+--------------------+\n| ::ffff:192.0.2.128 |\n+--------------------+\n\nSELECT HEX(a) FROM t1;\n+----------------------------------+\n| HEX(a) |\n+----------------------------------+\n| 00000000000000000000FFFFC0000280 |\n+----------------------------------+\n\nCasting from INET6 to anything other than CHAR returns an error:\n\nSELECT CAST(a AS DECIMAL) FROM t1;\n\nERROR 4079 (HY000): Illegal parameter data type inet6 for operation\n\'decimal_typecast\'\n\nComparison Examples\n-------------------\n\nComparison with another INET6 expression:\n\nCREATE OR REPLACE TABLE t1 (a INET6);\n CREATE OR REPLACE TABLE t2 (a INET6);\n\nINSERT INTO t1 VALUES\n(\'2001:db8::ff00:42:8328\'),(\'2001:db8::ff00:42:8329\');\n INSERT INTO t2 VALUES\n(\'2001:db8::ff00:42:832a\'),(\'2001:db8::ff00:42:8329\');\n\nSELECT t1.* FROM t1,t2 WHERE t1.a=t2.a;\n +------------------------+\n | a |\n +------------------------+\n | 2001:db8::ff00:42:8329 |\n +------------------------+\n\nWith a character string expression with a text (short or long) address\nrepresentation:\n\nCREATE OR REPLACE TABLE t1 (a INET6);\n\nINSERT INTO t1 VALUES (\'2001:db8::ff00:42:8329\');\n\nSELECT * FROM t1 WHERE a=\'2001:db8::ff00:42:8329\';\n +------------------------+\n | a |\n +------------------------+\n | 2001:db8::ff00:42:8329 |\n +------------------------+\n\nWith a 16-byte binary string expression:\n\nCREATE OR REPLACE TABLE t1 (a INET6);\n\nINSERT INTO t1 VALUES (\'2001:db8::ff00:42:8329\');\n\nSELECT * FROM t1 WHERE a=X\'20010DB8000000000000FF0000428329\';\n +------------------------+\n | a |\n +------------------------+\n | 2001:db8::ff00:42:8329 |\n +------------------------+\n\nWith an expression of another data type:\n\nSELECT * FROM t1 WHERE a=1;\nERROR 4078 (HY000): Illegal parameter data types inet6 and int for operation\n\'=\'\n\nMixing for Result Examples\n--------------------------\n\nMixed with another INET6 expression, returning an INET6 data type:\n\nCREATE OR REPLACE TABLE t1 (a INET6, b INET6);\n\nINSERT INTO t1 VALUES (NULL,\'2001:db8::ff00:42:8329\');\n\nSELECT a FROM t1 UNION SELECT b FROM t1;\n +------------------------+\n | a |\n +------------------------+\n | NULL |\n | 2001:db8::ff00:42:8329 |\n +------------------------+\n\nSELECT COALESCE(a, b) FROM t1;\n +------------------------+\n | COALESCE(a, b) |\n +------------------------+\n | 2001:db8::ff00:42:8329 |\n +------------------------+\n\nMixed with a character string in text (short or long) address representation:\n\nCREATE OR REPLACE TABLE t1 (a INET6, b VARCHAR(64));\n\nINSERT INTO t1 VALUES (NULL,\'2001:db8::ff00:42:8328\');\n\nINSERT INTO t1 VALUES (NULL,\'2001:db8::ff00:42:832a garbage\');\n\nSELECT COALESCE(a,b) FROM t1;\n +------------------------+\n | COALESCE(a,b) |\n +------------------------+\n | 2001:db8::ff00:42:8328 |\n | NULL |\n +------------------------+\n 2 rows in set, 1 warning (0.001 sec)\n\nSHOW WARNINGS;\n\n+---------+------+---------------------------------------------------------+\n | Level | Code | Message\n|\n\n+---------+------+---------------------------------------------------------+\n | Warning | 1292 | Incorrect inet6 value: \'2001:db8::ff00:42:832a garbage\'\n|\n\n+---------+------+---------------------------------------------------------+\n\nMixed with a 16-byte binary string:\n\nCREATE OR REPLACE TABLE t1 (a INET6, b VARBINARY(16));\n\nINSERT INTO t1 VALUES (NULL,CONCAT(0xFFFF,REPEAT(0x0000,6),0xFFFF));\n\nINSERT INTO t1 VALUES (NULL,0x00/*garbage*/);\n\nSELECT COALESCE(a,b) FROM t1;\n +---------------+\n | COALESCE(a,b) |\n +---------------+\n | ffff::ffff |\n | NULL |\n +---------------+\n 2 rows in set, 1 warning (0.001 sec)\n\nSHOW WARNINGS;\n +---------+------+-------------------------------+\n | Level | Code | Message |\n +---------+------+-------------------------------+\n | Warning | 1292 | Incorrect inet6 value: \'\\x00\' |\n +---------+------+-------------------------------+\n\nMixing with other data types:\n\nSELECT CAST(\'ffff::ffff\' AS INET6) UNION SELECT 1;\nERROR 4078 (HY000): Illegal parameter data types inet6 and int for operation\n\'UNION\'\n\nFunctions and Operators Examples\n--------------------------------\n\nHEX with an INET6 argument returning a hexadecimal representation:\n\nSELECT HEX(CAST(\'2001:db8::ff00:42:8329\' AS INET6));\n +----------------------------------------------+\n | HEX(CAST(\'2001:db8::ff00:42:8329\' AS INET6)) |\n +----------------------------------------------+\n | 20010DB8000000000000FF0000428329 |\n +----------------------------------------------+\n\nINET6_ATON now understands INET6 values as an argument:\n\nCREATE OR REPLACE TABLE t1 (a INET6);\n\nINSERT INTO t1 VALUES (\'2001:db8::ff00:42:8329\');\n\nSELECT a, HEX(INET6_ATON(a)) FROM t1;\n +------------------------+----------------------------------+\n | a | HEX(INET6_ATON(a)) |\n +------------------------+----------------------------------+\n | 2001:db8::ff00:42:8329 | 20010DB8000000000000FF0000428329 |\n +------------------------+----------------------------------+\n\nIS_IPV4_COMPAT and IS_IPV4_MAPPED prototype now a BINARY(16)):\n\nCREATE OR REPLACE TABLE t1 (a INET6);\n\nINSERT INTO t1 VALUES (\'2001:db8::ff00:42:8329\');\n INSERT INTO t1 VALUES (\'::ffff:192.168.0.1\');\n INSERT INTO t1 VALUES (\'::192.168.0.1\');\n\nSELECT a, IS_IPV4_MAPPED(a), IS_IPV4_COMPAT(a) FROM t1;\n +------------------------+-------------------+-------------------+\n | a | IS_IPV4_MAPPED(a) | IS_IPV4_COMPAT(a) |\n +------------------------+-------------------+-------------------+\n | 2001:db8::ff00:42:8329 | 0 | 0 |\n | ::ffff:192.168.0.1 | 1 | 0 |\n | ::192.168.0.1 | 0 | 1 |\n +------------------------+-------------------+-------------------+\n\nAutomatic implicit CAST to INET6:\n\nCREATE OR REPLACE TABLE t1 (\n a INET6,\n b VARCHAR(39) DEFAULT a\n );\n\nINSERT INTO t1 (a) VALUES (\'ffff::ffff\'),(\'::ffff:192.168.0.1\');\n\nSELECT a, IS_IPV4_MAPPED(a), b, IS_IPV4_MAPPED(b) FROM t1;\n\n+--------------------+-------------------+--------------------+----------------\n--+\n | a | IS_IPV4_MAPPED(a) | b |\nIS_IPV4_MAPPED(b) |\n\n+--------------------+-------------------+--------------------+----------------\n--+\n | ffff::ffff | 0 | ffff::ffff |\n 0 |\n | ::ffff:192.168.0.1 | 1 | ::ffff:192.168.0.1 |\n 1 |\n\n+--------------------+-------------------+--------------------+----------------\n--+\n\nCREATE OR REPLACE TABLE t1 (\n a INET6,\n b BINARY(16) DEFAULT UNHEX(HEX(a))\n );\n\nINSERT INTO t1 (a) VALUES (\'ffff::ffff\'),(\'::ffff:192.168.0.1\');\n\nSELECT a, IS_IPV4_MAPPED(a), HEX(b), IS_IPV4_MAPPED(b) FROM t1;\n\n+--------------------+-------------------+----------------------------------+--\n----------------+\n | a | IS_IPV4_MAPPED(a) | HEX(b)\n | IS_IPV4_MAPPED(b) |\n\n+--------------------+-------------------+----------------------------------+--\n----------------+\n | ffff::ffff | 0 |\nFFFF000000000000000000000000FFFF | 0 |\n | ::ffff:192.168.0.1 | 1 |\n00000000000000000000FFFFC0A80001 | 1 |\n\n+--------------------+-------------------+----------------------------------+--\n----------------+\n\nPrepared Statement Parameters Examples\n--------------------------------------\n\nCREATE OR REPLACE TABLE t1 (a INET6);\n\nEXECUTE IMMEDIATE \'INSERT INTO t1 VALUES (?)\' USING \'ffff::fffe\';\nEXECUTE IMMEDIATE \'INSERT INTO t1 VALUES (?)\' USING\nX\'FFFF000000000000000000000000FFFF\';\n\nSELECT * FROM t1;\n+------------+\n| a |\n+------------+\n| ffff::fffe |\n| ffff::ffff |\n+------------+\n\nEXECUTE IMMEDIATE \'SELECT * FROM t1 WHERE a=?\' USING \'ffff::fffe\';\n+------------+\n| a |\n+------------+\n| ffff::fffe |\n+------------+\n\nEXECUTE IMMEDIATE \'SELECT * FROM t1 WHERE a=?\' USING','','https://mariadb.com/kb/en/inet6/'); -update help_topic set description = CONCAT(description, '\nX\'FFFF000000000000000000000000FFFF\';\n+------------+\n| a |\n+------------+\n| ffff::ffff |\n+------------+\n\nMigration between BINARY(16) and INET6 Examples\n-----------------------------------------------\n\nBefore MariaDB 10.5:\n\nCREATE OR REPLACE TABLE t1 (a BINARY(16));\n\nINSERT INTO t1 VALUES (INET6_ATON(\'ffff::ffff\'));\n\nSELECT INET6_NTOA(a) FROM t1;\n+---------------+\n| INET6_NTOA(a) |\n+---------------+\n| ffff::ffff |\n+---------------+\n\nMigrating to INET6, from MariaDB 10.5:\n\nALTER TABLE t1 MODIFY a INET6;\n\nINSERT INTO t1 VALUES (\'ffff::fffe\');\n\nSELECT * FROM t1;\n+------------+\n| a |\n+------------+\n| ffff::ffff |\n| ffff::fffe |\n+------------+\n\nMigration from INET6 to BINARY(16):\n\nCREATE OR REPLACE TABLE t1 (a INET6);\n\nINSERT INTO t1 VALUES (\'2001:db8::ff00:42:8329\');\nINSERT INTO t1 VALUES (\'::ffff:192.168.0.1\');\nINSERT INTO t1 VALUES (\'::192.168.0.1\');\n\nALTER TABLE t1 MODIFY a BINARY(16);\n\nSELECT INET6_NTOA(a) FROM t1;\n+------------------------+\n| INET6_NTOA(a) |\n+------------------------+\n| 2001:db8::ff00:42:8329 |\n| ::ffff:192.168.0.1 |\n| ::192.168.0.1 |\n+------------------------+\n\nURL: https://mariadb.com/kb/en/inet6/') WHERE help_topic_id = 274; -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (275,23,'JSON Data Type','MariaDB starting with 10.2.7\n----------------------------\nThe JSON alias was added in MariaDB 10.2.7. This was done to make it possible\nto use JSON columns in statement based replication from MySQL to MariaDB and\nto make it possible for MariaDB to read mysqldumps from MySQL.\n\nJSON is an alias for LONGTEXT introduced for compatibility reasons with\nMySQL\'s JSON data type. MariaDB implements this as a LONGTEXT rather, as the\nJSON data type contradicts the SQL standard, and MariaDB\'s benchmarks indicate\nthat performance is at least equivalent.\n\nIn order to ensure that a a valid json document is inserted, the JSON_VALID\nfunction can be used as a CHECK constraint. This constraint is automatically\nincluded for types using the JSON alias from MariaDB 10.4.3.\n\nExamples\n--------\n\nCREATE TABLE t (j JSON);\n\nDESC t;\n+-------+----------+------+-----+---------+-------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+----------+------+-----+---------+-------+\n| j | longtext | YES | | NULL | |\n+-------+----------+------+-----+---------+-------+\n\nWith validation:\n\nCREATE TABLE t2 (\n j JSON\n CHECK (JSON_VALID(j))\n);\n\nINSERT INTO t2 VALUES (\'invalid\');\nERROR 4025 (23000): CONSTRAINT `j` failed for `test`.`t2`\n\nINSERT INTO t2 VALUES (\'{\"id\": 1, \"name\": \"Monty\"}\');\nQuery OK, 1 row affected (0.13 sec)\n\nReplicating JSON Data Between MySQL and MariaDB\n-----------------------------------------------\n\nThe JSON type in MySQL stores the JSON object in a compact form, not as\nLONGTEXT as in MariaDB. This means that row based replication will not work\nfor JSON types from MySQL to MariaDB.\n\nThere are a a few different ways to solve this:\n\n* Use statement based replication.\n* Change the JSON column to type TEXT in MySQL\n* If you must use row-based replication and cannot change the MySQL master\nfrom JSON to TEXT, you can try to introduce an intermediate MySQL slave and\nchange the column type from JSON to TEXT on it. Then you replicate from this\nintermediate slave to MariaDB.\n\nConverting a MySQL TABLE with JSON Fields to MariaDB\n----------------------------------------------------\n\nMariaDB can\'t directly access MySQL\'s JSON format.\n\nThere are a a few different ways to move the table to MariaDB:\n\n* From MariaDB 10.5.7, see the you can use the mysql_json plugin. See Making\nMariaDB understand MySQL JSON.\n* Change the JSON column to type TEXT in MySQL. After this, MariaDB can\ndirectly use the table without any need for a dump and restore.\n* Use mariadb-dump/mysqldump to copy the table.\n\nDifferences Between MySQL JSON Strings and MariaDB JSON Strings\n---------------------------------------------------------------\n\n* In MySQL, JSON is an object and is compared according to json values. In\nMariaDB JSON strings are normal strings and compared as strings. One exception\nis when using JSON_EXTRACT() in which case strings are unescaped before\ncomparison.\n\nURL: https://mariadb.com/kb/en/json-data-type/','','https://mariadb.com/kb/en/json-data-type/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (276,23,'MEDIUMBLOB','Syntax\n------\n\nMEDIUMBLOB\n\nDescription\n-----------\n\nA BLOB column with a maximum length of 16,777,215 (224 - 1) bytes. Each\nMEDIUMBLOB value is stored using a three-byte length prefix that indicates the\nnumber of bytes in the value.\n\nURL: https://mariadb.com/kb/en/mediumblob/','','https://mariadb.com/kb/en/mediumblob/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (277,23,'MEDIUMTEXT','Syntax\n------\n\nMEDIUMTEXT [CHARACTER SET charset_name] [COLLATE collation_name]\n\nDescription\n-----------\n\nA TEXT column with a maximum length of 16,777,215 (224 - 1) characters. The\neffective maximum length is less if the value contains multi-byte characters.\nEach MEDIUMTEXT value is stored using a three-byte length prefix that\nindicates the number of bytes in the value.\n\nURL: https://mariadb.com/kb/en/mediumtext/','','https://mariadb.com/kb/en/mediumtext/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (278,23,'LONGBLOB','Syntax\n------\n\nLONGBLOB\n\nDescription\n-----------\n\nA BLOB column with a maximum length of 4,294,967,295 bytes or 4GB (232 - 1).\nThe effective maximum length of LONGBLOB columns depends on the configured\nmaximum packet size in the client/server protocol and available memory. Each\nLONGBLOB value is stored using a four-byte length prefix that indicates the\nnumber of bytes in the value.\n\nOracle Mode\n-----------\n\nMariaDB starting with 10.3\n--------------------------\nIn Oracle mode from MariaDB 10.3, BLOB is a synonym for LONGBLOB.\n\nURL: https://mariadb.com/kb/en/longblob/','','https://mariadb.com/kb/en/longblob/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (279,23,'LONGTEXT','Syntax\n------\n\nLONGTEXT [CHARACTER SET charset_name] [COLLATE collation_name]\n\nDescription\n-----------\n\nA TEXT column with a maximum length of 4,294,967,295 or 4GB (232 - 1)\ncharacters. The effective maximum length is less if the value contains\nmulti-byte characters. The effective maximum length of LONGTEXT columns also\ndepends on the configured maximum packet size in the client/server protocol\nand available memory. Each LONGTEXT value is stored using a four-byte length\nprefix that indicates the number of bytes in the value.\n\nFrom MariaDB 10.2.7, JSON is an alias for LONGTEXT. See JSON Data Type for\ndetails.\n\nOracle Mode\n-----------\n\nMariaDB starting with 10.3\n--------------------------\nIn Oracle mode from MariaDB 10.3, CLOB is a synonym for LONGTEXT.\n\nURL: https://mariadb.com/kb/en/longtext/','','https://mariadb.com/kb/en/longtext/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (280,23,'ROW','MariaDB starting with 10.3.0\n----------------------------\nThe ROW data type was introduced in MariaDB 10.3.0.\n\nSyntax\n------\n\nROW ( [{, }... ])\n\nDescription\n-----------\n\nROW is a data type for stored procedure variables.\n\nFeatures\n--------\n\nROW fields as normal variables\n------------------------------\n\nROW fields (members) act as normal variables, and are able to appear in all\nquery parts where a stored procedure variable is allowed:\n\n* Assignment is using the := operator and the SET command:\n\na.x:= 10;\na.x:= b.x;\nSET a.x= 10, a.y=20, a.z= b.z;\n\n* Passing to functions and operators:\n\nSELECT f1(rec.a), rec.a<10;\n\n* Clauses (select list, WHERE, HAVING, LIMIT, etc...,):\n\nSELECT var.a, t1.b FROM t1 WHERE t1.b=var.b LIMIT var.c;\n\n* INSERT values:\n\nINSERT INTO t1 VALUES (rec.a, rec.b, rec.c);\n\n* SELECT .. INTO targets\n\nSELECT a,b INTO rec.a, rec.b FROM t1 WHERE t1.id=10;\n\n* Dynamic SQL out parameters (EXECUTE and EXECUTE IMMEDIATE)\n\nEXECUTE IMMEDIATE \'CALL proc_with_out_param(?)\' USING rec.a;\n\nROW type variables as FETCH targets\n-----------------------------------\n\nROW type variables are allowed as FETCH targets:\n\nFETCH cur INTO rec;\n\nwhere cur is a CURSOR and rec is a ROW type stored procedure variable.\n\nNote, currently an attempt to use FETCH for a ROW type variable returns this\nerror:\n\nERROR 1328 (HY000): Incorrect number of FETCH variables\n\nFETCH from a cursor cur into a ROW variable rec works as follows:\n\n* The number of fields in cur must match the number of fields in rec.\n Otherwise, an error is reported.\n\n* Assignment is done from left to right. The first cursor field is assigned to\n the first variable field, the second cursor field is assigned to the second\n variable field, etc.\n\n* Field names in rec are not important and can differ from field names\n in cur.\n\nSee FETCH Examples (below) for examples of using this with sql_mode=ORACLE and\nsql_mode=DEFAULT.\n\nROW type variables as SELECT...INTO targets\n-------------------------------------------\n\nROW type variables are allowed as SELECT..INTO targets with some differences\ndepending on which sql_mode is in use.\n\n* When using sql_mode=ORACLE, table%ROWTYPE and cursor%ROWTYPE\n variables can be used as SELECT...INTO targets.\n\n* Using multiple ROW variables in the SELECT..INTO list will report an\n error.\n\n* Using ROW variables with a different column count than in\n the SELECT..INTO list will report an error.\n\nSee SELECT...INTO Examples (below) for examples of using this with\nsql_mode=ORACLE and sql_mode=DEFAULT.\n\nFeatures not implemented\n------------------------\n\nThe following features are planned, but not implemented yet:\n\n* Returning a ROW type expression from a stored function (see MDEV-12252).\nThis will need some grammar change to support field names after parentheses:\n\nSELECT f1().x FROM DUAL;\n\n* Returning a ROW type expression from a built-in hybrid type function, such\nas CASE, IF, etc. \n* ROW of ROWs\n\nExamples\n--------\n\nDeclaring a ROW in a stored procedure\n-------------------------------------\n\nDELIMITER $$\nCREATE PROCEDURE p1()\nBEGIN\n DECLARE r ROW (c1 INT, c2 VARCHAR(10));\n SET r.c1= 10;\n SET r.c2= \'test\';\n INSERT INTO t1 VALUES (r.c1, r.c2);\nEND;\n$$\nDELIMITER ;\nCALL p1();\n\nFETCH Examples\n--------------\n\nA complete FETCH example for sql_mode=ORACLE:\n\nDROP TABLE IF EXISTS t1;\nCREATE TABLE t1 (a INT, b VARCHAR(32));\nINSERT INTO t1 VALUES (10,\'b10\');\nINSERT INTO t1 VALUES (20,\'b20\');\nINSERT INTO t1 VALUES (30,\'b30\');\n\nSET sql_mode=oracle;\nDROP PROCEDURE IF EXISTS p1;\nDELIMITER $$\nCREATE PROCEDURE p1 AS\n rec ROW(a INT, b VARCHAR(32));\n CURSOR c IS SELECT a,b FROM t1;\nBEGIN\n OPEN c;\n LOOP\n FETCH c INTO rec;\n EXIT WHEN c%NOTFOUND;\n SELECT (\'rec=(\' || rec.a ||\',\'|| rec.b||\')\');\n END LOOP;\n CLOSE c;\nEND;\n$$\nDELIMITER ;\nCALL p1();\n\nA complete FETCH example for sql_mode=DEFAULT:\n\nDROP TABLE IF EXISTS t1;\nCREATE TABLE t1 (a INT, b VARCHAR(32));\nINSERT INTO t1 VALUES (10,\'b10\');\nINSERT INTO t1 VALUES (20,\'b20\');\nINSERT INTO t1 VALUES (30,\'b30\');\n\nSET sql_mode=DEFAULT;\nDROP PROCEDURE IF EXISTS p1;\nDELIMITER $$\nCREATE PROCEDURE p1()\nBEGIN\n DECLARE done INT DEFAULT FALSE;\n DECLARE rec ROW(a INT, b VARCHAR(32));\n DECLARE c CURSOR FOR SELECT a,b FROM t1;\n DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;\n OPEN c;\nread_loop:\n LOOP\n FETCH c INTO rec;\n IF done THEN\n LEAVE read_loop;\n END IF;\n SELECT CONCAT(\'rec=(\',rec.a,\',\',rec.b,\')\');\n END LOOP;\n CLOSE c;\nEND;\n$$\nDELIMITER ;\nCALL p1();\n\nSELECT...INTO Examples\n----------------------\n\nA SELECT...INTO example for sql_mode=DEFAULT:\n\nSET sql_mode=DEFAULT;\nDROP TABLE IF EXISTS t1;\nDROP PROCEDURE IF EXISTS p1;\nCREATE TABLE t1 (a INT, b VARCHAR(32));\nINSERT INTO t1 VALUES (10,\'b10\');\nDELIMITER $$\nCREATE PROCEDURE p1()\nBEGIN\n DECLARE rec1 ROW(a INT, b VARCHAR(32));\n SELECT * FROM t1 INTO rec1;\n SELECT rec1.a, rec1.b;\nEND;\n$$\nDELIMITER ;\nCALL p1();\n\nThe above example returns:\n\n+--------+--------+\n| rec1.a | rec1.b |\n+--------+--------+\n| 10 | b10 |\n+--------+--------+\n\nA SELECT...INTO example for sql_mode=ORACLE:\n\nSET sql_mode=ORACLE;\nDROP TABLE IF EXISTS t1;\nDROP PROCEDURE IF EXISTS p1;\nCREATE TABLE t1 (a INT, b VARCHAR(32));\nINSERT INTO t1 VALUES (10,\'b10\');\nDELIMITER $$\nCREATE PROCEDURE p1 AS\n rec1 ROW(a INT, b VARCHAR(32));\nBEGIN\n SELECT * FROM t1 INTO rec1;\n SELECT rec1.a, rec1.b;\nEND;\n$$\nDELIMITER ;\nCALL p1();\n\nThe above example returns:\n\n+--------+--------+\n| rec1.a | rec1.b |\n+--------+--------+\n| 10 | b10 |\n+--------+--------+\n\nAn example for sql_mode=ORACLE using table%ROWTYPE variables as SELECT..INTO\ntargets:\n\nSET sql_mode=ORACLE;\nDROP TABLE IF EXISTS t1;\nDROP PROCEDURE IF EXISTS p1;\nCREATE TABLE t1 (a INT, b VARCHAR(32));\nINSERT INTO t1 VALUES (10,\'b10\');\nDELIMITER $$\nCREATE PROCEDURE p1 AS\n rec1 t1%ROWTYPE;\nBEGIN\n SELECT * FROM t1 INTO rec1;\n SELECT rec1.a, rec1.b;\nEND;\n$$\nDELIMITER ;\nCALL p1();\n\nThe above example returns:\n\n+--------+--------+\n| rec1.a | rec1.b |\n+--------+--------+\n| 10 | b10 |\n+--------+--------+\n\nAn example for sql_mode=ORACLE using cursor%ROWTYPE variables as SELECT..INTO\ntargets:\n\nSET sql_mode=ORACLE;\nDROP TABLE IF EXISTS t1;\nDROP PROCEDURE IF EXISTS p1;\nCREATE TABLE t1 (a INT, b VARCHAR(32));\nINSERT INTO t1 VALUES (10,\'b10\');\nDELIMITER $$\nCREATE PROCEDURE p1 AS\n CURSOR cur1 IS SELECT * FROM t1;\n rec1 cur1%ROWTYPE;\nBEGIN\n SELECT * FROM t1 INTO rec1;\n SELECT rec1.a, rec1.b;\nEND;\n$$\nDELIMITER ;\nCALL p1();\n\nThe above example returns:\n\n+--------+--------+\n| rec1.a | rec1.b |\n+--------+--------+\n| 10 | b10 |\n+--------+--------+\n\nURL: https://mariadb.com/kb/en/row/','','https://mariadb.com/kb/en/row/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (281,23,'TEXT','Syntax\n------\n\nTEXT[(M)] [CHARACTER SET charset_name] [COLLATE collation_name]\n\nDescription\n-----------\n\nA TEXT column with a maximum length of 65,535 (216 - 1) characters. The\neffective maximum length is less if the value contains multi-byte characters.\nEach TEXT value is stored using a two-byte length prefix that indicates the\nnumber of bytes in the value. If you need a bigger storage, consider using\nMEDIUMTEXT instead.\n\nAn optional length M can be given for this type. If this is done, MariaDB\ncreates the column as the smallest TEXT type large enough to hold values M\ncharacters long.\n\nBefore MariaDB 10.2, all MariaDB collations were of type PADSPACE, meaning\nthat TEXT (as well as VARCHAR and CHAR values) are compared without regard for\ntrailing spaces. This does not apply to the LIKE pattern-matching operator,\nwhich takes into account trailing spaces.\n\nBefore MariaDB 10.2.1, BLOB and TEXT columns could not be assigned a DEFAULT\nvalue. This restriction was lifted in MariaDB 10.2.1.\n\nExamples\n--------\n\nTrailing spaces:\n\nCREATE TABLE strtest (d TEXT(10));\nINSERT INTO strtest VALUES(\'Maria \');\n\nSELECT d=\'Maria\',d=\'Maria \' FROM strtest;\n+-----------+--------------+\n| d=\'Maria\' | d=\'Maria \' |\n+-----------+--------------+\n| 1 | 1 |\n+-----------+--------------+\n\nSELECT d LIKE \'Maria\',d LIKE \'Maria \' FROM strtest;\n+----------------+-------------------+\n| d LIKE \'Maria\' | d LIKE \'Maria \' |\n+----------------+-------------------+\n| 0 | 1 |\n+----------------+-------------------+\n\nIndexing\n--------\n\nTEXT columns can only be indexed over a specified length. This means that they\ncannot be used as the primary key of a table norm until MariaDB 10.4, can a\nunique index be created on them.\n\nMariaDB starting with 10.4\n--------------------------\nStarting with MariaDB 10.4, a unique index can be created on a TEXT column.\n\nInternally, this uses hash indexing to quickly check the values and if a hash\ncollision is found, the actual stored values are compared in order to retain\nthe uniqueness.\n\nDifference between VARCHAR and TEXT\n-----------------------------------\n\n* VARCHAR columns can be fully indexed. TEXT columns can only be indexed over\na specified length.\n* Using TEXT or BLOB in a SELECT query that uses temporary tables for storing\nintermediate results will force the temporary table to be disk based (using\nthe Aria storage engine instead of the memory storage engine, which is a bit\nslower. This is not that bad as the Aria storage engine caches the rows in\nmemory. To get the benefit of this, one should ensure that the\naria_pagecache_buffer_size variable is big enough to hold most of the row and\nindex data for temporary tables.\n\nFor Storage Engine Developers\n-----------------------------\n\n* Internally the full length of the VARCHAR column is allocated inside each\nTABLE objects record[] structure. As there are three such buffers, each open\ntable will allocate 3 times max-length-to-store-varchar bytes of memory.\n* TEXT and BLOB columns are stored with a pointer (4 or 8 bytes) + a 1-4 bytes\nlength. The TEXT data is only stored once. This means that internally TEXT\nuses less memory for each open table but instead has the additional overhead\nthat each TEXT object needs to be allocated and freed for each row access\n(with some caching in between).\n\nURL: https://mariadb.com/kb/en/text/','','https://mariadb.com/kb/en/text/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (282,23,'TINYBLOB','Syntax\n------\n\nTINYBLOB\n\nDescription\n-----------\n\nA BLOB column with a maximum length of 255 (28 - 1) bytes. Each TINYBLOB value\nis stored using a one-byte length prefix that indicates the number of bytes in\nthe value.\n\nURL: https://mariadb.com/kb/en/tinyblob/','','https://mariadb.com/kb/en/tinyblob/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (283,23,'TINYTEXT','Syntax\n------\n\nTINYTEXT [CHARACTER SET charset_name] [COLLATE collation_name]\n\nDescription\n-----------\n\nA TEXT column with a maximum length of 255 (28 - 1) characters. The effective\nmaximum length is less if the value contains multi-byte characters. Each\nTINYTEXT value is stored using a one-byte length prefix that indicates the\nnumber of bytes in the value.\n\nURL: https://mariadb.com/kb/en/tinytext/','','https://mariadb.com/kb/en/tinytext/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (284,23,'VARBINARY','Syntax\n------\n\nVARBINARY(M)\n\nDescription\n-----------\n\nThe VARBINARY type is similar to the VARCHAR type, but stores binary byte\nstrings rather than non-binary character strings. M represents the maximum\ncolumn length in bytes.\n\nIt contains no character set, and comparison and sorting are based on the\nnumeric value of the bytes.\n\nIf the maximum length is exceeded, and SQL strict mode is not enabled , the\nextra characters will be dropped with a warning. If strict mode is enabled, an\nerror will occur.\n\nUnlike BINARY values, VARBINARYs are not right-padded when inserting.\n\nOracle Mode\n-----------\n\nMariaDB starting with 10.3\n--------------------------\nIn Oracle mode from MariaDB 10.3, RAW is a synonym for VARBINARY.\n\nExamples\n--------\n\nInserting too many characters, first with strict mode off, then with it on:\n\nCREATE TABLE varbins (a VARBINARY(10));\n\nINSERT INTO varbins VALUES(\'12345678901\');\nQuery OK, 1 row affected, 1 warning (0.04 sec)\n\nSELECT * FROM varbins;\n+------------+\n| a |\n+------------+\n| 1234567890 |\n+------------+\n\nSET sql_mode=\'STRICT_ALL_TABLES\';\n\nINSERT INTO varbins VALUES(\'12345678901\');\nERROR 1406 (22001): Data too long for column \'a\' at row 1\n\nSorting is performed with the byte value:\n\nTRUNCATE varbins;\n\nINSERT INTO varbins VALUES(\'A\'),(\'B\'),(\'a\'),(\'b\');\n\nSELECT * FROM varbins ORDER BY a;\n+------+\n| a |\n+------+\n| A |\n| B |\n| a |\n| b |\n+------+\n\nUsing CAST to sort as a CHAR instead:\n\nSELECT * FROM varbins ORDER BY CAST(a AS CHAR);\n+------+\n| a |\n+------+\n| a |\n| A |\n| b |\n| B |\n+------+\n\nURL: https://mariadb.com/kb/en/varbinary/','','https://mariadb.com/kb/en/varbinary/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (285,23,'VARCHAR','Syntax\n------\n\n[NATIONAL] VARCHAR(M) [CHARACTER SET charset_name] [COLLATE collation_name]\n\nDescription\n-----------\n\nA variable-length string. M represents the maximum column length in\ncharacters. The range of M is 0 to 65,532. The effective maximum length of a\nVARCHAR is subject to the maximum row size and the character set used. For\nexample, utf8 characters can require up to three bytes per character, so a\nVARCHAR column that uses the utf8 character set can be declared to be a\nmaximum of 21,844 characters.\n\nNote: For the ColumnStore engine, M represents the maximum column length in\nbytes.\n\nMariaDB stores VARCHAR values as a one-byte or two-byte length prefix plus\ndata. The length prefix indicates the number of bytes in the value. A VARCHAR\ncolumn uses one length byte if values require no more than 255 bytes, two\nlength bytes if values may require more than 255 bytes.\n\nMariaDB follows the standard SQL specification, and does not remove trailing\nspaces from VARCHAR values.\n\nVARCHAR(0) columns can contain 2 values: an empty string or NULL. Such columns\ncannot be part of an index. The CONNECT storage engine does not support\nVARCHAR(0).\n\nVARCHAR is shorthand for CHARACTER VARYING. NATIONAL VARCHAR is the standard\nSQL way to define that a VARCHAR column should use some predefined character\nset. MariaDB uses utf8 as this predefined character set, as does MySQL 4.1 and\nup. NVARCHAR is shorthand for NATIONAL VARCHAR.\n\nBefore MariaDB 10.2, all MariaDB collations were of type PADSPACE, meaning\nthat VARCHAR (as well as CHAR and TEXT values) are compared without regard for\ntrailing spaces. This does not apply to the LIKE pattern-matching operator,\nwhich takes into account trailing spaces. From MariaDB 10.2, a number of NO\nPAD collations are available.\n\nIf a unique index consists of a column where trailing pad characters are\nstripped or ignored, inserts into that column where values differ only by the\nnumber of trailing pad characters will result in a duplicate-key error.\n\nExamples\n--------\n\nThe following are equivalent:\n\nVARCHAR(30) CHARACTER SET utf8\nNATIONAL VARCHAR(30)\nNVARCHAR(30)\nNCHAR VARCHAR(30)\nNATIONAL CHARACTER VARYING(30)\nNATIONAL CHAR VARYING(30)\n\nTrailing spaces:\n\nCREATE TABLE strtest (v VARCHAR(10));\nINSERT INTO strtest VALUES(\'Maria \');\n\nSELECT v=\'Maria\',v=\'Maria \' FROM strtest;\n+-----------+--------------+\n| v=\'Maria\' | v=\'Maria \' |\n+-----------+--------------+\n| 1 | 1 |\n+-----------+--------------+\n\nSELECT v LIKE \'Maria\',v LIKE \'Maria \' FROM strtest;\n+----------------+-------------------+\n| v LIKE \'Maria\' | v LIKE \'Maria \' |\n+----------------+-------------------+\n| 0 | 1 |\n+----------------+-------------------+\n\nTruncation\n----------\n\n* Depending on whether or not strict sql mode is set, you will either get a\nwarning or an error if you try to insert a string that is too long into a\nVARCHAR column. If the extra characters are spaces, the spaces that can\'t fit\nwill be removed and you will always get a warning, regardless of the sql mode\nsetting.\n\nDifference Between VARCHAR and TEXT\n-----------------------------------\n\n* VARCHAR columns can be fully indexed. TEXT columns can only be indexed over\na specified length.\n* Using TEXT or BLOB in a SELECT query that uses temporary tables for storing\nintermediate results will force the temporary table to be disk based (using\nthe Aria storage engine instead of the memory storage engine, which is a bit\nslower. This is not that bad as the Aria storage engine caches the rows in\nmemory. To get the benefit of this, one should ensure that the\naria_pagecache_buffer_size variable is big enough to hold most of the row and\nindex data for temporary tables.\n\nOracle Mode\n-----------\n\nMariaDB starting with 10.3\n--------------------------\nIn Oracle mode from MariaDB 10.3, VARCHAR2 is a synonym.\n\nFor Storage Engine Developers\n-----------------------------\n\n* Internally the full length of the VARCHAR column is allocated inside each\nTABLE objects record[] structure. As there are three such buffers, each open\ntable will allocate 3 times max-length-to-store-varchar bytes of memory.\n* TEXT and BLOB columns are stored with a pointer (4 or 8 bytes) + a 1-4 bytes\nlength. The TEXT data is only stored once. This means that internally TEXT\nuses less memory for each open table but instead has the additional overhead\nthat each TEXT object needs to be allocated and freed for each row access\n(with some caching in between).\n\nURL: https://mariadb.com/kb/en/varchar/','','https://mariadb.com/kb/en/varchar/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (286,23,'SET Data Type','Syntax\n------\n\nSET(\'value1\',\'value2\',...) [CHARACTER SET charset_name] [COLLATE\ncollation_name]\n\nDescription\n-----------\n\nA set. A string object that can have zero or more values, each of which must\nbe chosen from the list of values \'value1\', \'value2\', ... A SET column can\nhave a maximum of 64 members. SET values are represented internally as\nintegers.\n\nSET values cannot contain commas.\n\nIf a SET contains duplicate values, an error will be returned if strict mode\nis enabled, or a warning if strict mode is not enabled.\n\nURL: https://mariadb.com/kb/en/set-data-type/','','https://mariadb.com/kb/en/set-data-type/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (287,23,'UUID Data Type','MariaDB starting with 10.7.0\n----------------------------\nThe UUID data type was added in a MariaDB 10.7.0 preview.\n\nSyntax\n------\n\nUUID\n\nDescription\n-----------\n\nThe UUID data type is intended for the storage of 128-bit UUID (Universally\nUnique Identifier) data. See the UUID function page for more details on UUIDs\nthemselves.\n\nRetrieval\n---------\n\nData retrieved by this data type is in the string representation defined in\nRFC4122.\n\nCasting\n-------\n\nString literals of hexadecimal characters and CHAR/VARCHAR/TEXT can be cast to\nthe UUID data type. Likewise hexadecimal literals, binary-literals, and\nBINARY/VARBINARY/BLOB types can also be cast to UUID.\n\nThe data type will not accept a short UUID generated with the UUID_SHORT\nfunction, but will accept a value without the - character generated by the\nSYS_GUID function (or inserted directly). Hyphens can be partially omitted as\nwell, or included after any group of two digits.\n\nThe type does not accept UUIDs in braces, permitted by some implementations.\n\nStorage\n-------\n\nUUID are stored in an index friendly manner, the order of a UUID of\nllllllll-mmmm-Vhhh-vsss-nnnnnnnnnnnn is stored as:\n\nnnnnnnnnnnnn-vsss-Vhhh-mmmm-llllllll\n\nThis provides a sorting order, if a UUIDv1 (node and timestamp) is used, of\nthe node, followed by the timestamp.\n\nExamples\n--------\n\nCREATE TABLE t1 (id UUID);\n\nDirectly Inserting via string literals:\n\nINSERT INTO t1 VALUES(\'123e4567-e89b-12d3-a456-426655440000\');\n\nDirectly Inserting via hexadecimal literals:\n\nINSERT INTO t1 VALUES (x\'fffffffffffffffffffffffffffffffe\');\n\nGenerating and inserting via the UUID function.\n\nINSERT INTO t1 VALUES (UUID());\n\nRetrieval:\n\nSELECT * FROM t1;\n+--------------------------------------+\n| id |\n+--------------------------------------+\n| 123e4567-e89b-12d3-a456-426655440000 |\n| ffffffff-ffff-ffff-ffff-fffffffffffe |\n| 93aac041-1a14-11ec-ab4e-f859713e4be4 |\n+--------------------------------------+\n\nThe UUID_SHORT function does not generate valid full-length UUID:\n\nINSERT INTO t1 VALUES (UUID_SHORT());\nERROR 1292 (22007): Incorrect uuid value: \'99440417627439104\' \n for column `test`.`t1`.`id` at row 1\n\nAccepting a value without the - character, either directly or generated by the\nSYS_GUID function:\n\nINSERT INTO t1 VALUES (SYS_GUID());\n\nSELECT * FROM t1;\n+--------------------------------------+\n| id |\n+--------------------------------------+\n| 123e4567-e89b-12d3-a456-426655440000 |\n| ffffffff-ffff-ffff-ffff-fffffffffffe |\n| 93aac041-1a14-11ec-ab4e-f859713e4be4 |\n| ea0368d3-1a14-11ec-ab4e-f859713e4be4 |\n+--------------------------------------+\n\nSELECT SYS_GUID();\n+----------------------------------+\n| SYS_GUID() |\n+----------------------------------+\n| ff5b6bcc1a1411ecab4ef859713e4be4 |\n+----------------------------------+\n\nINSERT INTO t1 VALUES (\'ff5b6bcc1a1411ecab4ef859713e4be4\');\n\nSELECT * FROM t1;\n+--------------------------------------+\n| id |\n+--------------------------------------+\n| 123e4567-e89b-12d3-a456-426655440000 |\n| ffffffff-ffff-ffff-ffff-fffffffffffe |\n| 93aac041-1a14-11ec-ab4e-f859713e4be4 |\n| ea0368d3-1a14-11ec-ab4e-f859713e4be4 |\n| ff5b6bcc-1a14-11ec-ab4e-f859713e4be4 |\n+--------------------------------------+\n\nValid and invalid hyphen and brace usage:\n\nTRUNCATE t1;\n\nINSERT INTO t1 VALUES (\'f8aa-ed66-1a1b-11ec-ab4e-f859-713e-4be4\');\n\nINSERT INTO t1 VALUES (\'1b80667f1a1c-11ecab4ef859713e4be4\');\n\nINSERT INTO t1 VALUES (\'2fd6c945-1a-1c-11ec-ab4e-f859713e4be4\');\n\nINSERT INTO t1 VALUES (\'49-c9-f9-59-1a-1c-11ec-ab4e-f859713e4be4\');\n\nINSERT INTO t1 VALUES (\'57-96-da-c1-1a-1c-11-ec-ab-4e-f8-59-71-3e-4b-e4\');\n\nINSERT INTO t1 VALUES (\'6-eb74f8f-1a1c-11ec-ab4e-f859713e4be4\');\n\nINSERT INTO t1 VALUES (\'{29bad136-1a1d-11ec-ab4e-f859713e4be4}\');\nERROR 1292 (22007): Incorrect uuid value:\n\'{29bad136-1a1d-11ec-ab4e-f859713e4be4}\' \n for column `test`.`t1`.`id` at row 1\n\nSELECT * FROM t1;\n+--------------------------------------+\n| id |\n+--------------------------------------+\n| f8aaed66-1a1b-11ec-ab4e-f859713e4be4 |\n| 1b80667f-1a1c-11ec-ab4e-f859713e4be4 |\n| 2fd6c945-1a1c-11ec-ab4e-f859713e4be4 |\n| 49c9f959-1a1c-11ec-ab4e-f859713e4be4 |\n| 5796dac1-1a1c-11ec-ab4e-f859713e4be4 |\n| 6eb74f8f-1a1c-11ec-ab4e-f859713e4be4 |\n+--------------------------------------+\n\nURL: https://mariadb.com/kb/en/uuid-data-type/','','https://mariadb.com/kb/en/uuid-data-type/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (288,23,'DATE','Syntax\n------\n\nDATE\n\nDescription\n-----------\n\nA date. The supported range is \'1000-01-01\' to \'9999-12-31\'. MariaDB displays\nDATE values in \'YYYY-MM-DD\' format, but can be assigned dates in looser\nformats, including strings or numbers, as long as they make sense. These\ninclude a short year, YY-MM-DD, no delimiters, YYMMDD, or any other acceptable\ndelimiter, for example YYYY/MM/DD. For details, see date and time literals.\n\n\'0000-00-00\' is a permitted special value (zero-date), unless the NO_ZERO_DATE\nSQL_MODE is used. Also, individual components of a date can be set to 0 (for\nexample: \'2015-00-12\'), unless the NO_ZERO_IN_DATE SQL_MODE is used. In many\ncases, the result of en expression involving a zero-date, or a date with\nzero-parts, is NULL. If the ALLOW_INVALID_DATES SQL_MODE is enabled, if the\nday part is in the range between 1 and 31, the date does not produce any\nerror, even for months that have less than 31 days.\n\nOracle Mode\n-----------\n\nMariaDB starting with 10.3\n--------------------------\nIn Oracle mode from MariaDB 10.3, DATE with a time portion is a synonym for\nDATETIME. See also mariadb_schema.\n\nExamples\n--------\n\nCREATE TABLE t1 (d DATE);\n\nINSERT INTO t1 VALUES (\"2010-01-12\"), (\"2011-2-28\"), (\'120314\'),(\'13*04*21\');\n\nSELECT * FROM t1;\n+------------+\n| d |\n+------------+\n| 2010-01-12 |\n| 2011-02-28 |\n| 2012-03-14 |\n| 2013-04-21 |\n+------------+\n\nURL: https://mariadb.com/kb/en/date/','','https://mariadb.com/kb/en/date/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (289,23,'TIME','Syntax\n------\n\nTIME [()]\n\nDescription\n-----------\n\nA time. The range is \'-838:59:59.999999\' to \'838:59:59.999999\'. Microsecond\nprecision can be from 0-6; if not specified 0 is used. Microseconds have been\navailable since MariaDB 5.3.\n\nMariaDB displays TIME values in \'HH:MM:SS.ssssss\' format, but allows\nassignment of times in looser formats, including \'D HH:MM:SS\', \'HH:MM:SS\',\n\'HH:MM\', \'D HH:MM\', \'D HH\', \'SS\', or \'HHMMSS\', as well as permitting dropping\nof any leading zeros when a delimiter is provided, for example \'3:9:10\'. For\ndetails, see date and time literals.\n\nMariaDB starting with 10.1.2\n----------------------------\nMariaDB 10.1.2 introduced the --mysql56-temporal-format option, on by default,\nwhich allows MariaDB to store TIMEs using the same low-level format MySQL 5.6\nuses.\n\nInternal Format\n---------------\n\nIn MariaDB 10.1.2 a new temporal format was introduced from MySQL 5.6 that\nalters how the TIME, DATETIME and TIMESTAMP columns operate at lower levels.\nThese changes allow these temporal data types to have fractional parts and\nnegative values. You can disable this feature using the\nmysql56_temporal_format system variable.\n\nTables that include TIMESTAMP values that were created on an older version of\nMariaDB or that were created while the mysql56_temporal_format system variable\nwas disabled continue to store data using the older data type format.\n\nIn order to update table columns from the older format to the newer format,\nexecute an ALTER TABLE... MODIFY COLUMN statement that changes the column to\nthe *same* data type. This change may be needed if you want to export the\ntable\'s tablespace and import it onto a server that has\nmysql56_temporal_format=ON set (see MDEV-15225).\n\nFor instance, if you have a TIME column in your table:\n\nSHOW VARIABLES LIKE \'mysql56_temporal_format\';\n\n+-------------------------+-------+\n| Variable_name | Value |\n+-------------------------+-------+\n| mysql56_temporal_format | ON |\n+-------------------------+-------+\n\nALTER TABLE example_table MODIFY ts_col TIME;\n\nWhen MariaDB executes the ALTER TABLE statement, it converts the data from the\nolder temporal format to the newer one.\n\nIn the event that you have several tables and columns using temporal data\ntypes that you want to switch over to the new format, make sure the system\nvariable is enabled, then perform a dump and restore using mysqldump. The\ncolumns using relevant temporal data types are restored using the new temporal\nformat.\n\nStarting from MariaDB 10.5.1 columns with old temporal formats are marked with\na /* mariadb-5.3 */ comment in the output of SHOW CREATE TABLE, SHOW COLUMNS,\nDESCRIBE statements, as well as in the COLUMN_TYPE column of the\nINFORMATION_SCHEMA.COLUMNS Table.\n\nSHOW CREATE TABLE mariadb5312_time\\G\n*************************** 1. row ***************************\n Table: mariadb5312_time\nCreate Table: CREATE TABLE `mariadb5312_time` (\n `t0` time /* mariadb-5.3 */ DEFAULT NULL,\n `t6` time(6) /* mariadb-5.3 */ DEFAULT NULL\n) ENGINE=MyISAM DEFAULT CHARSET=latin1\n\nNote, columns with the current format are not marked with a comment.\n\nExamples\n--------\n\nINSERT INTO time VALUES (\'90:00:00\'), (\'800:00:00\'), (800), (22), (151413),\n(\'9:6:3\'), (\'12 09\');\n\nSELECT * FROM time;\n+-----------+\n| t |\n+-----------+\n| 90:00:00 |\n| 800:00:00 |\n| 00:08:00 |\n| 00:00:22 |\n| 15:14:13 |\n| 09:06:03 |\n| 297:00:00 |\n+-----------+\n\nURL: https://mariadb.com/kb/en/time/','','https://mariadb.com/kb/en/time/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (290,23,'DATETIME','Syntax\n------\n\nDATETIME [(microsecond precision)]\n\nDescription\n-----------\n\nA date and time combination.\n\nMariaDB displays DATETIME values in \'YYYY-MM-DD HH:MM:SS.ffffff\' format, but\nallows assignment of values to DATETIME columns using either strings or\nnumbers. For details, see date and time literals.\n\nDATETIME columns also accept CURRENT_TIMESTAMP as the default value.\n\nMariaDB 10.1.2 introduced the --mysql56-temporal-format option, on by default,\nwhich allows MariaDB to store DATETMEs using the same low-level format MySQL\n5.6 uses. For more information, see Internal Format, below.\n\nFor storage requirements, see Data Type Storage Requirements.\n\nSupported Values\n----------------\n\nMariaDB stores values that use the DATETIME data type in a format that\nsupports values between 1000-01-01 00:00:00.000000 and 9999-12-31\n23:59:59.999999.\n\nMariaDB can also store microseconds with a precision between 0 and 6. If no\nmicrosecond precision is specified, then 0 is used by default.\n\nMariaDB also supports \'0000-00-00\' as a special zero-date value, unless\nNO_ZERO_DATE is specified in the SQL_MODE. Similarly, individual components of\na date can be set to 0 (for example: \'2015-00-12\'), unless NO_ZERO_IN_DATE is\nspecified in the SQL_MODE. In many cases, the result of en expression\ninvolving a zero-date, or a date with zero-parts, is NULL. If the\nALLOW_INVALID_DATES SQL_MODE is enabled, if the day part is in the range\nbetween 1 and 31, the date does not produce any error, even for months that\nhave less than 31 days.\n\nTime Zones\n----------\n\nIf a column uses the DATETIME data type, then any inserted values are stored\nas-is, so no automatic time zone conversions are performed.\n\nMariaDB also does not currently support time zone literals that contain time\nzone identifiers. See MDEV-11829 for more information.\n\nMariaDB validates DATETIME literals against the session\'s time zone. For\nexample, if a specific time range never occurred in a specific time zone due\nto daylight savings time, then DATETIME values within that range would be\ninvalid for that time zone.\n\nFor example, daylight savings time started on March 10, 2019 in the US, so the\ntime range between 02:00:00 and 02:59:59 is invalid for that day in US time\nzones:\n\nSET time_zone = \'America/New_York\';\nQuery OK, 0 rows affected (0.000 sec)\n\nINSERT INTO timestamp_test VALUES (\'2019-03-10 02:55:05\');\nERROR 1292 (22007): Incorrect datetime value: \'2019-03-10 02:55:05\' for column\n`db1`.`timestamp_test`.`timestamp_test` at row 1\n\nBut that same time range is fine in other time zones, such as Coordinated\nUniversal Time (UTC). For example:\n\nSET time_zone = \'UTC\';\nQuery OK, 0 rows affected (0.000 sec)\n\nINSERT INTO timestamp_test VALUES (\'2019-03-10 02:55:05\');\nQuery OK, 1 row affected (0.002 sec)\n\nOracle Mode\n-----------\n\nMariaDB starting with 10.3\n--------------------------\nIn Oracle mode from MariaDB 10.3, DATE with a time portion is a synonym for\nDATETIME. See also mariadb_schema.\n\nInternal Format\n---------------\n\nIn MariaDB 10.1.2 a new temporal format was introduced from MySQL 5.6 that\nalters how the TIME, DATETIME and TIMESTAMP columns operate at lower levels.\nThese changes allow these temporal data types to have fractional parts and\nnegative values. You can disable this feature using the\nmysql56_temporal_format system variable.\n\nTables that include TIMESTAMP values that were created on an older version of\nMariaDB or that were created while the mysql56_temporal_format system variable\nwas disabled continue to store data using the older data type format.\n\nIn order to update table columns from the older format to the newer format,\nexecute an ALTER TABLE... MODIFY COLUMN statement that changes the column to\nthe *same* data type. This change may be needed if you want to export the\ntable\'s tablespace and import it onto a server that has\nmysql56_temporal_format=ON set (see MDEV-15225).\n\nFor instance, if you have a DATETIME column in your table:\n\nSHOW VARIABLES LIKE \'mysql56_temporal_format\';\n\n+-------------------------+-------+\n| Variable_name | Value |\n+-------------------------+-------+\n| mysql56_temporal_format | ON |\n+-------------------------+-------+\n\nALTER TABLE example_table MODIFY ts_col DATETIME;\n\nWhen MariaDB executes the ALTER TABLE statement, it converts the data from the\nolder temporal format to the newer one.\n\nIn the event that you have several tables and columns using temporal data\ntypes that you want to switch over to the new format, make sure the system\nvariable is enabled, then perform a dump and restore using mysqldump. The\ncolumns using relevant temporal data types are restored using the new temporal\nformat.\n\nStarting from MariaDB 10.5.1 columns with old temporal formats are marked with\na /* mariadb-5.3 */ comment in the output of SHOW CREATE TABLE, SHOW COLUMNS,\nDESCRIBE statements, as well as in the COLUMN_TYPE column of the\nINFORMATION_SCHEMA.COLUMNS Table.\n\nSHOW CREATE TABLE mariadb5312_datetime\\G\n*************************** 1. row ***************************\n Table: mariadb5312_datetime\nCreate Table: CREATE TABLE `mariadb5312_datetime` (\n `dt0` datetime /* mariadb-5.3 */ DEFAULT NULL,\n `dt6` datetime(6) /* mariadb-5.3 */ DEFAULT NULL\n) ENGINE=MyISAM DEFAULT CHARSET=latin1\n\nExamples\n--------\n\nCREATE TABLE t1 (d DATETIME);\n\nINSERT INTO t1 VALUES (\"2011-03-11\"), (\"2012-04-19 13:08:22\"),\n (\"2013-07-18 13:44:22.123456\");\n\nSELECT * FROM t1;\n+---------------------+\n| d |\n+---------------------+\n| 2011-03-11 00:00:00 |\n| 2012-04-19 13:08:22 |\n| 2013-07-18 13:44:22 |\n+---------------------+\n\nCREATE TABLE t2 (d DATETIME(6));\n\nINSERT INTO t2 VALUES (\"2011-03-11\"), (\"2012-04-19 13:08:22\"),\n (\"2013-07-18 13:44:22.123456\");\n\nSELECT * FROM t2;\n+----------------------------+\n| d |\n+----------------------------+\n| 2011-03-11 00:00:00.000000 |\n| 2012-04-19 13:08:22.000000 |\n| 2013-07-18 13:44:22.123456 |\n+----------------------------++\n\nStrings used in datetime context are automatically converted to datetime(6).\nIf you want to have a datetime without seconds, you should use\nCONVERT(..,datetime).\n\nSELECT CONVERT(\'2007-11-30 10:30:19\',datetime);\n+-----------------------------------------+\n| CONVERT(\'2007-11-30 10:30:19\',datetime) |\n+-----------------------------------------+\n| 2007-11-30 10:30:19 |\n+-----------------------------------------+\n\nSELECT CONVERT(\'2007-11-30 10:30:19\',datetime(6));\n+--------------------------------------------+\n| CONVERT(\'2007-11-30 10:30:19\',datetime(6)) |\n+--------------------------------------------+\n| 2007-11-30 10:30:19.000000 |\n+--------------------------------------------+\n\nURL: https://mariadb.com/kb/en/datetime/','','https://mariadb.com/kb/en/datetime/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (291,23,'TIMESTAMP','Syntax\n------\n\nTIMESTAMP [(0) THEN BEGIN NOT ATOMIC SELECT 1; END ; END IF;;\n\nExample of how to use WHILE loop:\n\nDELIMITER |\nBEGIN NOT ATOMIC\n DECLARE x INT DEFAULT 0;\n WHILE x <= 10 DO\n SET x = x + 1;\n SELECT x;\n END WHILE;\nEND|\nDELIMITER ;\n\nURL:\nhttps://mariadb.com/kb/en/using-compound-statements-outside-of-stored-programs/','','https://mariadb.com/kb/en/using-compound-statements-outside-of-stored-programs/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (295,24,'BEGIN END','Syntax\n------\n\n[begin_label:] BEGIN [NOT ATOMIC]\n [statement_list]\nEND [end_label]\n\nNOT ATOMIC is required when used outside of a stored procedure. Inside stored\nprocedures or within an anonymous block, BEGIN alone starts a new anonymous\nblock.\n\nDescription\n-----------\n\nBEGIN ... END syntax is used for writing compound statements. A compound\nstatement can contain multiple statements, enclosed by the BEGIN and END\nkeywords. statement_list represents a list of one or more statements, each\nterminated by a semicolon (i.e., ;) statement delimiter. statement_list is\noptional, which means that the empty compound statement (BEGIN END) is legal.\n\nNote that END will perform a commit. If you are running in autocommit mode,\nevery statement will be committed separately. If you are not running in\nautocommit mode, you must execute a COMMIT or ROLLBACK after END to get the\ndatabase up to date.\n\nUse of multiple statements requires that a client is able to send statement\nstrings containing the ; statement delimiter. This is handled in the mysql\ncommand-line client with the DELIMITER command. Changing the ;\nend-of-statement delimiter (for example, to //) allows ; to be used in a\nprogram body.\n\nA compound statement within a stored program can be labeled. end_label cannot\nbe given unless begin_label also is present. If both are present, they must be\nthe same.\n\nBEGIN ... END constructs can be nested. Each block can define its own\nvariables, a CONDITION, a HANDLER and a CURSOR, which don\'t exist in the outer\nblocks. The most local declarations override the outer objects which use the\nsame name (see example below).\n\nThe declarations order is the following:\n\n* DECLARE local variables;\n* DECLARE CONDITIONs;\n* DECLARE CURSORs;\n* DECLARE HANDLERs;\n\nNote that DECLARE HANDLER contains another BEGIN ... END construct.\n\nHere is an example of a very simple, anonymous block:\n\nBEGIN NOT ATOMIC\nSET @a=1;\nCREATE TABLE test.t1(a INT);\nEND|\n\nBelow is an example of nested blocks in a stored procedure:\n\nCREATE PROCEDURE t( )\nBEGIN\n DECLARE x TINYINT UNSIGNED DEFAULT 1;\n BEGIN\n DECLARE x CHAR(2) DEFAULT \'02\';\n DECLARE y TINYINT UNSIGNED DEFAULT 10;\n SELECT x, y;\n END;\n SELECT x;\nEND;\n\nIn this example, a TINYINT variable, x is declared in the outter block. But in\nthe inner block x is re-declared as a CHAR and an y variable is declared. The\ninner SELECT shows the \"new\" value of x, and the value of y. But when x is\nselected in the outer block, the \"old\" value is returned. The final SELECT\ndoesn\'t try to read y, because it doesn\'t exist in that context.\n\nURL: https://mariadb.com/kb/en/begin-end/','','https://mariadb.com/kb/en/begin-end/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (296,24,'CASE Statement','Syntax\n------\n\nCASE case_value\n WHEN when_value THEN statement_list\n [WHEN when_value THEN statement_list] ...\n [ELSE statement_list]\nEND CASE\n\nOr:\n\nCASE\n WHEN search_condition THEN statement_list\n [WHEN search_condition THEN statement_list] ...\n [ELSE statement_list]\nEND CASE\n\nDescription\n-----------\n\nThe text on this page describes the CASE statement for stored programs. See\nthe CASE OPERATOR for details on the CASE operator outside of stored programs.\n\nThe CASE statement for stored programs implements a complex conditional\nconstruct. If a search_condition evaluates to true, the corresponding SQL\nstatement list is executed. If no search condition matches, the statement list\nin the ELSE clause is executed. Each statement_list consists of one or more\nstatements.\n\nThe CASE statement cannot have an ELSE NULL clause, and it is terminated with\nEND CASE instead of END. implements a complex conditional construct. If a\nsearch_condition evaluates to true, the corresponding SQL statement list is\nexecuted. If no search condition matches, the statement list in the ELSE\nclause is executed. Each statement_list consists of one or more statements.\n\nIf no when_value or search_condition matches the value tested and the CASE\nstatement contains no ELSE clause, a Case not found for CASE statement error\nresults.\n\nEach statement_list consists of one or more statements; an empty\nstatement_list is not allowed. To handle situations where no value is matched\nby any WHEN clause, use an ELSE containing an empty BEGIN ... END block, as\nshown in this example:\n\nDELIMITER |\nCREATE PROCEDURE p()\nBEGIN\n DECLARE v INT DEFAULT 1;\n CASE v\n WHEN 2 THEN SELECT v;\n WHEN 3 THEN SELECT 0;\n ELSE BEGIN END;\n END CASE;\nEND;\n|\n\nThe indentation used here in the ELSE clause is for purposes of clarity only,\nand is not otherwise significant. See Delimiters in the mysql client for more\non the use of the delimiter command.\n\nNote: The syntax of the CASE statement used inside stored programs differs\nslightly from that of the SQL CASE expression described in CASE OPERATOR. The\nCASE statement cannot have an ELSE NULL clause, and it is terminated with END\nCASE instead of END.\n\nURL: https://mariadb.com/kb/en/case-statement/','','https://mariadb.com/kb/en/case-statement/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (297,24,'DECLARE CONDITION','Syntax\n------\n\nDECLARE condition_name CONDITION FOR condition_value\n\ncondition_value:\n SQLSTATE [VALUE] sqlstate_value\n | mysql_error_code\n\nDescription\n-----------\n\nThe DECLARE ... CONDITION statement defines a named error condition. It\nspecifies a condition that needs specific handling and associates a name with\nthat condition. Later, the name can be used in a DECLARE ... HANDLER, SIGNAL\nor RESIGNAL statement (as long as the statement is located in the same BEGIN\n... END block).\n\nConditions must be declared after local variables, but before CURSORs and\nHANDLERs.\n\nA condition_value for DECLARE ... CONDITION can be an SQLSTATE value (a\n5-character string literal) or a MySQL error code (a number). You should not\nuse SQLSTATE value \'00000\' or MySQL error code 0, because those indicate\nsucess rather than an error condition. If you try, or if you specify an\ninvalid SQLSTATE value, an error like this is produced:\n\nERROR 1407 (42000): Bad SQLSTATE: \'00000\'\n\nFor a list of SQLSTATE values and MariaDB error codes, see MariaDB Error Codes.\n\nURL: https://mariadb.com/kb/en/declare-condition/','','https://mariadb.com/kb/en/declare-condition/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (298,24,'DECLARE HANDLER','Syntax\n------\n\nDECLARE handler_type HANDLER\n FOR condition_value [, condition_value] ...\n statement\n\nhandler_type:\n CONTINUE\n | EXIT\n | UNDO\n\ncondition_value:\n SQLSTATE [VALUE] sqlstate_value\n | condition_name\n | SQLWARNING\n | NOT FOUND\n | SQLEXCEPTION\n | mariadb_error_code\n\nDescription\n-----------\n\nThe DECLARE ... HANDLER statement specifies handlers that each may deal with\none or more conditions. If one of these conditions occurs, the specified\nstatement is executed. statement can be a simple statement (for example, SET\nvar_name = value), or it can be a compound statement written using BEGIN and\nEND.\n\nHandlers must be declared after local variables, a CONDITION and a CURSOR.\n\nFor a CONTINUE handler, execution of the current program continues after\nexecution of the handler statement. For an EXIT handler, execution terminates\nfor the BEGIN ... END compound statement in which the handler is declared.\n(This is true even if the condition occurs in an inner block.) The UNDO\nhandler type statement is not supported.\n\nIf a condition occurs for which no handler has been declared, the default\naction is EXIT.\n\nA condition_value for DECLARE ... HANDLER can be any of the following values:\n\n* An SQLSTATE value (a 5-character string literal) or a MariaDB error\ncode (a number). You should not use SQLSTATE value \'00000\' or MariaDB\nerror code 0, because those indicate sucess rather than an error\ncondition. For a list of SQLSTATE values and MariaDB error codes, see\nMariaDB Error Codes.\n* A condition name previously specified with DECLARE ... CONDITION. It must be\nin the same stored program. See DECLARE CONDITION.\n* SQLWARNING is shorthand for the class of SQLSTATE values that begin\nwith \'01\'.\n* NOT FOUND is shorthand for the class of SQLSTATE values that begin\nwith \'02\'. This is relevant only the context of cursors and is used to\ncontrol what happens when a cursor reaches the end of a data set. If\nno more rows are available, a No Data condition occurs with SQLSTATE\nvalue 02000. To detect this condition, you can set up a handler for it\n(or for a NOT FOUND condition). An example is shown in Cursor Overview. This\ncondition also occurs for SELECT ... INTO var_list statements that retrieve no\nrows.\n* SQLEXCEPTION is shorthand for the class of SQLSTATE values that do\nnot begin with \'00\', \'01\', or \'02\'.\n\nWhen an error raises, in some cases it could be handled by multiple HANDLERs.\nFor example, there may be an handler for 1050 error, a separate handler for\nthe 42S01 SQLSTATE, and another separate handler for the SQLEXCEPTION class:\nin theory all occurrences of HANDLER may catch the 1050 error, but MariaDB\nchooses the HANDLER with the highest precedence. Here are the precedence rules:\n\n* Handlers which refer to an error code have the highest precedence.\n* Handlers which refer to a SQLSTATE come next.\n* Handlers which refer to an error class have the lowest precedence.\n\nIn some cases, a statement could produce multiple errors. If this happens, in\nsome cases multiple handlers could have the highest precedence. In such cases,\nthe choice of the handler is indeterminate.\n\nNote that if an error occurs within a CONTINUE HANDLER block, it can be\nhandled by another HANDLER. However, a HANDLER which is already in the stack\n(that is, it has been called to handle an error and its execution didn\'t\nfinish yet) cannot handle new errors—this prevents endless loops. For example,\nsuppose that a stored procedure contains a CONTINUE HANDLER for SQLWARNING and\nanother CONTINUE HANDLER for NOT FOUND. At some point, a NOT FOUND error\noccurs, and the execution enters the NOT FOUND HANDLER. But within that\nhandler, a warning occurs, and the execution enters the SQLWARNING HANDLER. If\nanother NOT FOUND error occurs, it cannot be handled again by the NOT FOUND\nHANDLER, because its execution is not finished.\n\nWhen a DECLARE HANDLER block can handle more than one error condition, it may\nbe useful to know which errors occurred. To do so, you can use the GET\nDIAGNOSTICS statement.\n\nAn error that is handled by a DECLARE HANDLER construct can be issued again\nusing the RESIGNAL statement.\n\nBelow is an example using DECLARE HANDLER:\n\nCREATE TABLE test.t (s1 INT, PRIMARY KEY (s1));\n\nDELIMITER //\n\nCREATE PROCEDURE handlerdemo ( )\n BEGIN\n DECLARE CONTINUE HANDLER FOR SQLSTATE \'23000\' SET @x2 = 1;\n SET @x = 1;\n INSERT INTO test.t VALUES (1);\n SET @x = 2;\n INSERT INTO test.t VALUES (1);\n SET @x = 3;\n END;\n //\n\nDELIMITER ;\n\nCALL handlerdemo( );\n\nSELECT @x;\n+------+\n| @x |\n+------+\n| 3 |\n+------+\n\nURL: https://mariadb.com/kb/en/declare-handler/','','https://mariadb.com/kb/en/declare-handler/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (299,24,'DECLARE Variable','Syntax\n------\n\nDECLARE var_name [, var_name] ... [[ROW] TYPE OF]] type [DEFAULT value]\n\nDescription\n-----------\n\nThis statement is used to declare local variables within stored programs. To\nprovide a default value for the variable, include a DEFAULT clause. The value\ncan be specified as an expression (even subqueries are permitted); it need not\nbe a constant. If the DEFAULT clause is missing, the initial value is NULL.\n\nLocal variables are treated like stored routine parameters with respect to\ndata type and overflow checking. See CREATE PROCEDURE.\n\nLocal variables must be declared before CONDITIONs, CURSORs and HANDLERs.\n\nLocal variable names are not case sensitive.\n\nThe scope of a local variable is within the BEGIN ... END block where it is\ndeclared. The variable can be referred to in blocks nested within the\ndeclaring block, except those blocks that declare a variable with the same\nname.\n\nTYPE OF / ROW TYPE OF\n---------------------\n\nMariaDB starting with 10.3\n--------------------------\nTYPE OF and ROW TYPE OF anchored data types for stored routines were\nintroduced in MariaDB 10.3.\n\nAnchored data types allow a data type to be defined based on another object,\nsuch as a table row, rather than specifically set in the declaration. If the\nanchor object changes, so will the anchored data type. This can lead to\nroutines being easier to maintain, so that if the data type in the table is\nchanged, it will automatically be changed in the routine as well.\n\nVariables declared with ROW TYPE OF will have the same features as implicit\nROW variables. It is not possible to use ROW TYPE OF variables in a LIMIT\nclause.\n\nThe real data type of TYPE OF and ROW TYPE OF table_name will become known at\nthe very beginning of the stored routine call. ALTER TABLE or DROP TABLE\nstatements performed inside the current routine on the tables that appear in\nanchors won\'t affect the data type of the anchored variables, even if the\nvariable is declared after an ALTER TABLE or DROP TABLE statement.\n\nThe real data type of a ROW TYPE OF cursor_name variable will become known\nwhen execution enters into the block where the variable is declared. Data type\ninstantiation will happen only once. In a cursor ROW TYPE OF variable that is\ndeclared inside a loop, its data type will become known on the very first\niteration and won\'t change on further loop iterations.\n\nThe tables referenced in TYPE OF and ROW TYPE OF declarations will be checked\nfor existence at the beginning of the stored routine call. CREATE PROCEDURE or\nCREATE FUNCTION will not check the referenced tables for existence.\n\nExamples\n--------\n\nTYPE OF and ROW TYPE OF from MariaDB 10.3:\n\nDECLARE tmp TYPE OF t1.a; -- Get the data type from the column {{a}} in the\ntable {{t1}}\n\nDECLARE rec1 ROW TYPE OF t1; -- Get the row data type from the table {{t1}}\n\nDECLARE rec2 ROW TYPE OF cur1; -- Get the row data type from the cursor\n{{cur1}}\n\nURL: https://mariadb.com/kb/en/declare-variable/','','https://mariadb.com/kb/en/declare-variable/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (300,24,'FOR','MariaDB starting with 10.3\n--------------------------\nFOR loops were introduced in MariaDB 10.3.\n\nSyntax\n------\n\nInteger range FOR loop:\n\n[begin_label:]\nFOR var_name IN [ REVERSE ] lower_bound .. upper_bound\nDO statement_list\nEND FOR [ end_label ]\n\nExplicit cursor FOR loop\n\n[begin_label:]\nFOR record_name IN cursor_name [ ( cursor_actual_parameter_list)]\nDO statement_list\nEND FOR [ end_label ]\n\nExplicit cursor FOR loop (Oracle mode)\n\n[begin_label:]\nFOR record_name IN cursor_name [ ( cursor_actual_parameter_list)]\nLOOP\n statement_list\nEND LOOP [ end_label ]\n\nImplicit cursor FOR loop\n\n[begin_label:]\nFOR record_name IN ( select_statement )\nDO statement_list\nEND FOR [ end_label ]\n\nDescription\n-----------\n\nFOR loops allow code to be executed a fixed number of times.\n\nIn an integer range FOR loop, MariaDB will compare the lower bound and upper\nbound values, and assign the lower bound value to a counter. If REVERSE is not\nspecified, and the upper bound value is greater than or equal to the counter,\nthe counter will be incremented and the statement will continue, after which\nthe loop is entered again. If the upper bound value is greater than the\ncounter, the loop will be exited.\n\nIf REVERSE is specified, the counter is decremented, and the upper bound value\nneeds to be less than or equal for the loop to continue.\n\nExamples\n--------\n\nIntger range FOR loop:\n\nCREATE TABLE t1 (a INT);\n\nDELIMITER //\n\nFOR i IN 1..3\nDO\n INSERT INTO t1 VALUES (i);\nEND FOR;\n//\n\nDELIMITER ;\n\nSELECT * FROM t1;\n+------+\n| a |\n+------+\n| 1 |\n| 2 |\n| 3 |\n+------+\n\nREVERSE integer range FOR loop:\n\nCREATE OR REPLACE TABLE t1 (a INT);\n\nDELIMITER //\nFOR i IN REVERSE 4..12\n DO\n INSERT INTO t1 VALUES (i);\nEND FOR;\n//\nQuery OK, 9 rows affected (0.422 sec)\n\nDELIMITER ;\n\nSELECT * FROM t1;\n+------+\n| a |\n+------+\n| 12 |\n| 11 |\n| 10 |\n| 9 |\n| 8 |\n| 7 |\n| 6 |\n| 5 |\n| 4 |\n+------+\n\nExplicit cursor in Oracle mode:\n\nSET sql_mode=ORACLE;\n\nCREATE OR REPLACE TABLE t1 (a INT, b VARCHAR(32));\n\nINSERT INTO t1 VALUES (10,\'b0\');\nINSERT INTO t1 VALUES (11,\'b1\');\nINSERT INTO t1 VALUES (12,\'b2\');\n\nDELIMITER //\n\nCREATE OR REPLACE PROCEDURE p1(pa INT) AS \n CURSOR cur(va INT) IS\n SELECT a, b FROM t1 WHERE a=va;\nBEGIN\n FOR rec IN cur(pa)\n LOOP\n SELECT rec.a, rec.b;\n END LOOP;\nEND;\n//\n\nDELIMITER ;\n\nCALL p1(10);\n+-------+-------+\n| rec.a | rec.b |\n+-------+-------+\n| 10 | b0 |\n+-------+-------+\n\nCALL p1(11);\n+-------+-------+\n| rec.a | rec.b |\n+-------+-------+\n| 11 | b1 |\n+-------+-------+\n\nCALL p1(12);\n+-------+-------+\n| rec.a | rec.b |\n+-------+-------+\n| 12 | b2 |\n+-------+-------+\n\nCALL p1(13);\nQuery OK, 0 rows affected (0.000 sec)\n\nURL: https://mariadb.com/kb/en/for/','','https://mariadb.com/kb/en/for/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (301,24,'GOTO','MariaDB starting with 10.3\n--------------------------\nThe GOTO statement was introduced in MariaDB 10.3 for Oracle compatibility.\n\nSyntax\n------\n\nGOTO label\n\nDescription\n-----------\n\nThe GOTO statement causes the code to jump to the specified label, and\ncontinue operating from there. It is only accepted when in Oracle mode.\n\nExample\n-------\n\nSET sql_mode=ORACLE;\n\nDELIMITER //\n\nCREATE OR REPLACE PROCEDURE p1 AS\n\nBEGIN\n\nSELECT 1;\n GOTO label;\n SELECT 2;\n <