From 54ed3939f7fc8008aac72999b6c5fc5dae9c0467 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 22 Dec 2023 23:09:27 -0800 Subject: [PATCH 001/129] MDEV-31657 Crash on query using CTE with the same name as a base table If a query contained a CTE whose name coincided with the name of one of the base tables used in the specification of the CTE and the query had at least two references to this CTE in the specifications of other CTEs then processing of the query led to unlimited recursion that ultimately caused a crash of the server. Any secondary non-recursive reference to a CTE requires creation of a copy of the CTE specification. All the references to CTEs in this copy must be resolved. If the specification contains a reference to a base table whose name coincides with the name of then CTE then it should be ensured that this reference in no way can be resolved against the name of the CTE. --- mysql-test/main/cte_nonrecursive.result | 45 ++++++++++++++++++++++++ mysql-test/main/cte_nonrecursive.test | 46 +++++++++++++++++++++++++ sql/sql_acl.cc | 5 --- sql/sql_cte.cc | 34 ++++++++++++------ sql/sql_cte.h | 6 ++-- sql/sql_lex.h | 6 ++-- sql/sql_view.cc | 3 +- 7 files changed, 124 insertions(+), 21 deletions(-) diff --git a/mysql-test/main/cte_nonrecursive.result b/mysql-test/main/cte_nonrecursive.result index e86c101686cbb..f499b7a532122 100644 --- a/mysql-test/main/cte_nonrecursive.result +++ b/mysql-test/main/cte_nonrecursive.result @@ -2624,4 +2624,49 @@ a 1 1 DROP TABLE t1; +# +# MDEV-31657: CTE with the same name as base table used twice +# in another CTE +# +create table t (a int); +insert into t values (3), (7), (1); +with +t as (select * from t), +cte as (select t1.a as t1a, t2.a as t2a from t as t1, t as t2 where t1.a=t2.a) +select * from cte; +t1a t2a +3 3 +7 7 +1 1 +create table s (a int); +insert into s values (1), (4), (7); +with +t as (select * from t), +s as (select a-1 as a from s), +cte as (select t.a as ta, s.a as sa from t, s where t.a=s.a +union +select t.a+1, s.a+1 from t, s where t.a=s.a+1) +select * from cte; +ta sa +3 3 +2 1 +8 7 +with +t as (select * from t), +cte as (select t.a as ta, s.a as sa from t, s where t.a=s.a +union +select t.a+1, s.a+1 from t, s where t.a=s.a), +s as (select a+10 as a from s) +select * from cte; +ta sa +1 1 +7 7 +2 2 +8 8 +drop table t,s; +with +t as (select * from t), +cte as (select t1.a as t1a, t2.a as t2a from t as t1, t as t2 where t1.a=t2.a) +select * from cte; +ERROR 42S02: Table 'test.t' doesn't exist # End of 10.4 tests diff --git a/mysql-test/main/cte_nonrecursive.test b/mysql-test/main/cte_nonrecursive.test index a666ed3a25f14..0d342edf97d58 100644 --- a/mysql-test/main/cte_nonrecursive.test +++ b/mysql-test/main/cte_nonrecursive.test @@ -1967,4 +1967,50 @@ SELECT * FROM t1; DROP TABLE t1; +--echo # +--echo # MDEV-31657: CTE with the same name as base table used twice +--echo # in another CTE +--echo # + +create table t (a int); +insert into t values (3), (7), (1); + +let $q1= +with +t as (select * from t), +cte as (select t1.a as t1a, t2.a as t2a from t as t1, t as t2 where t1.a=t2.a) +select * from cte; + +eval $q1; + +create table s (a int); +insert into s values (1), (4), (7); + +let $q2= +with +t as (select * from t), +s as (select a-1 as a from s), +cte as (select t.a as ta, s.a as sa from t, s where t.a=s.a + union + select t.a+1, s.a+1 from t, s where t.a=s.a+1) +select * from cte; + +eval $q2; + +let $q3= +with +t as (select * from t), +cte as (select t.a as ta, s.a as sa from t, s where t.a=s.a + union + select t.a+1, s.a+1 from t, s where t.a=s.a), +s as (select a+10 as a from s) +select * from cte; + +eval $q3; + +drop table t,s; + +--ERROR ER_NO_SUCH_TABLE +eval $q1; + --echo # End of 10.4 tests diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 82f8101e929fc..6a6a6b3984dba 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -8137,11 +8137,6 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, INSERT_ACL : SELECT_ACL); } - if (tl->with || !tl->db.str || - (tl->select_lex && - (tl->with= tl->select_lex->find_table_def_in_with_clauses(tl)))) - continue; - const ACL_internal_table_access *access= get_cached_table_access(&t_ref->grant.m_internal, t_ref->get_db_name(), diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index 0e422b30216c3..ce735ba26d14b 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -105,6 +105,7 @@ bool LEX::check_dependencies_in_with_clauses() @param tables Points to the beginning of the sub-chain @param tables_last Points to the address with the sub-chain barrier + @param excl_spec Ignore the definition with this spec @details The method resolves tables references to CTE from the chain of @@ -146,7 +147,8 @@ bool LEX::check_dependencies_in_with_clauses() */ bool LEX::resolve_references_to_cte(TABLE_LIST *tables, - TABLE_LIST **tables_last) + TABLE_LIST **tables_last, + st_select_lex_unit *excl_spec) { With_element *with_elem= 0; @@ -155,7 +157,8 @@ bool LEX::resolve_references_to_cte(TABLE_LIST *tables, if (tbl->derived) continue; if (!tbl->db.str && !tbl->with) - tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl); + tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl, + excl_spec); if (!tbl->with) // no CTE matches table reference tbl { if (only_cte_resolution) @@ -243,7 +246,7 @@ LEX::check_cte_dependencies_and_resolve_references() return true; if (!with_cte_resolution) return false; - if (resolve_references_to_cte(query_tables, query_tables_last)) + if (resolve_references_to_cte(query_tables, query_tables_last, NULL)) return true; return false; } @@ -387,6 +390,7 @@ bool With_element::check_dependencies_in_spec() @param table The reference to the table that is looked for @param barrier The barrier with element for the search + @param excl_spec Ignore the definition with this spec @details The function looks through the elements of this with clause trying to find @@ -400,12 +404,15 @@ bool With_element::check_dependencies_in_spec() */ With_element *With_clause::find_table_def(TABLE_LIST *table, - With_element *barrier) + With_element *barrier, + st_select_lex_unit *excl_spec) { for (With_element *with_elem= with_list.first; with_elem != barrier; with_elem= with_elem->next) { + if (excl_spec && with_elem->spec == excl_spec) + continue; if (my_strcasecmp(system_charset_info, with_elem->get_name_str(), table->table_name.str) == 0 && !table->is_fqtn) @@ -465,7 +472,7 @@ With_element *find_table_def_in_with_clauses(TABLE_LIST *tbl, top_unit->with_element && top_unit->with_element->get_owner() == with_clause) barrier= top_unit->with_element; - found= with_clause->find_table_def(tbl, barrier); + found= with_clause->find_table_def(tbl, barrier, NULL); if (found) break; } @@ -520,10 +527,11 @@ void With_element::check_dependencies_in_select(st_select_lex *sl, { With_clause *with_clause= sl->master_unit()->with_clause; if (with_clause) - tbl->with= with_clause->find_table_def(tbl, NULL); + tbl->with= with_clause->find_table_def(tbl, NULL, NULL); if (!tbl->with) tbl->with= owner->find_table_def(tbl, - owner->with_recursive ? NULL : this); + owner->with_recursive ? NULL : this, + NULL); } if (!tbl->with) tbl->with= find_table_def_in_with_clauses(tbl, ctxt); @@ -1098,7 +1106,8 @@ st_select_lex_unit *With_element::clone_parsed_spec(LEX *old_lex, */ lex->only_cte_resolution= old_lex->only_cte_resolution; if (lex->resolve_references_to_cte(lex->query_tables, - lex->query_tables_last)) + lex->query_tables_last, + spec)) { res= NULL; goto err; @@ -1264,6 +1273,7 @@ bool With_element::is_anchor(st_select_lex *sel) Search for the definition of the given table referred in this select node @param table reference to the table whose definition is searched for + @param excl_spec ignore the definition with this spec @details The method looks for the definition of the table whose reference is occurred @@ -1276,7 +1286,8 @@ bool With_element::is_anchor(st_select_lex *sel) NULL - otherwise */ -With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table) +With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table, + st_select_lex_unit *excl_spec) { With_element *found= NULL; With_clause *containing_with_clause= NULL; @@ -1293,7 +1304,7 @@ With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table) With_clause *attached_with_clause= sl->get_with_clause(); if (attached_with_clause && attached_with_clause != containing_with_clause && - (found= attached_with_clause->find_table_def(table, NULL))) + (found= attached_with_clause->find_table_def(table, NULL, excl_spec))) break; master_unit= sl->master_unit(); outer_sl= master_unit->outer_select(); @@ -1303,7 +1314,8 @@ With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table) containing_with_clause= with_elem->get_owner(); With_element *barrier= containing_with_clause->with_recursive ? NULL : with_elem; - if ((found= containing_with_clause->find_table_def(table, barrier))) + if ((found= containing_with_clause->find_table_def(table, barrier, + excl_spec))) break; if (outer_sl && !outer_sl->get_with_element()) break; diff --git a/sql/sql_cte.h b/sql/sql_cte.h index 4bc3ed69582cc..4d56672e3cc58 100644 --- a/sql/sql_cte.h +++ b/sql/sql_cte.h @@ -322,7 +322,8 @@ class With_element : public Sql_alloc friend bool LEX::resolve_references_to_cte(TABLE_LIST *tables, - TABLE_LIST **tables_last); + TABLE_LIST **tables_last, + st_select_lex_unit *excl_spec); }; const uint max_number_of_elements_in_with_clause= sizeof(table_map)*8; @@ -422,7 +423,8 @@ class With_clause : public Sql_alloc void move_anchors_ahead(); - With_element *find_table_def(TABLE_LIST *table, With_element *barrier); + With_element *find_table_def(TABLE_LIST *table, With_element *barrier, + st_select_lex_unit *excl_spec); With_element *find_table_def_in_with_clauses(TABLE_LIST *table); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 0f2e096fdc675..2305616d5b366 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1531,7 +1531,8 @@ class st_select_lex: public st_select_lex_node master_unit()->cloned_from->with_element : master_unit()->with_element; } - With_element *find_table_def_in_with_clauses(TABLE_LIST *table); + With_element *find_table_def_in_with_clauses(TABLE_LIST *table, + st_select_lex_unit * excl_spec); bool check_unrestricted_recursive(bool only_standard_compliant); bool check_subqueries_with_recursive_references(); void collect_grouping_fields_for_derived(THD *thd, ORDER *grouping_list); @@ -4708,7 +4709,8 @@ struct LEX: public Query_tables_list bool check_dependencies_in_with_clauses(); bool check_cte_dependencies_and_resolve_references(); bool resolve_references_to_cte(TABLE_LIST *tables, - TABLE_LIST **tables_last); + TABLE_LIST **tables_last, + st_select_lex_unit *excl_spec); }; diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 9f9a97169824a..0dca5f7c226bb 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -295,7 +295,8 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local) { if (!tbl->with && tbl->select_lex) - tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl); + tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl, + NULL); /* Ensure that we have some privileges on this table, more strict check will be done on column level after preparation, From 613d0194979849fb5b3dd752f13b14672a2409e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 4 Jan 2024 12:50:05 +0200 Subject: [PATCH 002/129] MDEV-33160 show_status_array() calls various functions via incompatible pointer In commit b4ff64568c88ab3ce559e7bd39853d9cbf86704a the signature of mysql_show_var_func was changed, but not all functions of that type were adjusted. When the server is configured with `cmake -DWITH_ASAN=ON` and compiled with clang, runtime errors would be flagged for invoking functions through an incompatible function pointer. Reviewed by: Michael 'Monty' Widenius --- sql/mysqld.cc | 144 +++++++++++++++++------------------- sql/sql_acl.cc | 8 +- sql/table_cache.cc | 4 +- sql/table_cache.h | 4 +- sql/threadpool.h | 3 - storage/sphinx/ha_sphinx.cc | 19 +++-- storage/sphinx/ha_sphinx.h | 6 -- storage/spider/spd_param.cc | 18 +++-- 8 files changed, 102 insertions(+), 104 deletions(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a1a8a862a8e6e..5713045056dd8 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -7180,8 +7180,8 @@ struct my_option my_long_options[]= MYSQL_TO_BE_IMPLEMENTED_OPTION("validate-user-plugins") // NO_EMBEDDED_ACCESS_CHECKS }; -static int show_queries(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_queries(THD *thd, SHOW_VAR *var, void *, + system_status_var *, enum_var_type) { var->type= SHOW_LONGLONG; var->value= &thd->query_id; @@ -7189,16 +7189,16 @@ static int show_queries(THD *thd, SHOW_VAR *var, char *buff, } -static int show_net_compression(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_net_compression(THD *thd, SHOW_VAR *var, void *, + system_status_var *, enum_var_type) { var->type= SHOW_MY_BOOL; var->value= &thd->net.compress; return 0; } -static int show_starttime(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_starttime(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -7207,8 +7207,8 @@ static int show_starttime(THD *thd, SHOW_VAR *var, char *buff, } #ifdef ENABLED_PROFILING -static int show_flushstatustime(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_flushstatustime(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -7218,32 +7218,28 @@ static int show_flushstatustime(THD *thd, SHOW_VAR *var, char *buff, #endif #ifdef HAVE_REPLICATION -static int show_rpl_status(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_rpl_status(THD *, SHOW_VAR *var, void *, system_status_var *, + enum_var_type) { var->type= SHOW_CHAR; var->value= const_cast(rpl_status_type[(int)rpl_status]); return 0; } -static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_slave_running(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { - Master_info *mi= NULL; - bool UNINIT_VAR(tmp); - - var->type= SHOW_MY_BOOL; - var->value= buff; - - if ((mi= get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_NOTE))) + if (Master_info *mi= + get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_NOTE)) { - tmp= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_READING && - mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN); + *((my_bool*) buff)= + (mi->slave_running == MYSQL_SLAVE_RUN_READING && + mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN); mi->release(); + var->type= SHOW_MY_BOOL; + var->value= buff; } - if (mi) - *((my_bool *)buff)= tmp; else var->type= SHOW_UNDEF; return 0; @@ -7253,7 +7249,8 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff, /* How many masters this slave is connected to */ -static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff) +static int show_slaves_running(THD *, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONGLONG; var->value= buff; @@ -7264,19 +7261,17 @@ static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff) } -static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { - Master_info *mi; - - var->type= SHOW_LONGLONG; - var->value= buff; - - if ((mi= get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_NOTE))) + if (Master_info *mi= + get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_NOTE)) { *((longlong *)buff)= mi->received_heartbeats; mi->release(); + var->type= SHOW_LONGLONG; + var->value= buff; } else var->type= SHOW_UNDEF; @@ -7284,19 +7279,17 @@ static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff, } -static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_heartbeat_period(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { - Master_info *mi; - - var->type= SHOW_CHAR; - var->value= buff; - - if ((mi= get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_NOTE))) + if (Master_info *mi= + get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_NOTE)) { - sprintf(buff, "%.3f", mi->heartbeat_period); + sprintf(static_cast(buff), "%.3f", mi->heartbeat_period); mi->release(); + var->type= SHOW_CHAR; + var->value= buff; } else var->type= SHOW_UNDEF; @@ -7306,8 +7299,8 @@ static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff, #endif /* HAVE_REPLICATION */ -static int show_open_tables(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_open_tables(THD *, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -7315,8 +7308,8 @@ static int show_open_tables(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_prepared_stmt_count(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_prepared_stmt_count(THD *, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -7326,8 +7319,8 @@ static int show_prepared_stmt_count(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_table_definitions(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_table_definitions(THD *, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -7336,8 +7329,8 @@ static int show_table_definitions(THD *thd, SHOW_VAR *var, char *buff, } -static int show_flush_commands(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_flush_commands(THD *, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONGLONG; var->value= buff; @@ -7356,8 +7349,8 @@ static int show_flush_commands(THD *thd, SHOW_VAR *var, char *buff, inside an Event. */ -static int show_ssl_get_version(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_ssl_get_version(THD *thd, SHOW_VAR *var, void *, + system_status_var *, enum_var_type) { var->type= SHOW_CHAR; if( thd->vio_ok() && thd->net.vio->ssl_arg ) @@ -7367,8 +7360,8 @@ static int show_ssl_get_version(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_ssl_get_default_timeout(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_ssl_get_default_timeout(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -7379,8 +7372,8 @@ static int show_ssl_get_default_timeout(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_ssl_get_verify_mode(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_ssl_get_verify_mode(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -7395,8 +7388,8 @@ static int show_ssl_get_verify_mode(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_ssl_get_verify_depth(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_ssl_get_verify_depth(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -7408,8 +7401,8 @@ static int show_ssl_get_verify_depth(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_ssl_get_cipher(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_ssl_get_cipher(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_CHAR; if( thd->vio_ok() && thd->net.vio->ssl_arg ) @@ -7419,9 +7412,10 @@ static int show_ssl_get_cipher(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, void *buf, + system_status_var *, enum_var_type) { + char *buff= static_cast(buf); var->type= SHOW_CHAR; var->value= buff; if (thd->vio_ok() && thd->net.vio->ssl_arg) @@ -7506,8 +7500,8 @@ my_asn1_time_to_string(const ASN1_TIME *time, char *buf, size_t len) */ static int -show_ssl_get_server_not_before(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +show_ssl_get_server_not_before(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_CHAR; if(thd->vio_ok() && thd->net.vio->ssl_arg) @@ -7516,7 +7510,7 @@ show_ssl_get_server_not_before(THD *thd, SHOW_VAR *var, char *buff, X509 *cert= SSL_get_certificate(ssl); const ASN1_TIME *not_before= X509_get0_notBefore(cert); - var->value= my_asn1_time_to_string(not_before, buff, + var->value= my_asn1_time_to_string(not_before, static_cast(buff), SHOW_VAR_FUNC_BUFF_SIZE); if (!var->value) return 1; @@ -7540,8 +7534,8 @@ show_ssl_get_server_not_before(THD *thd, SHOW_VAR *var, char *buff, */ static int -show_ssl_get_server_not_after(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +show_ssl_get_server_not_after(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_CHAR; if(thd->vio_ok() && thd->net.vio->ssl_arg) @@ -7550,7 +7544,7 @@ show_ssl_get_server_not_after(THD *thd, SHOW_VAR *var, char *buff, X509 *cert= SSL_get_certificate(ssl); const ASN1_TIME *not_after= X509_get0_notAfter(cert); - var->value= my_asn1_time_to_string(not_after, buff, + var->value= my_asn1_time_to_string(not_after, static_cast(buff), SHOW_VAR_FUNC_BUFF_SIZE); if (!var->value) return 1; @@ -7604,7 +7598,7 @@ static int show_default_keycache(THD *thd, SHOW_VAR *var, void *buff, } -static int show_memory_used(THD *thd, SHOW_VAR *var, char *buff, +static int show_memory_used(THD *thd, SHOW_VAR *var, void *buff, struct system_status_var *status_var, enum enum_var_type scope) { @@ -7660,8 +7654,8 @@ static int debug_status_func(THD *thd, SHOW_VAR *var, void *buff, #endif #ifdef HAVE_POOL_OF_THREADS -int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +int show_threadpool_idle_threads(THD *, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_INT; var->value= buff; @@ -7670,8 +7664,8 @@ int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff, } -static int show_threadpool_threads(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_threadpool_threads(THD *, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_INT; var->value= buff; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 6a6a6b3984dba..1949a0c02b6a8 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -11869,8 +11869,8 @@ static my_bool count_column_grants(void *grant_table, This must be performed under the mutex in order to make sure the iteration does not fail. */ -static int show_column_grants(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_column_grants(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum enum_var_type scope) { var->type= SHOW_ULONG; var->value= buff; @@ -11886,8 +11886,8 @@ static int show_column_grants(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_database_grants(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_database_grants(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum enum_var_type scope) { var->type= SHOW_UINT; var->value= buff; diff --git a/sql/table_cache.cc b/sql/table_cache.cc index 9b28e7b7b0ff5..ca7c25ae552f7 100644 --- a/sql/table_cache.cc +++ b/sql/table_cache.cc @@ -1345,8 +1345,8 @@ int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument, } -int show_tc_active_instances(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +int show_tc_active_instances(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum enum_var_type scope) { var->type= SHOW_UINT; var->value= buff; diff --git a/sql/table_cache.h b/sql/table_cache.h index 3be7b5fe413b6..ee738eb8ba6da 100644 --- a/sql/table_cache.h +++ b/sql/table_cache.h @@ -97,8 +97,8 @@ extern int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument, bool no_dups= false); extern uint tc_records(void); -int show_tc_active_instances(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope); +int show_tc_active_instances(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum enum_var_type scope); extern void tc_purge(bool mark_flushed= false); extern void tc_add_table(THD *thd, TABLE *table); extern void tc_release_table(TABLE *table); diff --git a/sql/threadpool.h b/sql/threadpool.h index ea6c93b4a65ea..e432132716316 100644 --- a/sql/threadpool.h +++ b/sql/threadpool.h @@ -64,9 +64,6 @@ extern int tp_get_thread_count(); /* Activate threadpool scheduler */ extern void tp_scheduler(void); -extern int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope); - enum TP_PRIORITY { TP_PRIORITY_HIGH, TP_PRIORITY_LOW, diff --git a/storage/sphinx/ha_sphinx.cc b/storage/sphinx/ha_sphinx.cc index f2bc24c47d4ce..4549e8dccb6de 100644 --- a/storage/sphinx/ha_sphinx.cc +++ b/storage/sphinx/ha_sphinx.cc @@ -3548,7 +3548,8 @@ CSphSEStats * sphinx_get_stats ( THD * thd, SHOW_VAR * out ) return 0; } -int sphinx_showfunc_total ( THD * thd, SHOW_VAR * out, char * ) +static int sphinx_showfunc_total ( THD * thd, SHOW_VAR * out, void *, + system_status_var *, enum_var_type ) { CSphSEStats * pStats = sphinx_get_stats ( thd, out ); if ( pStats ) @@ -3559,7 +3560,8 @@ int sphinx_showfunc_total ( THD * thd, SHOW_VAR * out, char * ) return 0; } -int sphinx_showfunc_total_found ( THD * thd, SHOW_VAR * out, char * ) +static int sphinx_showfunc_total_found ( THD * thd, SHOW_VAR * out, void *, + system_status_var *, enum_var_type ) { CSphSEStats * pStats = sphinx_get_stats ( thd, out ); if ( pStats ) @@ -3570,7 +3572,8 @@ int sphinx_showfunc_total_found ( THD * thd, SHOW_VAR * out, char * ) return 0; } -int sphinx_showfunc_time ( THD * thd, SHOW_VAR * out, char * ) +static int sphinx_showfunc_time ( THD * thd, SHOW_VAR * out, void *, + system_status_var *, enum_var_type ) { CSphSEStats * pStats = sphinx_get_stats ( thd, out ); if ( pStats ) @@ -3581,7 +3584,8 @@ int sphinx_showfunc_time ( THD * thd, SHOW_VAR * out, char * ) return 0; } -int sphinx_showfunc_word_count ( THD * thd, SHOW_VAR * out, char * ) +static int sphinx_showfunc_word_count ( THD * thd, SHOW_VAR * out, void *, + system_status_var *, enum_var_type ) { CSphSEStats * pStats = sphinx_get_stats ( thd, out ); if ( pStats ) @@ -3592,9 +3596,11 @@ int sphinx_showfunc_word_count ( THD * thd, SHOW_VAR * out, char * ) return 0; } -int sphinx_showfunc_words ( THD * thd, SHOW_VAR * out, char * sBuffer ) +static int sphinx_showfunc_words ( THD * thd, SHOW_VAR * out, void * buf, + system_status_var *, enum_var_type ) { #if MYSQL_VERSION_ID>50100 + char *sBuffer = static_cast(buf); if ( sphinx_hton_ptr ) { CSphTLS * pTls = (CSphTLS *) *thd_ha_data ( thd, sphinx_hton_ptr ); @@ -3649,7 +3655,8 @@ int sphinx_showfunc_words ( THD * thd, SHOW_VAR * out, char * sBuffer ) return 0; } -int sphinx_showfunc_error ( THD * thd, SHOW_VAR * out, char * ) +static int sphinx_showfunc_error ( THD * thd, SHOW_VAR * out, void *, + system_status_var *, enum_var_type ) { CSphSEStats * pStats = sphinx_get_stats ( thd, out ); out->type = SHOW_CHAR; diff --git a/storage/sphinx/ha_sphinx.h b/storage/sphinx/ha_sphinx.h index decd88bad5a84..ddc1567328f92 100644 --- a/storage/sphinx/ha_sphinx.h +++ b/storage/sphinx/ha_sphinx.h @@ -164,12 +164,6 @@ class ha_sphinx : public handler bool sphinx_show_status ( THD * thd ); #endif -int sphinx_showfunc_total_found ( THD *, SHOW_VAR *, char * ); -int sphinx_showfunc_total ( THD *, SHOW_VAR *, char * ); -int sphinx_showfunc_time ( THD *, SHOW_VAR *, char * ); -int sphinx_showfunc_word_count ( THD *, SHOW_VAR *, char * ); -int sphinx_showfunc_words ( THD *, SHOW_VAR *, char * ); - // // $Id: ha_sphinx.h 4818 2014-09-24 08:53:38Z tomat $ // diff --git a/storage/spider/spd_param.cc b/storage/spider/spd_param.cc index 5eaf95180488e..5c86af68c0a85 100644 --- a/storage/spider/spd_param.cc +++ b/storage/spider/spd_param.cc @@ -115,7 +115,8 @@ extern volatile ulonglong spider_mon_table_cache_version_req; } #ifdef HANDLER_HAS_DIRECT_UPDATE_ROWS -static int spider_direct_update(THD *thd, SHOW_VAR *var, char *buff) +static int spider_direct_update(THD *thd, SHOW_VAR *var, void *, + system_status_var *, enum_var_type) { int error_num = 0; SPIDER_TRX *trx; @@ -126,7 +127,8 @@ static int spider_direct_update(THD *thd, SHOW_VAR *var, char *buff) DBUG_RETURN(error_num); } -static int spider_direct_delete(THD *thd, SHOW_VAR *var, char *buff) +static int spider_direct_delete(THD *thd, SHOW_VAR *var, void *, + system_status_var *, enum_var_type) { int error_num = 0; SPIDER_TRX *trx; @@ -138,7 +140,8 @@ static int spider_direct_delete(THD *thd, SHOW_VAR *var, char *buff) } #endif -static int spider_direct_order_limit(THD *thd, SHOW_VAR *var, char *buff) +static int spider_direct_order_limit(THD *thd, SHOW_VAR *var, void *, + system_status_var *, enum_var_type) { int error_num = 0; SPIDER_TRX *trx; @@ -149,7 +152,8 @@ static int spider_direct_order_limit(THD *thd, SHOW_VAR *var, char *buff) DBUG_RETURN(error_num); } -static int spider_direct_aggregate(THD *thd, SHOW_VAR *var, char *buff) +static int spider_direct_aggregate(THD *thd, SHOW_VAR *var, void *, + system_status_var *, enum_var_type) { int error_num = 0; SPIDER_TRX *trx; @@ -160,7 +164,8 @@ static int spider_direct_aggregate(THD *thd, SHOW_VAR *var, char *buff) DBUG_RETURN(error_num); } -static int spider_parallel_search(THD *thd, SHOW_VAR *var, char *buff) +static int spider_parallel_search(THD *thd, SHOW_VAR *var, void *, + system_status_var *, enum_var_type) { int error_num = 0; SPIDER_TRX *trx; @@ -172,7 +177,8 @@ static int spider_parallel_search(THD *thd, SHOW_VAR *var, char *buff) } #if defined(HS_HAS_SQLCOM) && defined(HAVE_HANDLERSOCKET) -static int spider_hs_result_free(THD *thd, SHOW_VAR *var, char *buff) +static int spider_hs_result_free(THD *thd, SHOW_VAR *var, void *, + system_status_var *, enum_var_type) { int error_num = 0; SPIDER_TRX *trx; From ac0ce4451958a5c95c9b2ede242586c600836f81 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 3 Jan 2024 12:04:50 +0100 Subject: [PATCH 003/129] ./mtr --skip-not-found should skip combinations too With the result like encryption.innochecksum 'debug' [ skipped ] combination not found instead of *** ERROR: Could not run encryption.innochecksum with 'debug' combination(s) --- mysql-test/lib/mtr_cases.pm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mysql-test/lib/mtr_cases.pm b/mysql-test/lib/mtr_cases.pm index a4473a07e814d..666ac74791e0f 100644 --- a/mysql-test/lib/mtr_cases.pm +++ b/mysql-test/lib/mtr_cases.pm @@ -892,6 +892,12 @@ sub collect_one_test_case { } my @no_combs = grep { $test_combs{$_} == 1 } keys %test_combs; if (@no_combs) { + if ($::opt_skip_not_found) { + push @{$tinfo->{combinations}}, @no_combs; + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "combination not found"; + return $tinfo; + } mtr_error("Could not run $name with '".( join(',', sort @no_combs))."' combination(s)"); } From 8172d07785245ad129dfaa9fecca4da206fd3e07 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 3 Jan 2024 18:36:36 +0100 Subject: [PATCH 004/129] MDEV-33090 plugin/auth_pam/testing/pam_mariadb_mtr.c doesn't compile on Solaris Fix by Rainer Orth --- plugin/auth_pam/testing/pam_mariadb_mtr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/auth_pam/testing/pam_mariadb_mtr.c b/plugin/auth_pam/testing/pam_mariadb_mtr.c index d4c79f3133065..dc2bc150dc9c7 100644 --- a/plugin/auth_pam/testing/pam_mariadb_mtr.c +++ b/plugin/auth_pam/testing/pam_mariadb_mtr.c @@ -10,8 +10,8 @@ #include #include -#include #include +#include #define N 3 From f7573e7a837af75768b518936f99c4df634e1b79 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 3 Jan 2024 18:55:16 +0100 Subject: [PATCH 005/129] MDEV-33093 plugin/disks/information_schema_disks.cc doesn't compile on Solaris Fix by Rainer Orth --- plugin/disks/information_schema_disks.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugin/disks/information_schema_disks.cc b/plugin/disks/information_schema_disks.cc index 01c6c0e173feb..1be8fce37fa92 100644 --- a/plugin/disks/information_schema_disks.cc +++ b/plugin/disks/information_schema_disks.cc @@ -19,10 +19,14 @@ #include #if defined(HAVE_GETMNTENT) #include +#elif defined(HAVE_SYS_MNTENT) +#include #elif !defined(HAVE_GETMNTINFO_TAKES_statvfs) /* getmntinfo (the not NetBSD variants) */ #include +#if defined(HAVE_SYS_UCRED) #include +#endif #include #endif #if defined(HAVE_GETMNTENT_IN_SYS_MNTAB) From ca276a0f3fcb45ff0abc011e334c700e0c5d4315 Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Fri, 5 Jan 2024 09:35:57 +1100 Subject: [PATCH 006/129] MDEV-33169 Reset sequence used fields after check in alter sequence The bitmap is temporarily flipped to ~0 for the sake of checking all fields. It needs to be restored because it will be reused in second and subsequent ps execution. --- mysql-test/suite/sql_sequence/alter.result | 49 ++++++++++++++++++++++ mysql-test/suite/sql_sequence/alter.test | 35 ++++++++++++++++ sql/sql_sequence.cc | 2 + 3 files changed, 86 insertions(+) diff --git a/mysql-test/suite/sql_sequence/alter.result b/mysql-test/suite/sql_sequence/alter.result index 68d42e5278479..d4e930ebb9244 100644 --- a/mysql-test/suite/sql_sequence/alter.result +++ b/mysql-test/suite/sql_sequence/alter.result @@ -252,3 +252,52 @@ SELECT NEXTVAL(s); NEXTVAL(s) 1 DROP SEQUENCE s; +# +# MDEV-33169 Alter sequence 2nd ps fails while alter sequence 2nd time (no ps) succeeds +# +create sequence s; +show create sequence s; +Table Create Table +s CREATE SEQUENCE `s` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +alter sequence s maxvalue 123; +show create sequence s; +Table Create Table +s CREATE SEQUENCE `s` start with 1 minvalue 1 maxvalue 123 increment by 1 cache 1000 nocycle ENGINE=MyISAM +alter sequence s maxvalue 123; +show create sequence s; +Table Create Table +s CREATE SEQUENCE `s` start with 1 minvalue 1 maxvalue 123 increment by 1 cache 1000 nocycle ENGINE=MyISAM +drop sequence s; +create sequence s; +show create sequence s; +Table Create Table +s CREATE SEQUENCE `s` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +prepare stmt from 'alter sequence s maxvalue 123'; +execute stmt; +show create sequence s; +Table Create Table +s CREATE SEQUENCE `s` start with 1 minvalue 1 maxvalue 123 increment by 1 cache 1000 nocycle ENGINE=MyISAM +execute stmt; +show create sequence s; +Table Create Table +s CREATE SEQUENCE `s` start with 1 minvalue 1 maxvalue 123 increment by 1 cache 1000 nocycle ENGINE=MyISAM +deallocate prepare stmt; +drop sequence s; +create sequence s; +show create sequence s; +Table Create Table +s CREATE SEQUENCE `s` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=MyISAM +create procedure p() alter sequence s maxvalue 123; +call p; +show create sequence s; +Table Create Table +s CREATE SEQUENCE `s` start with 1 minvalue 1 maxvalue 123 increment by 1 cache 1000 nocycle ENGINE=MyISAM +call p; +show create sequence s; +Table Create Table +s CREATE SEQUENCE `s` start with 1 minvalue 1 maxvalue 123 increment by 1 cache 1000 nocycle ENGINE=MyISAM +drop procedure p; +drop sequence s; +# +# End of 10.4 tests +# diff --git a/mysql-test/suite/sql_sequence/alter.test b/mysql-test/suite/sql_sequence/alter.test index a771c9bba2f78..015aba22af6e4 100644 --- a/mysql-test/suite/sql_sequence/alter.test +++ b/mysql-test/suite/sql_sequence/alter.test @@ -167,3 +167,38 @@ ALTER TABLE s ORDER BY cache_size; SELECT NEXTVAL(s); DROP SEQUENCE s; --enable_ps2_protocol + +--echo # +--echo # MDEV-33169 Alter sequence 2nd ps fails while alter sequence 2nd time (no ps) succeeds +--echo # +create sequence s; +show create sequence s; +alter sequence s maxvalue 123; +show create sequence s; +alter sequence s maxvalue 123; +show create sequence s; +drop sequence s; + +create sequence s; +show create sequence s; +prepare stmt from 'alter sequence s maxvalue 123'; +execute stmt; +show create sequence s; +execute stmt; +show create sequence s; +deallocate prepare stmt; +drop sequence s; + +create sequence s; +show create sequence s; +create procedure p() alter sequence s maxvalue 123; +call p; +show create sequence s; +call p; +show create sequence s; +drop procedure p; +drop sequence s; + +--echo # +--echo # End of 10.4 tests +--echo # diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc index ab77eabfa2832..405b2d5b003ba 100644 --- a/sql/sql_sequence.cc +++ b/sql/sql_sequence.cc @@ -924,6 +924,7 @@ bool Sql_cmd_alter_sequence::execute(THD *thd) TABLE_LIST *first_table= lex->query_tables; TABLE *table; sequence_definition *new_seq= lex->create_info.seq_create_info; + uint saved_used_fields= new_seq->used_fields; SEQUENCE *seq; No_such_table_error_handler no_such_table_handler; DBUG_ENTER("Sql_cmd_alter_sequence::execute"); @@ -1043,5 +1044,6 @@ bool Sql_cmd_alter_sequence::execute(THD *thd) my_ok(thd); end: + new_seq->used_fields= saved_used_fields; DBUG_RETURN(error); } From 9322ef03e339ee8fcea25231c73c2f63d56c0d49 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 13 Nov 2023 11:18:16 +0400 Subject: [PATCH 007/129] MDEV-32645 CAST(AS UNSIGNED) fails with --view-protocol Item_float::neg() did not preserve the "presentation" from "this". So CAST(-1e0 AS UNSIGNED) -- cast from double to unsigned changes its meaning to: CAST(-1 AS UNSIGNED) -- cast signed to undigned Fixing Item_float::neg() to construct the new value for Item_float::presentation as follows: - if the old value starts with minus, then the minus is truncated: '-2e0' -> '2e0' - otherwise, minus sign followed by its old value: '1e0' -> '-1e0' --- mysql-test/main/cast.test | 3 --- mysql-test/main/type_float.result | 37 +++++++++++++++++++++++++++++++ mysql-test/main/type_float.test | 26 ++++++++++++++++++++++ sql/item.cc | 20 ++++++++++++++++- 4 files changed, 82 insertions(+), 4 deletions(-) diff --git a/mysql-test/main/cast.test b/mysql-test/main/cast.test index ce4b1f6a5749e..5a0f87beb696d 100644 --- a/mysql-test/main/cast.test +++ b/mysql-test/main/cast.test @@ -768,14 +768,11 @@ INSERT INTO t1 VALUES (-1.0); SELECT * FROM t1; DROP TABLE t1; -#enable after MDEV-32645 is fixed ---disable_view_protocol SELECT CAST(-1e0 AS UNSIGNED); CREATE TABLE t1 (a BIGINT UNSIGNED); INSERT INTO t1 VALUES (-1e0); SELECT * FROM t1; DROP TABLE t1; ---enable_view_protocol SELECT CAST(-1e308 AS UNSIGNED); CREATE TABLE t1 (a BIGINT UNSIGNED); diff --git a/mysql-test/main/type_float.result b/mysql-test/main/type_float.result index 500f906642ddd..fa850cf9b347c 100644 --- a/mysql-test/main/type_float.result +++ b/mysql-test/main/type_float.result @@ -1167,5 +1167,42 @@ d 50 fdbl 123.456.789,12345678000000000000000000000000000000 fdec 123.456.789,12345678900000000000000000000000000000 # +# MDEV-32645 CAST(AS UNSIGNED) fails with --view-protocol +# +SELECT +CAST(-1e0 AS UNSIGNED), +CAST(--2e0 AS UNSIGNED), +CAST(---3e0 AS UNSIGNED), +CAST(----4e0 AS UNSIGNED); +CAST(-1e0 AS UNSIGNED) CAST(--2e0 AS UNSIGNED) CAST(---3e0 AS UNSIGNED) CAST(----4e0 AS UNSIGNED) +0 2 0 4 +Warnings: +Note 1916 Got overflow when converting '-1' to UNSIGNED BIGINT. Value truncated +Note 1916 Got overflow when converting '-3' to UNSIGNED BIGINT. Value truncated +EXPLAIN EXTENDED SELECT +CAST(-1e0 AS UNSIGNED), +CAST(--2e0 AS UNSIGNED), +CAST(---3e0 AS UNSIGNED), +CAST(----4e0 AS UNSIGNED); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select cast(-1e0 as unsigned) AS `CAST(-1e0 AS UNSIGNED)`,cast(2e0 as unsigned) AS `CAST(--2e0 AS UNSIGNED)`,cast(-3e0 as unsigned) AS `CAST(---3e0 AS UNSIGNED)`,cast(4e0 as unsigned) AS `CAST(----4e0 AS UNSIGNED)` +CREATE VIEW v1 AS SELECT +CAST(-1e0 AS UNSIGNED), +CAST(--2e0 AS UNSIGNED), +CAST(---3e0 AS UNSIGNED), +CAST(----4e0 AS UNSIGNED); +SHOW CREATE VIEW v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select cast(-1e0 as unsigned) AS `CAST(-1e0 AS UNSIGNED)`,cast(2e0 as unsigned) AS `CAST(--2e0 AS UNSIGNED)`,cast(-3e0 as unsigned) AS `CAST(---3e0 AS UNSIGNED)`,cast(4e0 as unsigned) AS `CAST(----4e0 AS UNSIGNED)` latin1 latin1_swedish_ci +SELECT * FROM v1; +CAST(-1e0 AS UNSIGNED) CAST(--2e0 AS UNSIGNED) CAST(---3e0 AS UNSIGNED) CAST(----4e0 AS UNSIGNED) +0 2 0 4 +Warnings: +Note 1916 Got overflow when converting '-1' to UNSIGNED BIGINT. Value truncated +Note 1916 Got overflow when converting '-3' to UNSIGNED BIGINT. Value truncated +DROP VIEW v1; +# # End of 10.4 tests # diff --git a/mysql-test/main/type_float.test b/mysql-test/main/type_float.test index f1041080e268c..f1d74f2f8d714 100644 --- a/mysql-test/main/type_float.test +++ b/mysql-test/main/type_float.test @@ -713,6 +713,32 @@ $$ DELIMITER ;$$ --horizontal_results +--echo # +--echo # MDEV-32645 CAST(AS UNSIGNED) fails with --view-protocol +--echo # + +SELECT + CAST(-1e0 AS UNSIGNED), + CAST(--2e0 AS UNSIGNED), + CAST(---3e0 AS UNSIGNED), + CAST(----4e0 AS UNSIGNED); + +EXPLAIN EXTENDED SELECT + CAST(-1e0 AS UNSIGNED), + CAST(--2e0 AS UNSIGNED), + CAST(---3e0 AS UNSIGNED), + CAST(----4e0 AS UNSIGNED); + +CREATE VIEW v1 AS SELECT + CAST(-1e0 AS UNSIGNED), + CAST(--2e0 AS UNSIGNED), + CAST(---3e0 AS UNSIGNED), + CAST(----4e0 AS UNSIGNED); + +SHOW CREATE VIEW v1; +SELECT * FROM v1; +DROP VIEW v1; + --echo # --echo # End of 10.4 tests --echo # diff --git a/sql/item.cc b/sql/item.cc index 21190b38e1aa8..6d30d63bc1167 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6896,7 +6896,25 @@ Item *Item_float::neg(THD *thd) else if (value < 0 && max_length) max_length--; value= -value; - presentation= 0; + if (presentation) + { + if (*presentation == '-') + { + // Strip double minus: -(-1) -> '1' instead of '--1' + presentation++; + } + else + { + size_t presentation_length= strlen(presentation); + if (char *tmp= (char*) thd->alloc(presentation_length + 2)) + { + tmp[0]= '-'; + // Copy with the trailing '\0' + memcpy(tmp + 1, presentation, presentation_length + 1); + presentation= tmp; + } + } + } name= null_clex_str; return this; } From c6e1ffd1a07fc451e7211b0d00edbace78137276 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 31 Dec 2023 23:30:48 +0100 Subject: [PATCH 008/129] MDEV-33148 A connection can control RAND() in following connection initialize THD::rand in THD::init() not in THD::THD(), because the former is also called when a THD is reused - in COM_CHANGE_USER and in taking a THD from the cache. Also use current cycle timer for more unpreditability --- sql/sql_class.cc | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b4893581e1aca..179e2a1a9a573 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -684,7 +684,6 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) wsrep_wfc() #endif /*WITH_WSREP */ { - ulong tmp; bzero(&variables, sizeof(variables)); /* @@ -834,14 +833,6 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) tablespace_op=FALSE; - /* - Initialize the random generator. We call my_rnd() without a lock as - it's not really critical if two threads modifies the structure at the - same time. We ensure that we have an unique number foreach thread - by adding the address of the stack. - */ - tmp= (ulong) (my_rnd(&sql_rand) * 0xffffffff); - my_rnd_init(&rand, tmp + (ulong)((size_t) &rand), tmp + (ulong) ::global_query_id); substitute_null_with_insert_id = FALSE; lock_info.mysql_thd= (void *)this; @@ -1297,6 +1288,17 @@ void THD::init() /* Set to handle counting of aborted connections */ userstat_running= opt_userstat_running; last_global_update_time= current_connect_time= time(NULL); + + /* + Initialize the random generator. We call my_rnd() without a lock as + it's not really critical if two threads modify the structure at the + same time. We ensure that we have a unique number for each thread + by adding the address of this THD. + */ + ulong tmp= (ulong) (my_rnd(&sql_rand) * 0xffffffff); + my_rnd_init(&rand, tmp + (ulong)(intptr) this, + (ulong)(my_timer_cycles() + global_query_id)); + #if defined(ENABLED_DEBUG_SYNC) /* Initialize the Debug Sync Facility. See debug_sync.cc. */ debug_sync_init_thread(this); From 2310f659f528a189bb9c2a8e70a63e7ee7702780 Mon Sep 17 00:00:00 2001 From: Rainer Orth Date: Wed, 10 Jan 2024 10:11:43 +1100 Subject: [PATCH 009/129] MDEV-8941 Compile on Solaris (SPARC) fails with errors in filamvct.cpp There are a large number of uses of `strerror` in the codebase, the local declaration in `storage/connect/tabvct.cpp` is the only one. Given that none is needed elsewhere, I conclude that this instance can simply be removed. --- storage/connect/tabvct.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/storage/connect/tabvct.cpp b/storage/connect/tabvct.cpp index 9cf5f41d1feec..f5710688d3c5c 100644 --- a/storage/connect/tabvct.cpp +++ b/storage/connect/tabvct.cpp @@ -71,11 +71,6 @@ #include "tabvct.h" #include "valblk.h" -#if defined(UNIX) -//add dummy strerror (NGC) -char *strerror(int num); -#endif // UNIX - /***********************************************************************/ /* External function. */ /***********************************************************************/ From eabc74aaef472cb415f4ead502559ca1c27efbc8 Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Wed, 10 Jan 2024 16:36:39 +1100 Subject: [PATCH 010/129] MDEV-33008 Fix spider table discovery A new column was introduced to the show index output in 10.6 in f691d9865becfd242ba44cc632034433336af1e7 Thus we update the check of the number of columns to be at least 13, rather than exactly 13. Also backport an err number and format from 10.5 for better error messages when the column number is wrong. --- .../spider/bugfix/r/mdev_33008.result | 25 +++++++++++++++++++ .../spider/bugfix/t/mdev_33008.test | 24 ++++++++++++++++++ storage/spider/spd_db_mysql.cc | 9 ++++--- storage/spider/spd_err.h | 2 ++ 4 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 storage/spider/mysql-test/spider/bugfix/r/mdev_33008.result create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_33008.test diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_33008.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_33008.result new file mode 100644 index 0000000000000..3bcb4bb038ab8 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_33008.result @@ -0,0 +1,25 @@ +for master_1 +for child2 +for child3 +set spider_same_server_link=on; +CREATE SERVER srv FOREIGN DATA WRAPPER mysql +OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); +create table t2 ( +`id` int(11) NOT NULL AUTO_INCREMENT, +`code` varchar(10) DEFAULT NULL, +PRIMARY KEY (`id`) +); +create table t1 ENGINE=Spider +COMMENT='WRAPPER "mysql", srv "srv",TABLE "t2"'; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `code` varchar(10) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=SPIDER DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci COMMENT='WRAPPER "mysql", srv "srv",TABLE "t2"' +drop table t1, t2; +drop server srv; +for master_1 +for child2 +for child3 diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_33008.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_33008.test new file mode 100644 index 0000000000000..48d9a4f01ec72 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_33008.test @@ -0,0 +1,24 @@ +--disable_query_log +--disable_result_log +--source ../../t/test_init.inc +--enable_result_log +--enable_query_log +set spider_same_server_link=on; +evalp CREATE SERVER srv FOREIGN DATA WRAPPER mysql +OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); +create table t2 ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `code` varchar(10) DEFAULT NULL, + PRIMARY KEY (`id`) +); +create table t1 ENGINE=Spider +COMMENT='WRAPPER "mysql", srv "srv",TABLE "t2"'; +show create table t1; +drop table t1, t2; + +drop server srv; +--disable_query_log +--disable_result_log +--source ../../t/test_deinit.inc +--enable_result_log +--enable_query_log diff --git a/storage/spider/spd_db_mysql.cc b/storage/spider/spd_db_mysql.cc index e689c9d93f04b..d2e9dc1f0ee89 100644 --- a/storage/spider/spd_db_mysql.cc +++ b/storage/spider/spd_db_mysql.cc @@ -1566,10 +1566,13 @@ int spider_db_mbase_result::fetch_index_for_discover_table_structure( } DBUG_RETURN(0); } - if (num_fields() != 13) + if (num_fields() < 13) { - DBUG_PRINT("info",("spider num_fields != 13")); - my_printf_error(ER_SPIDER_UNKNOWN_NUM, ER_SPIDER_UNKNOWN_STR, MYF(0)); + DBUG_PRINT("info",("spider num_fields < 13")); + my_printf_error(ER_SPIDER_CANT_NUM, ER_SPIDER_CANT_STR1, MYF(0), + "fetch index for table structure discovery because of " + "wrong number of columns in SHOW INDEX FROM output: ", + num_fields()); DBUG_RETURN(ER_SPIDER_UNKNOWN_NUM); } bool first = TRUE; diff --git a/storage/spider/spd_err.h b/storage/spider/spd_err.h index 9889fcfa7fb5c..d8f11e18f26ea 100644 --- a/storage/spider/spd_err.h +++ b/storage/spider/spd_err.h @@ -127,6 +127,8 @@ #define ER_SPIDER_SAME_SERVER_LINK_NUM 12720 #define ER_SPIDER_SAME_SERVER_LINK_STR1 "Host:%s and Socket:%s aim self server. Please change spider_same_server_link parameter if this link is required." #define ER_SPIDER_SAME_SERVER_LINK_STR2 "Host:%s and Port:%ld aim self server. Please change spider_same_server_link parameter if this link is required." +#define ER_SPIDER_CANT_NUM 12721 +#define ER_SPIDER_CANT_STR1 "Can't %s%d" #define ER_SPIDER_COND_SKIP_NUM 12801 #define ER_SPIDER_UNKNOWN_NUM 12500 From bc3d416a17c9d35382f2db6387e51619e80c59da Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Wed, 10 Jan 2024 16:37:36 +1100 Subject: [PATCH 011/129] MDEV-29718 Fix spider detection of same data node server When the host is not specified, it defaults to localhost. --- storage/spider/mysql-test/spider/bugfix/r/mdev_26151.result | 4 ++++ .../mysql-test/spider/bugfix/r/mdev_28739_simple.result | 1 + storage/spider/mysql-test/spider/bugfix/r/mdev_28856.result | 1 + storage/spider/mysql-test/spider/bugfix/r/mdev_28998.result | 1 + storage/spider/mysql-test/spider/bugfix/r/mdev_29163.result | 1 + storage/spider/mysql-test/spider/bugfix/r/mdev_29456.result | 1 + storage/spider/mysql-test/spider/bugfix/r/mdev_29963.result | 1 + storage/spider/mysql-test/spider/bugfix/r/mdev_30014.result | 1 + storage/spider/mysql-test/spider/bugfix/r/mdev_30392.result | 1 + storage/spider/mysql-test/spider/bugfix/r/mdev_31338.result | 1 + storage/spider/mysql-test/spider/bugfix/r/mdev_31524.result | 1 + storage/spider/mysql-test/spider/bugfix/r/mdev_31645.result | 1 + storage/spider/mysql-test/spider/bugfix/r/mdev_31996.result | 1 + storage/spider/mysql-test/spider/bugfix/r/mdev_32986.result | 1 + .../spider/bugfix/r/spider_join_with_non_spider.result | 1 + storage/spider/mysql-test/spider/bugfix/r/subquery.result | 1 + storage/spider/mysql-test/spider/bugfix/t/mdev_26151.test | 5 +++++ .../spider/mysql-test/spider/bugfix/t/mdev_28739_simple.test | 1 + storage/spider/mysql-test/spider/bugfix/t/mdev_28856.test | 1 + storage/spider/mysql-test/spider/bugfix/t/mdev_28998.test | 1 + storage/spider/mysql-test/spider/bugfix/t/mdev_29163.test | 1 + storage/spider/mysql-test/spider/bugfix/t/mdev_29456.test | 1 + storage/spider/mysql-test/spider/bugfix/t/mdev_29963.test | 1 + storage/spider/mysql-test/spider/bugfix/t/mdev_30014.test | 1 + storage/spider/mysql-test/spider/bugfix/t/mdev_30392.test | 1 + storage/spider/mysql-test/spider/bugfix/t/mdev_31338.test | 1 + storage/spider/mysql-test/spider/bugfix/t/mdev_31524.test | 1 + storage/spider/mysql-test/spider/bugfix/t/mdev_31645.test | 1 + storage/spider/mysql-test/spider/bugfix/t/mdev_31996.test | 1 + storage/spider/mysql-test/spider/bugfix/t/mdev_32986.test | 1 + .../spider/bugfix/t/spider_join_with_non_spider.test | 1 + storage/spider/mysql-test/spider/bugfix/t/subquery.test | 1 + storage/spider/spd_db_mysql.cc | 4 ++-- 33 files changed, 41 insertions(+), 2 deletions(-) diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_26151.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_26151.result index b0a430e0e212e..326b84a030e4b 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_26151.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_26151.result @@ -6,6 +6,9 @@ for child2 for child3 set @old_spider_bgs_mode= @@spider_bgs_mode; set session spider_bgs_mode=1; +set spider_same_server_link=1; +set @old_spider_same_server_link=@@global.spider_same_server_link; +set global spider_same_server_link=1; CREATE SERVER $srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); create table td (a int, PRIMARY KEY (a)); create table ts (a int, PRIMARY KEY (a)) ENGINE=Spider COMMENT='WRAPPER "mysql", srv "srv_mdev_26151",TABLE "td", casual_read "3"'; @@ -26,6 +29,7 @@ min(a) drop table td, ts; drop server srv_mdev_26151; set session spider_bgs_mode=@old_spider_bgs_mode; +set global spider_same_server_link=@old_spider_same_server_link; for master_1 for child2 for child3 diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_28739_simple.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_28739_simple.result index db232f8a6d373..1c337c3d2dd4c 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_28739_simple.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_28739_simple.result @@ -5,6 +5,7 @@ for master_1 for child2 for child3 set global query_cache_type= on; +set spider_same_server_link=1; CREATE SERVER srv FOREIGN DATA WRAPPER mysql OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); create table t2 (c int); diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_28856.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_28856.result index fae3cc6b6ce76..7e4fd3cd08466 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_28856.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_28856.result @@ -4,6 +4,7 @@ for master_1 for child2 for child3 +set spider_same_server_link=1; CREATE SERVER srv FOREIGN DATA WRAPPER mysql OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); # testing monitoring_* diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_28998.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_28998.result index edac80d97616e..e92fb19957567 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_28998.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_28998.result @@ -4,6 +4,7 @@ for master_1 for child2 for child3 +set spider_same_server_link=1; CREATE SERVER s FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); CREATE TABLE t1 (a INT); INSERT INTO t1 VALUES (1),(2); diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_29163.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_29163.result index af4bef1efa987..f58ab605e111c 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_29163.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_29163.result @@ -4,6 +4,7 @@ for master_1 for child2 for child3 +set spider_same_server_link=1; CREATE SERVER s FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); CREATE TABLE t1 (a INT); CREATE TABLE t2 (b INT); diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_29456.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_29456.result index 4d9095830d1e5..365c3d6373ad3 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_29456.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_29456.result @@ -4,6 +4,7 @@ for master_1 for child2 for child3 +set spider_same_server_link=1; CREATE SERVER srv FOREIGN DATA WRAPPER mysql OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); create table t1 (c int); diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_29963.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_29963.result index 17390346a9934..604515964f3fb 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_29963.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_29963.result @@ -4,6 +4,7 @@ for master_1 for child2 for child3 +set spider_same_server_link=1; CREATE SERVER srv FOREIGN DATA WRAPPER mysql OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); CREATE TABLE t (a INT) ENGINE=Spider; diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_30014.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_30014.result index e1dca495047c7..ee53009e3f24d 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_30014.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_30014.result @@ -4,6 +4,7 @@ for master_1 for child2 for child3 +set spider_same_server_link=1; CREATE SERVER srv FOREIGN DATA WRAPPER mysql OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); create table t1 (c int); diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_30392.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_30392.result index 58873d2c6e55c..cefa5248d44f7 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_30392.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_30392.result @@ -4,6 +4,7 @@ for master_1 for child2 for child3 +set spider_same_server_link=1; CREATE SERVER srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); CREATE TABLE t1 (a INT); INSERT INTO t1 VALUES (1),(2); diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_31338.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_31338.result index 62b06336ff6b5..f156cf38a15c2 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_31338.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_31338.result @@ -4,6 +4,7 @@ for master_1 for child2 for child3 +set spider_same_server_link=1; CREATE SERVER srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); CREATE TABLE t (c BLOB) ENGINE=InnoDB; CREATE TABLE ts (c BLOB) ENGINE=Spider COMMENT='WRAPPER "mysql",srv "srv",TABLE "t"'; diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_31524.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_31524.result index 645fc62862d94..b3a4c7525652c 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_31524.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_31524.result @@ -5,6 +5,7 @@ for master_1 for child2 for child3 SET @old_spider_read_only_mode = @@session.spider_read_only_mode; +set spider_same_server_link=1; CREATE SERVER $srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); set session spider_read_only_mode = default; create table t2 (c int); diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_31645.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_31645.result index 94b76de7df477..5197abd3fb6dc 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_31645.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_31645.result @@ -4,6 +4,7 @@ for master_1 for child2 for child3 +set spider_same_server_link=1; CREATE SERVER srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); CREATE TABLE t1 ( a bigint(20) NOT NULL, b bigint(20) DEFAULT 0, PRIMARY KEY (a)); CREATE TABLE t2 ( a bigint(20) NOT NULL, b bigint(20) DEFAULT 0, PRIMARY KEY (a)) ENGINE=SPIDER COMMENT='srv "srv", WRAPPER "mysql", TABLE "t1"'; diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_31996.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_31996.result index 04d7e88467635..cbc91432dbc3b 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_31996.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_31996.result @@ -1,6 +1,7 @@ for master_1 for child2 for child3 +set spider_same_server_link=1; CREATE SERVER srv FOREIGN DATA WRAPPER mysql OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); set session spider_delete_all_rows_type=0; diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_32986.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_32986.result index bdc580d421ac4..c3bdef9870d92 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_32986.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_32986.result @@ -4,6 +4,7 @@ for master_1 for child2 for child3 +set spider_same_server_link=1; CREATE SERVER srv FOREIGN DATA WRAPPER mysql OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); create table t2 (c varchar(16)); diff --git a/storage/spider/mysql-test/spider/bugfix/r/spider_join_with_non_spider.result b/storage/spider/mysql-test/spider/bugfix/r/spider_join_with_non_spider.result index b9c1c5c9de550..420ca657a339d 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/spider_join_with_non_spider.result +++ b/storage/spider/mysql-test/spider/bugfix/r/spider_join_with_non_spider.result @@ -4,6 +4,7 @@ for master_1 for child2 for child3 +set spider_same_server_link=1; CREATE SERVER srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); create table t1 (c int); create table t2 (d int); diff --git a/storage/spider/mysql-test/spider/bugfix/r/subquery.result b/storage/spider/mysql-test/spider/bugfix/r/subquery.result index c6ea45e2dc0a4..280f57155bdb8 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/subquery.result +++ b/storage/spider/mysql-test/spider/bugfix/r/subquery.result @@ -4,6 +4,7 @@ for master_1 for child2 for child3 +set spider_same_server_link=1; CREATE SERVER srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); create table t1 (c1 int); create table t2 (c2 int); diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_26151.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_26151.test index f9e157d33e60d..dcf1438fb8088 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_26151.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_26151.test @@ -14,6 +14,10 @@ --let $srv=srv_mdev_26151 set @old_spider_bgs_mode= @@spider_bgs_mode; set session spider_bgs_mode=1; +set spider_same_server_link=1; +set @old_spider_same_server_link=@@global.spider_same_server_link; +set global spider_same_server_link=1; + evalp CREATE SERVER $srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); # casual_read != 0 && casual_read != 1 @@ -42,6 +46,7 @@ drop table td, ts; eval drop server $srv; set session spider_bgs_mode=@old_spider_bgs_mode; +set global spider_same_server_link=@old_spider_same_server_link; --disable_query_log --disable_result_log diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_28739_simple.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_28739_simple.test index 7a011520bb6e7..feff85df6b37e 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_28739_simple.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_28739_simple.test @@ -12,6 +12,7 @@ #set @@global.debug_dbug="d:t:i:o,mysqld.trace"; set global query_cache_type= on; +set spider_same_server_link=1; evalp CREATE SERVER srv FOREIGN DATA WRAPPER mysql OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); create table t2 (c int); diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_28856.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_28856.test index 4f23168ec1b57..a1642f7a9cdbe 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_28856.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_28856.test @@ -9,6 +9,7 @@ # This test covers some table params under consideration for inclusion # in the engine-defined options to be implemented in MDEV-28856. +set spider_same_server_link=1; evalp CREATE SERVER srv FOREIGN DATA WRAPPER mysql OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_28998.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_28998.test index e1735706341c6..66e3a15addbd1 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_28998.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_28998.test @@ -12,6 +12,7 @@ if (`select not(count(*)) from information_schema.system_variables where variabl --source ../../t/test_init.inc --enable_result_log --enable_query_log +set spider_same_server_link=1; evalp CREATE SERVER s FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); CREATE TABLE t1 (a INT); diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_29163.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_29163.test index ac116fd0e4fef..2e56583d8311a 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_29163.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_29163.test @@ -6,6 +6,7 @@ --source ../../t/test_init.inc --enable_result_log --enable_query_log +set spider_same_server_link=1; evalp CREATE SERVER s FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); CREATE TABLE t1 (a INT); diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_29456.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_29456.test index 16291f82075f2..89d53227c6c5d 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_29456.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_29456.test @@ -8,6 +8,7 @@ --enable_result_log --enable_query_log +set spider_same_server_link=1; evalp CREATE SERVER srv FOREIGN DATA WRAPPER mysql OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_29963.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_29963.test index ab7a4ded07cdc..93b38c7963c60 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_29963.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_29963.test @@ -7,6 +7,7 @@ --enable_result_log --enable_query_log +set spider_same_server_link=1; evalp CREATE SERVER srv FOREIGN DATA WRAPPER mysql OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_30014.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_30014.test index 4dc3dafab24ac..9c59adc80dd02 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_30014.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_30014.test @@ -7,6 +7,7 @@ --enable_result_log --enable_query_log +set spider_same_server_link=1; evalp CREATE SERVER srv FOREIGN DATA WRAPPER mysql OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_30392.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_30392.test index 03417013b0749..36e06f3f3c467 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_30392.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_30392.test @@ -6,6 +6,7 @@ --source ../../t/test_init.inc --enable_result_log --enable_query_log +set spider_same_server_link=1; evalp CREATE SERVER srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); CREATE TABLE t1 (a INT); diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_31338.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_31338.test index e628c3b921d64..a3698c9771723 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_31338.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_31338.test @@ -9,6 +9,7 @@ --enable_result_log --enable_query_log +set spider_same_server_link=1; evalp CREATE SERVER srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); CREATE TABLE t (c BLOB) ENGINE=InnoDB; CREATE TABLE ts (c BLOB) ENGINE=Spider COMMENT='WRAPPER "mysql",srv "srv",TABLE "t"'; diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_31524.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_31524.test index 64cbf4154aa12..a5942fad2b303 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_31524.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_31524.test @@ -10,6 +10,7 @@ --let $srv=srv_mdev_31524 SET @old_spider_read_only_mode = @@session.spider_read_only_mode; +set spider_same_server_link=1; evalp CREATE SERVER $srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); # when the user does not set var nor the table option, the default diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_31645.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_31645.test index bec9dd6c3162b..4dfe3b57e5019 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_31645.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_31645.test @@ -6,6 +6,7 @@ --source ../../t/test_init.inc --enable_result_log --enable_query_log +set spider_same_server_link=1; evalp CREATE SERVER srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); CREATE TABLE t1 ( a bigint(20) NOT NULL, b bigint(20) DEFAULT 0, PRIMARY KEY (a)); diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_31996.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_31996.test index 3e823790c8274..93b004a06ebe9 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_31996.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_31996.test @@ -4,6 +4,7 @@ --enable_result_log --enable_query_log +set spider_same_server_link=1; evalp CREATE SERVER srv FOREIGN DATA WRAPPER mysql OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_32986.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_32986.test index 94081d24ad809..144387452f7c2 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_32986.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_32986.test @@ -6,6 +6,7 @@ --source ../../t/test_init.inc --enable_result_log --enable_query_log +set spider_same_server_link=1; evalp CREATE SERVER srv FOREIGN DATA WRAPPER mysql OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); diff --git a/storage/spider/mysql-test/spider/bugfix/t/spider_join_with_non_spider.test b/storage/spider/mysql-test/spider/bugfix/t/spider_join_with_non_spider.test index 7b5d38014ed99..294b469a8a559 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/spider_join_with_non_spider.test +++ b/storage/spider/mysql-test/spider/bugfix/t/spider_join_with_non_spider.test @@ -8,6 +8,7 @@ --enable_result_log --enable_query_log +set spider_same_server_link=1; evalp CREATE SERVER srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); create table t1 (c int); create table t2 (d int); diff --git a/storage/spider/mysql-test/spider/bugfix/t/subquery.test b/storage/spider/mysql-test/spider/bugfix/t/subquery.test index 7a50719603d56..70238a524a6ed 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/subquery.test +++ b/storage/spider/mysql-test/spider/bugfix/t/subquery.test @@ -6,6 +6,7 @@ --source ../../t/test_init.inc --enable_result_log --enable_query_log +set spider_same_server_link=1; evalp CREATE SERVER srv FOREIGN DATA WRAPPER MYSQL OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); create table t1 (c1 int); create table t2 (c2 int); diff --git a/storage/spider/spd_db_mysql.cc b/storage/spider/spd_db_mysql.cc index d2e9dc1f0ee89..fe83c6522600a 100644 --- a/storage/spider/spd_db_mysql.cc +++ b/storage/spider/spd_db_mysql.cc @@ -1985,7 +1985,7 @@ int spider_db_mbase::connect( if (!spider_param_same_server_link(thd)) { - if (!strcmp(tgt_host, my_localhost)) + if (!strcmp(tgt_host, my_localhost) || !tgt_host || !tgt_host[0]) { if (!strcmp(tgt_socket, *spd_mysqld_unix_port)) { @@ -1995,7 +1995,7 @@ int spider_db_mbase::connect( DBUG_RETURN(ER_SPIDER_SAME_SERVER_LINK_NUM); } } else if (!strcmp(tgt_host, "127.0.0.1") || - !strcmp(tgt_host, glob_hostname)) + !strcmp(tgt_host, glob_hostname) || !tgt_host || !tgt_host[0]) { if (tgt_port == (long) *spd_mysqld_port) { From 8b0fb154f767daf0870d751957a402fe12622848 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 10 Jan 2024 00:32:50 +0100 Subject: [PATCH 012/129] MDEV-33093 plugin/disks/information_schema_disks.cc doesn't compile on Solaris second part of the fix by Rainer Orth --- config.h.cmake | 1 + plugin/disks/CMakeLists.txt | 2 ++ plugin/disks/information_schema_disks.cc | 9 ++++----- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/config.h.cmake b/config.h.cmake index da2fc539bab0a..56c2bc3bcdc15 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -72,6 +72,7 @@ #cmakedefine HAVE_SYS_IPC_H 1 #cmakedefine HAVE_SYS_MALLOC_H 1 #cmakedefine HAVE_SYS_MMAN_H 1 +#cmakedefine HAVE_SYS_MNTENT_H 1 #cmakedefine HAVE_SYS_NDIR_H 1 #cmakedefine HAVE_SYS_PTE_H 1 #cmakedefine HAVE_SYS_PTEM_H 1 diff --git a/plugin/disks/CMakeLists.txt b/plugin/disks/CMakeLists.txt index e4cf0dc1de3de..408a4324d1bd7 100644 --- a/plugin/disks/CMakeLists.txt +++ b/plugin/disks/CMakeLists.txt @@ -5,6 +5,8 @@ CHECK_SYMBOL_EXISTS (getmntent "sys/mnttab.h" HAVE_GETMNTENT_IN_SYS_MNTAB) CHECK_SYMBOL_EXISTS (setmntent "mntent.h" HAVE_SETMNTENT) CHECK_SYMBOL_EXISTS (getmntinfo "sys/types.h;sys/mount.h" HAVE_GETMNTINFO) +CHECK_INCLUDE_FILES (sys/mntent.h HAVE_SYS_MNTENT_H) + IF (HAVE_GETMNTINFO) CHECK_CXX_SOURCE_COMPILES(" #include diff --git a/plugin/disks/information_schema_disks.cc b/plugin/disks/information_schema_disks.cc index 1be8fce37fa92..cbc749ff60339 100644 --- a/plugin/disks/information_schema_disks.cc +++ b/plugin/disks/information_schema_disks.cc @@ -19,19 +19,18 @@ #include #if defined(HAVE_GETMNTENT) #include -#elif defined(HAVE_SYS_MNTENT) -#include -#elif !defined(HAVE_GETMNTINFO_TAKES_statvfs) +#elif defined(HAVE_GETMNTINFO) && !defined(HAVE_GETMNTINFO_TAKES_statvfs) /* getmntinfo (the not NetBSD variants) */ #include -#if defined(HAVE_SYS_UCRED) #include -#endif #include #endif #if defined(HAVE_GETMNTENT_IN_SYS_MNTAB) #include #define HAVE_GETMNTENT +#if defined(HAVE_SYS_MNTENT_H) +#include +#endif #endif #include #include From c4ebf87f862ad6ab610e553a94dfa385a94a116c Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 13 Dec 2023 21:02:44 +0100 Subject: [PATCH 013/129] MDEV-32984 Update federated table and column privileges mark auto-inc columns for read/write on INSERT, but only for read on UPDATE --- mysql-test/suite/federated/update.result | 36 ++++++++++++++++++++++++ mysql-test/suite/federated/update.test | 32 +++++++++++++++++++++ sql/log_event_server.cc | 2 +- sql/table.cc | 9 +++--- sql/table.h | 2 +- 5 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 mysql-test/suite/federated/update.result create mode 100644 mysql-test/suite/federated/update.test diff --git a/mysql-test/suite/federated/update.result b/mysql-test/suite/federated/update.result new file mode 100644 index 0000000000000..1905f80ed71f1 --- /dev/null +++ b/mysql-test/suite/federated/update.result @@ -0,0 +1,36 @@ +connect master,127.0.0.1,root,,test,$MASTER_MYPORT,; +connect slave,127.0.0.1,root,,test,$SLAVE_MYPORT,; +connection master; +CREATE DATABASE federated; +connection slave; +CREATE DATABASE federated; +# +# MDEV-32984 Update federated table and column privileges +# +connection slave; +create database db1; +create user my@localhost identified by '1qaz2wsx'; +create table db1.t1 ( +f1 int auto_increment primary key, +f2 varchar(50), +f3 varchar(50), +unique (f2) +); +grant insert, select (f1, f2, f3), update (f3) on db1.t1 to my@localhost; +connection master; +create table tt1 engine=federated connection='mysql://my:1qaz2wsx@localhost:$SLAVE_MYPORT/db1/t1'; +insert into tt1 (f2,f3) values ('test','123'); +select * from tt1; +f1 f2 f3 +1 test 123 +update tt1 set f3='123456' where f2='test'; +drop table tt1; +connection slave; +drop database db1; +drop user my@localhost; +connection master; +DROP TABLE IF EXISTS federated.t1; +DROP DATABASE IF EXISTS federated; +connection slave; +DROP TABLE IF EXISTS federated.t1; +DROP DATABASE IF EXISTS federated; diff --git a/mysql-test/suite/federated/update.test b/mysql-test/suite/federated/update.test new file mode 100644 index 0000000000000..5a0414f1e1a0d --- /dev/null +++ b/mysql-test/suite/federated/update.test @@ -0,0 +1,32 @@ +source include/federated.inc; +source have_federatedx.inc; + +--echo # +--echo # MDEV-32984 Update federated table and column privileges +--echo # +connection slave; +create database db1; +create user my@localhost identified by '1qaz2wsx'; +create table db1.t1 ( + f1 int auto_increment primary key, + f2 varchar(50), + f3 varchar(50), + unique (f2) +); +grant insert, select (f1, f2, f3), update (f3) on db1.t1 to my@localhost; + +connection master; +evalp create table tt1 engine=federated connection='mysql://my:1qaz2wsx@localhost:$SLAVE_MYPORT/db1/t1'; +insert into tt1 (f2,f3) values ('test','123'); +select * from tt1; +update tt1 set f3='123456' where f2='test'; + +drop table tt1; + +connection slave; +drop database db1; +drop user my@localhost; + +source include/federated_cleanup.inc; + + diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc index f7a8a1db574f6..13a981d438117 100644 --- a/sql/log_event_server.cc +++ b/sql/log_event_server.cc @@ -7103,7 +7103,7 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability indexed and it cannot have a DEFAULT value). */ m_table->auto_increment_field_not_null= FALSE; - m_table->mark_auto_increment_column(); + m_table->mark_auto_increment_column(true); } return error; diff --git a/sql/table.cc b/sql/table.cc index 5ed8022da22cd..a5e33052d3f5b 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -7320,7 +7320,7 @@ inline void TABLE::mark_index_columns_for_read(uint index) always set and sometimes read. */ -void TABLE::mark_auto_increment_column() +void TABLE::mark_auto_increment_column(bool is_insert) { DBUG_ASSERT(found_next_number_field); /* @@ -7328,7 +7328,8 @@ void TABLE::mark_auto_increment_column() store() to check overflow of auto_increment values */ bitmap_set_bit(read_set, found_next_number_field->field_index); - bitmap_set_bit(write_set, found_next_number_field->field_index); + if (is_insert) + bitmap_set_bit(write_set, found_next_number_field->field_index); if (s->next_number_keypart) mark_index_columns_for_read(s->next_number_index); file->column_bitmaps_signal(); @@ -7453,7 +7454,7 @@ void TABLE::mark_columns_needed_for_update() else { if (found_next_number_field) - mark_auto_increment_column(); + mark_auto_increment_column(false); } if (file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) @@ -7527,7 +7528,7 @@ void TABLE::mark_columns_needed_for_insert() triggers->mark_fields_used(TRG_EVENT_INSERT); } if (found_next_number_field) - mark_auto_increment_column(); + mark_auto_increment_column(true); if (default_field) mark_default_fields_for_write(TRUE); /* Mark virtual columns for insert */ diff --git a/sql/table.h b/sql/table.h index 45095a8d1370f..9d659bb71eda4 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1585,7 +1585,7 @@ struct TABLE void mark_index_columns_no_reset(uint index, MY_BITMAP *bitmap); void mark_index_columns_for_read(uint index); void restore_column_maps_after_keyread(MY_BITMAP *backup); - void mark_auto_increment_column(void); + void mark_auto_increment_column(bool insert_fl); void mark_columns_needed_for_update(void); void mark_columns_needed_for_delete(void); void mark_columns_needed_for_insert(void); From 761d5c8987f1770767591ea3bf43f4048ffba8c1 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 10 Jan 2024 00:56:19 +0100 Subject: [PATCH 014/129] MDEV-33092 Undefined reference to concurrency on Solaris remove thr_setconcurrency() followup for 8bbcaab160c Fix by Rainer Orth --- cmake/os/WindowsCache.cmake | 1 - config.h.cmake | 1 - configure.cmake | 1 - include/my_pthread.h | 6 ------ libmysqld/lib_sql.cc | 2 -- mysys/thr_alarm.c | 1 - mysys/thr_lock.c | 3 --- mysys/thr_timer.c | 1 - sql/mysqld.cc | 2 -- storage/maria/unittest/ma_pagecache_consist.c | 4 ---- storage/maria/unittest/ma_pagecache_rwconsist.c | 4 ---- storage/maria/unittest/ma_pagecache_rwconsist2.c | 4 ---- storage/maria/unittest/ma_pagecache_single.c | 4 ---- storage/maria/unittest/ma_test_loghandler_multithread-t.c | 4 ---- 14 files changed, 38 deletions(-) diff --git a/cmake/os/WindowsCache.cmake b/cmake/os/WindowsCache.cmake index 4a3b3383393fd..628b17287475d 100644 --- a/cmake/os/WindowsCache.cmake +++ b/cmake/os/WindowsCache.cmake @@ -243,7 +243,6 @@ SET(HAVE_TERMCAP_H CACHE INTERNAL "") SET(HAVE_TERMIOS_H CACHE INTERNAL "") SET(HAVE_TERMIO_H CACHE INTERNAL "") SET(HAVE_TERM_H CACHE INTERNAL "") -SET(HAVE_THR_SETCONCURRENCY CACHE INTERNAL "") SET(HAVE_THR_YIELD CACHE INTERNAL "") SET(HAVE_TIME 1 CACHE INTERNAL "") SET(HAVE_TIMES CACHE INTERNAL "") diff --git a/config.h.cmake b/config.h.cmake index a8154bb18d1fa..652b95a1ed20a 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -231,7 +231,6 @@ #cmakedefine HAVE_STRTOUL 1 #cmakedefine HAVE_STRTOULL 1 #cmakedefine HAVE_TELL 1 -#cmakedefine HAVE_THR_SETCONCURRENCY 1 #cmakedefine HAVE_THR_YIELD 1 #cmakedefine HAVE_TIME 1 #cmakedefine HAVE_TIMES 1 diff --git a/configure.cmake b/configure.cmake index c7266cdef8109..fcfce85edb9be 100644 --- a/configure.cmake +++ b/configure.cmake @@ -417,7 +417,6 @@ CHECK_FUNCTION_EXISTS (strtoul HAVE_STRTOUL) CHECK_FUNCTION_EXISTS (strtoull HAVE_STRTOULL) CHECK_FUNCTION_EXISTS (strcasecmp HAVE_STRCASECMP) CHECK_FUNCTION_EXISTS (tell HAVE_TELL) -CHECK_FUNCTION_EXISTS (thr_setconcurrency HAVE_THR_SETCONCURRENCY) CHECK_FUNCTION_EXISTS (thr_yield HAVE_THR_YIELD) CHECK_FUNCTION_EXISTS (vasprintf HAVE_VASPRINTF) CHECK_FUNCTION_EXISTS (vsnprintf HAVE_VSNPRINTF) diff --git a/include/my_pthread.h b/include/my_pthread.h index a95e6f77b133a..10cc2301ea691 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -148,9 +148,6 @@ int pthread_cancel(pthread_t thread); #ifndef _REENTRANT #define _REENTRANT #endif -#ifdef HAVE_THR_SETCONCURRENCY -#include /* Probably solaris */ -#endif #ifdef HAVE_SCHED_H #include #endif @@ -619,9 +616,6 @@ extern int my_rw_trywrlock(my_rw_lock_t *); #define GETHOSTBYADDR_BUFF_SIZE 2048 -#ifndef HAVE_THR_SETCONCURRENCY -#define thr_setconcurrency(A) pthread_dummy(0) -#endif #if !defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE) && ! defined(pthread_attr_setstacksize) #define pthread_attr_setstacksize(A,B) pthread_dummy(0) #endif diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 8f772ac81223d..41eee607d2312 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -635,8 +635,6 @@ int init_embedded_server(int argc, char **argv, char **groups) udf_init(); #endif - (void) thr_setconcurrency(concurrency); // 10 by default - if (flush_time && flush_time != ~(ulong) 0L) start_handle_manager(); diff --git a/mysys/thr_alarm.c b/mysys/thr_alarm.c index 90429f728232c..d2b542ee9b678 100644 --- a/mysys/thr_alarm.c +++ b/mysys/thr_alarm.c @@ -786,7 +786,6 @@ int main(int argc __attribute__((unused)),char **argv __attribute__((unused))) mysql_mutex_unlock(&LOCK_thread_count); DBUG_PRINT("info",("signal thread created")); - thr_setconcurrency(3); pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS); printf("Main thread: %s\n",my_thread_name()); for (i=0 ; i < 2 ; i++) diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 168ec035e79d9..c9bc3a2d9fbf1 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -1783,9 +1783,6 @@ int main(int argc __attribute__((unused)),char **argv __attribute__((unused))) error,errno); exit(1); } -#endif -#ifdef HAVE_THR_SETCONCURRENCY - (void) thr_setconcurrency(2); #endif for (i=0 ; i < array_elements(lock_counts) ; i++) { diff --git a/mysys/thr_timer.c b/mysys/thr_timer.c index f87c1f755557c..d3627fea9830e 100644 --- a/mysys/thr_timer.c +++ b/mysys/thr_timer.c @@ -533,7 +533,6 @@ static void run_test() mysql_mutex_init(0, &LOCK_thread_count, MY_MUTEX_INIT_FAST); mysql_cond_init(0, &COND_thread_count, NULL); - thr_setconcurrency(3); pthread_attr_init(&thr_attr); pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS); printf("Main thread: %s\n",my_thread_name()); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index b698928c5f7fe..05545a1f4fb23 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -5490,8 +5490,6 @@ int mysqld_main(int argc, char **argv) SYSVAR_AUTOSIZE(my_thread_stack_size, new_thread_stack_size); } - (void) thr_setconcurrency(concurrency); // 10 by default - select_thread=pthread_self(); select_thread_in_use=1; diff --git a/storage/maria/unittest/ma_pagecache_consist.c b/storage/maria/unittest/ma_pagecache_consist.c index 29fa29ca0352e..4a9056e66c237 100644 --- a/storage/maria/unittest/ma_pagecache_consist.c +++ b/storage/maria/unittest/ma_pagecache_consist.c @@ -403,10 +403,6 @@ int main(int argc __attribute__((unused)), exit(1); } -#ifdef HAVE_THR_SETCONCURRENCY - thr_setconcurrency(2); -#endif - if ((pagen= init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, TEST_PAGE_SIZE, 0, 0)) == 0) { diff --git a/storage/maria/unittest/ma_pagecache_rwconsist.c b/storage/maria/unittest/ma_pagecache_rwconsist.c index a3303eb65a4ef..c4a2148175d17 100644 --- a/storage/maria/unittest/ma_pagecache_rwconsist.c +++ b/storage/maria/unittest/ma_pagecache_rwconsist.c @@ -272,10 +272,6 @@ int main(int argc __attribute__((unused)), exit(1); } -#ifdef HAVE_THR_SETCONCURRENCY - thr_setconcurrency(2); -#endif - if ((pagen= init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, TEST_PAGE_SIZE, 0, 0)) == 0) { diff --git a/storage/maria/unittest/ma_pagecache_rwconsist2.c b/storage/maria/unittest/ma_pagecache_rwconsist2.c index 2a0f76b478ff5..e1a80ac495bb0 100644 --- a/storage/maria/unittest/ma_pagecache_rwconsist2.c +++ b/storage/maria/unittest/ma_pagecache_rwconsist2.c @@ -268,10 +268,6 @@ int main(int argc __attribute__((unused)), exit(1); } -#ifdef HAVE_THR_SETCONCURRENCY - thr_setconcurrency(2); -#endif - if ((pagen= init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, TEST_PAGE_SIZE, 0, 0)) == 0) { diff --git a/storage/maria/unittest/ma_pagecache_single.c b/storage/maria/unittest/ma_pagecache_single.c index b0a259c85a4ef..a8aecc10a42d9 100644 --- a/storage/maria/unittest/ma_pagecache_single.c +++ b/storage/maria/unittest/ma_pagecache_single.c @@ -795,10 +795,6 @@ int main(int argc __attribute__((unused)), exit(1); } -#ifdef HAVE_THR_SETCONCURRENCY - thr_setconcurrency(2); -#endif - if ((pagen= init_pagecache(&pagecache, PCACHE_SIZE, 0, 0, TEST_PAGE_SIZE, 0, MYF(MY_WME))) == 0) { diff --git a/storage/maria/unittest/ma_test_loghandler_multithread-t.c b/storage/maria/unittest/ma_test_loghandler_multithread-t.c index cb4d2bc70bae8..e07f8d56e3232 100644 --- a/storage/maria/unittest/ma_test_loghandler_multithread-t.c +++ b/storage/maria/unittest/ma_test_loghandler_multithread-t.c @@ -331,10 +331,6 @@ int main(int argc __attribute__((unused)), exit(1); } -#ifdef HAVE_THR_SETCONCURRENCY - thr_setconcurrency(2); -#endif - if (ma_control_file_open(TRUE, TRUE, TRUE)) { fprintf(stderr, "Can't init control file (%d)\n", errno); From 7801c6d22dce546e9661be909dd0302000933f0b Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Fri, 15 Dec 2023 17:37:28 +1100 Subject: [PATCH 015/129] MDEV-29002 Spider: remove SPIDER_CONN::loop_check_meraged_last The field is assigned but unused, and it causes heap-use-after-free. --- .../spider/bugfix/r/mdev_29002.result | 34 +++++++++++++++++++ .../spider/bugfix/t/mdev_29002.test | 32 +++++++++++++++++ storage/spider/spd_conn.cc | 6 ---- storage/spider/spd_include.h | 1 - 4 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 storage/spider/mysql-test/spider/bugfix/r/mdev_29002.result create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_29002.test diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_29002.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_29002.result new file mode 100644 index 0000000000000..894f51c5e3670 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_29002.result @@ -0,0 +1,34 @@ +for master_1 +for child2 +for child3 +SET spider_same_server_link= on; +CREATE SERVER s FOREIGN DATA WRAPPER mysql +OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); +CREATE TABLE t (a INT); +CREATE TABLE t1_spider (a INT) ENGINE=SPIDER COMMENT = "wrapper 'mysql', srv 's', table 't'"; +CREATE TABLE t2_spider (a INT) ENGINE=SPIDER COMMENT = "wrapper 'mysql', srv 's', table 't'"; +SELECT * FROM t1_spider, t2_spider; +a a +SELECT table_name, index_name, cardinality FROM INFORMATION_SCHEMA.STATISTICS WHERE table_name IN ('t1_spider','t2_spider'); +table_name index_name cardinality +RENAME TABLE t1_spider TO t3_spider; +SELECT * FROM t3_spider; +a +DROP TABLE t3_spider, t2_spider, t; +drop server s; +CREATE TABLE t1 (c INT) ENGINE=Spider COMMENT='WRAPPER "mysql",HOST "srv",TABLE "t"'; +CREATE TABLE t2 (c INT) ENGINE=Spider COMMENT='WRAPPER "mysql",HOST "srv",TABLE "t"'; +CREATE TABLE t3 (c INT) ENGINE=Spider COMMENT='WRAPPER "mysql",HOST "srv",TABLE "t"'; +SHOW TABLE STATUS; +Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment Max_index_length Temporary +t1 SPIDER 10 NULL 0 0 0 0 0 NULL NULL NULL NULL latin1_swedish_ci NULL Unable to connect to foreign data source: srv 0 +t2 SPIDER 10 NULL 0 0 0 0 0 NULL NULL NULL NULL latin1_swedish_ci NULL Unable to connect to foreign data source: srv 0 +t3 SPIDER 10 NULL 0 0 0 0 0 NULL NULL NULL NULL latin1_swedish_ci NULL Unable to connect to foreign data source: srv 0 +Warnings: +Warning 1429 Unable to connect to foreign data source: srv +Warning 1429 Unable to connect to foreign data source: srv +Warning 1429 Unable to connect to foreign data source: srv +drop table t1, t2, t3; +for master_1 +for child2 +for child3 diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_29002.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_29002.test new file mode 100644 index 0000000000000..51620a5a23c91 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_29002.test @@ -0,0 +1,32 @@ +--disable_query_log +--disable_result_log +--source ../../t/test_init.inc +--enable_result_log +--enable_query_log +SET spider_same_server_link= on; +evalp CREATE SERVER s FOREIGN DATA WRAPPER mysql +OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); + +CREATE TABLE t (a INT); +CREATE TABLE t1_spider (a INT) ENGINE=SPIDER COMMENT = "wrapper 'mysql', srv 's', table 't'"; +CREATE TABLE t2_spider (a INT) ENGINE=SPIDER COMMENT = "wrapper 'mysql', srv 's', table 't'"; +SELECT * FROM t1_spider, t2_spider; +SELECT table_name, index_name, cardinality FROM INFORMATION_SCHEMA.STATISTICS WHERE table_name IN ('t1_spider','t2_spider'); +RENAME TABLE t1_spider TO t3_spider; +SELECT * FROM t3_spider; + +DROP TABLE t3_spider, t2_spider, t; +drop server s; + +# case by roel +CREATE TABLE t1 (c INT) ENGINE=Spider COMMENT='WRAPPER "mysql",HOST "srv",TABLE "t"'; +CREATE TABLE t2 (c INT) ENGINE=Spider COMMENT='WRAPPER "mysql",HOST "srv",TABLE "t"'; +CREATE TABLE t3 (c INT) ENGINE=Spider COMMENT='WRAPPER "mysql",HOST "srv",TABLE "t"'; +SHOW TABLE STATUS; +drop table t1, t2, t3; + +--disable_query_log +--disable_result_log +--source ../../t/test_deinit.inc +--enable_result_log +--enable_query_log diff --git a/storage/spider/spd_conn.cc b/storage/spider/spd_conn.cc index 5ca3bb06fec21..9897e495c8377 100644 --- a/storage/spider/spd_conn.cc +++ b/storage/spider/spd_conn.cc @@ -1783,13 +1783,7 @@ int spider_conn_queue_and_merge_loop_check( lcptr->flag = SPIDER_LOP_CHK_MERAGED; lcptr->next = NULL; if (!conn->loop_check_meraged_first) - { conn->loop_check_meraged_first = lcptr; - conn->loop_check_meraged_last = lcptr; - } else { - conn->loop_check_meraged_last->next = lcptr; - conn->loop_check_meraged_last = lcptr; - } } DBUG_RETURN(0); diff --git a/storage/spider/spd_include.h b/storage/spider/spd_include.h index 59c3a181780f6..8bf44d094b263 100644 --- a/storage/spider/spd_include.h +++ b/storage/spider/spd_include.h @@ -940,7 +940,6 @@ typedef struct st_spider_conn SPIDER_CONN_LOOP_CHECK *loop_check_ignored_first; SPIDER_CONN_LOOP_CHECK *loop_check_ignored_last; SPIDER_CONN_LOOP_CHECK *loop_check_meraged_first; - SPIDER_CONN_LOOP_CHECK *loop_check_meraged_last; } SPIDER_CONN; typedef struct st_spider_lgtm_tblhnd_share From 9e9e0b99ade0e6ccd34abc3bc3abaf7bbd5ecf4e Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Tue, 19 Dec 2023 12:18:13 +1100 Subject: [PATCH 016/129] MDEV-30170 ha_spider::delete_table() should report table not exist All Spider tables are recorded in the system table mysql.spider_tables. Deleting a spider table removes the corresponding rows from the system table, among other things. This patch makes it so that if spider could not find any record in the system table to delete for a given table, it should correctly report that no such Spider table exists. --- storage/spider/ha_spider.cc | 9 +++++++- .../spider/bugfix/r/mdev_30170.result | 7 ++++++ .../spider/bugfix/t/mdev_30170.test | 8 +++++++ storage/spider/spd_sys_table.cc | 22 ++++++++++++++++++- 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 storage/spider/mysql-test/spider/bugfix/r/mdev_30170.result create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_30170.test diff --git a/storage/spider/ha_spider.cc b/storage/spider/ha_spider.cc index 08d3fc40f0595..5011f86c044ad 100644 --- a/storage/spider/ha_spider.cc +++ b/storage/spider/ha_spider.cc @@ -11371,7 +11371,10 @@ int ha_spider::create( if ( thd->lex->create_info.or_replace() && (error_num = spider_delete_tables( - table_tables, tmp_share.table_name, &dummy)) + table_tables, tmp_share.table_name, &dummy)) && + /* In this context, no key found in mysql.spider_tables means + the Spider table does not exist */ + error_num != HA_ERR_KEY_NOT_FOUND ) { goto error; } @@ -11842,6 +11845,10 @@ int ha_spider::delete_table( (error_num = spider_delete_tables( table_tables, name, &old_link_count)) ) { + /* In this context, no key found in mysql.spider_tables means + the Spider table does not exist */ + if (error_num == HA_ERR_KEY_NOT_FOUND) + error_num= HA_ERR_NO_SUCH_TABLE; goto error; } spider_close_sys_table(current_thd, table_tables, diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_30170.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_30170.result new file mode 100644 index 0000000000000..2183447bfa375 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_30170.result @@ -0,0 +1,7 @@ +install soname 'ha_spider'; +DROP TABLE non_existing_table; +ERROR 42S02: Unknown table 'test.non_existing_table' +create or replace table non_existing_table (c int) engine=Spider; +drop table non_existing_table; +Warnings: +Warning 1620 Plugin is busy and will be uninstalled on shutdown diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_30170.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_30170.test new file mode 100644 index 0000000000000..690268432d33e --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_30170.test @@ -0,0 +1,8 @@ +install soname 'ha_spider'; +--error ER_BAD_TABLE_ERROR +DROP TABLE non_existing_table; +# Test that create or replace a non existing spider table work +create or replace table non_existing_table (c int) engine=Spider; +drop table non_existing_table; +--disable_query_log +--source ../../include/clean_up_spider.inc diff --git a/storage/spider/spd_sys_table.cc b/storage/spider/spd_sys_table.cc index b3a45b73af773..a8549d5467d96 100644 --- a/storage/spider/spd_sys_table.cc +++ b/storage/spider/spd_sys_table.cc @@ -1865,6 +1865,16 @@ int spider_delete_xa_member( DBUG_RETURN(0); } +/** + Delete a Spider table from mysql.spider_tables. + + @param table The table mysql.spider_tables + @param name The name of the Spider table to delete + @param old_link_count The number of links in the deleted table + + @retval 0 Success + @retval nonzero Failure +*/ int spider_delete_tables( TABLE *table, const char *name, @@ -1880,10 +1890,20 @@ int spider_delete_tables( { spider_store_tables_link_idx(table, roop_count); if ((error_num = spider_check_sys_table(table, table_key))) - break; + { + /* There's a problem with finding the first record for the + spider table, likely because it does not exist. Fail */ + if (roop_count == 0) + DBUG_RETURN(error_num); + /* At least one row has been deleted for the Spider table. + Success */ + else + break; + } else { if ((error_num = spider_delete_sys_table_row(table))) { + /* There's a problem deleting the row. Fail */ DBUG_RETURN(error_num); } } From d277a63c749bd49c9025da4efdd3008d22180dc0 Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Tue, 28 Nov 2023 14:56:23 +1100 Subject: [PATCH 017/129] MDEV-31101 Re-enable spider/bugfix.mdev_29904 The spider init bug fixes remove any race conditions during spider init. Also remove the add_suppressions in spider/bugfix.mdev_27575 which is a similar issue. --- storage/spider/mysql-test/spider/bugfix/disabled.def | 1 - storage/spider/mysql-test/spider/bugfix/r/mdev_27575.result | 2 -- storage/spider/mysql-test/spider/bugfix/t/mdev_27575.test | 4 ---- 3 files changed, 7 deletions(-) diff --git a/storage/spider/mysql-test/spider/bugfix/disabled.def b/storage/spider/mysql-test/spider/bugfix/disabled.def index 99443c854e557..e19ea07b76bbb 100644 --- a/storage/spider/mysql-test/spider/bugfix/disabled.def +++ b/storage/spider/mysql-test/spider/bugfix/disabled.def @@ -1,2 +1 @@ wait_timeout : MDEV-26045 -mdev_29904 : MDEV-31101 diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_27575.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_27575.result index 4899f191ada92..91af2c8f1e092 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_27575.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_27575.result @@ -4,8 +4,6 @@ for master_1 for child2 for child3 -call mtr.add_suppression("\\[ERROR\\] Table 'mysql.spider_table_sts' doesn't exist"); -call mtr.add_suppression("\\[ERROR\\] Server shutdown in progress"); SET GLOBAL default_tmp_storage_engine=spider; # restart SET GLOBAL default_storage_engine=Spider; diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_27575.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_27575.test index 6f291c6f69082..79a08489bae26 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_27575.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_27575.test @@ -7,10 +7,6 @@ --enable_result_log --enable_query_log -# These suppressions are a workaround and should not be needed once -# MDEV-29870 is done. -call mtr.add_suppression("\\[ERROR\\] Table 'mysql.spider_table_sts' doesn't exist"); -call mtr.add_suppression("\\[ERROR\\] Server shutdown in progress"); SET GLOBAL default_tmp_storage_engine=spider; --source include/restart_mysqld.inc From 88c46aba753c0094ea16dc707bb76cc5254806c8 Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Thu, 11 Jan 2024 12:32:26 +1100 Subject: [PATCH 018/129] MDEV-32997 Disable spider/bugfix.mdev_27575 until we find a solution The failure described in MDEV-32997 is happening a bit too often and polluting the CI results. --- storage/spider/mysql-test/spider/bugfix/disabled.def | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/spider/mysql-test/spider/bugfix/disabled.def b/storage/spider/mysql-test/spider/bugfix/disabled.def index e19ea07b76bbb..559feeff4bd14 100644 --- a/storage/spider/mysql-test/spider/bugfix/disabled.def +++ b/storage/spider/mysql-test/spider/bugfix/disabled.def @@ -1 +1,2 @@ wait_timeout : MDEV-26045 +mdev_27575 : MDEV-32997 From f807a9f874a8079d95bc71c9f27216e4d952f157 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Thu, 11 Jan 2024 11:21:32 +0100 Subject: [PATCH 019/129] MDEV-31523 Using two temporary tables in OPTIMIZE TABLE lead to crash Fixed typo in mysql_admin_table which cused call of close_unused_temporary_table_instances alwas for the first table instead of the current table. Added ASSERT that close_unused_temporary_table_instances should not remove all instances of user created temporary table. --- mysql-test/main/temp_table.result | 16 ++++++++++++++++ mysql-test/main/temp_table.test | 16 ++++++++++++++++ sql/sql_admin.cc | 2 +- sql/temporary_tables.cc | 5 +++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/temp_table.result b/mysql-test/main/temp_table.result index e5bb0d54306ed..33ea170047473 100644 --- a/mysql-test/main/temp_table.result +++ b/mysql-test/main/temp_table.result @@ -602,3 +602,19 @@ DROP TEMPORARY TABLE t1; # # End of 10.2 tests # +# +# MDEV-31523: Using two temporary tables in OPTIMIZE TABLE lead to crash +# +CREATE TEMPORARY TABLE t1 (c INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE t2 (c INT) ENGINE=MyISAM; +optimize TABLE t1,t2; +Table Op Msg_type Msg_text +test.t1 optimize status Table is already up to date +test.t2 optimize status Table is already up to date +SHOW TABLES; +Tables_in_test +# in 11.2 and above here should be listed above used temporary tables +DROP TEMPORARY TABLE t1, t2; +# +# End of 10.4 tests +# diff --git a/mysql-test/main/temp_table.test b/mysql-test/main/temp_table.test index 9e639ffce2ce4..e1e671f5ae93b 100644 --- a/mysql-test/main/temp_table.test +++ b/mysql-test/main/temp_table.test @@ -661,3 +661,19 @@ DROP TEMPORARY TABLE t1; --echo # --echo # End of 10.2 tests --echo # + +--echo # +--echo # MDEV-31523: Using two temporary tables in OPTIMIZE TABLE lead to crash +--echo # + +CREATE TEMPORARY TABLE t1 (c INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE t2 (c INT) ENGINE=MyISAM; +optimize TABLE t1,t2; +SHOW TABLES; +--echo # in 11.2 and above here should be listed above used temporary tables + +DROP TEMPORARY TABLE t1, t2; + +--echo # +--echo # End of 10.4 tests +--echo # diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 66dfc93f45d68..09deef9f590ce 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -776,7 +776,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, if (lock_type == TL_WRITE && table->mdl_request.type > MDL_SHARED_WRITE) { if (table->table->s->tmp_table) - thd->close_unused_temporary_table_instances(tables); + thd->close_unused_temporary_table_instances(table); else { if (wait_while_table_is_used(thd, table->table, HA_EXTRA_NOT_USED)) diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc index 05e32d78b7f61..4ffae3d53bf81 100644 --- a/sql/temporary_tables.cc +++ b/sql/temporary_tables.cc @@ -1577,6 +1577,11 @@ void THD::close_unused_temporary_table_instances(const TABLE_LIST *tl) { /* Note: removing current list element doesn't invalidate iterator. */ share->all_tmp_tables.remove(table); + /* + At least one instance should be left (guaratead by calling this + function for table which is opened and the table is under processing) + */ + DBUG_ASSERT(share->all_tmp_tables.front()); free_temporary_table(table); } } From 9a5f85dcbe12dd9e3228d77834aed8ab53885f20 Mon Sep 17 00:00:00 2001 From: Anel Husakovic Date: Mon, 13 Nov 2023 15:40:13 +0100 Subject: [PATCH 020/129] MDEV-32790: Output result in show create table for mysql_json type should be longtext - We don't test `json` MySQL tables from `std_data` since the error `ER_TABLE_NEEDS_REBUILD ` is invoked. However MDEV-32235 will override this test after merge, but leave it to show behavior and historical changes. - Closes PR #2833 Reviewer: --- .../main/mysql_json_table_recreate.result | 44 +++++++++++++++++++ .../main/mysql_json_table_recreate.test | 36 +++++++++++++++ plugin/type_mysql_json/type.cc | 2 +- 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/mysql_json_table_recreate.result b/mysql-test/main/mysql_json_table_recreate.result index 207dde9d8ad2c..b2ab19f4fbf5f 100644 --- a/mysql-test/main/mysql_json_table_recreate.result +++ b/mysql-test/main/mysql_json_table_recreate.result @@ -169,3 +169,47 @@ Total_Number_of_Tests Succesful_Tests String_is_valid_JSON drop table tempty; drop table mysql_json_test; drop table mysql_json_test_big; +# +# MDEV-32790: Output result in show create table +# for mysql_json type should be longtext +# +create table t1(j json); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `j` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`j`)) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +drop table t1; +create table t1(j mysql_json); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `j` mysql_json /* JSON from MySQL 5.7 */ CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +drop table t1; +create table `testjson` ( +`t` json /* JSON from MySQL 5.7*/ CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL +) ENGINE=InnoDB DEFAULT CH...' at line 2 +create table `testjson` ( +`t` json /* JSON from MySQL 5.7*/ COLLATE utf8mb4_bin NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci; +show create table testjson; +Table Create Table +testjson CREATE TABLE `testjson` ( + `t` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL CHECK (json_valid(`t`)) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +drop table testjson; +create table `testjson` ( +`t` longtext /* JSON from MySQL 5.7 */ CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci; +show create table testjson; +Table Create Table +testjson CREATE TABLE `testjson` ( + `t` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +drop table testjson; +# +# End of 10.5 tests +# diff --git a/mysql-test/main/mysql_json_table_recreate.test b/mysql-test/main/mysql_json_table_recreate.test index a399b546591b2..7e9895a1f107f 100644 --- a/mysql-test/main/mysql_json_table_recreate.test +++ b/mysql-test/main/mysql_json_table_recreate.test @@ -88,3 +88,39 @@ from mysql_json_test_big; drop table tempty; drop table mysql_json_test; drop table mysql_json_test_big; + +--echo # +--echo # MDEV-32790: Output result in show create table +--echo # for mysql_json type should be longtext +--echo # + +create table t1(j json); +show create table t1; +drop table t1; +create table t1(j mysql_json); +show create table t1; +drop table t1; +# `json` type should not have character set and collation other than utf8mb4_bin +--error ER_PARSE_ERROR +create table `testjson` ( + `t` json /* JSON from MySQL 5.7*/ CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci; + +# By removing character set from `json` field query should work and +# expand to `longtext` with characterset +create table `testjson` ( + `t` json /* JSON from MySQL 5.7*/ COLLATE utf8mb4_bin NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci; +show create table testjson; +drop table testjson; + +# `longtext` that is alias can have character set +create table `testjson` ( + `t` longtext /* JSON from MySQL 5.7 */ CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci; +show create table testjson; +drop table testjson; + +--echo # +--echo # End of 10.5 tests +--echo # diff --git a/plugin/type_mysql_json/type.cc b/plugin/type_mysql_json/type.cc index c5168969b67e2..c915e0312bbc3 100644 --- a/plugin/type_mysql_json/type.cc +++ b/plugin/type_mysql_json/type.cc @@ -62,7 +62,7 @@ class Field_mysql_json: public Field_blob bool parse_mysql(String *dest, const char *data, size_t length) const; bool send(Protocol *protocol) { return Field::send(protocol); } void sql_type(String &s) const - { s.set_ascii(STRING_WITH_LEN("json /* MySQL 5.7 */")); } + { s.set_ascii(STRING_WITH_LEN("mysql_json /* JSON from MySQL 5.7 */")); } /* this will make ALTER TABLE to consider it different from built-in field */ Compression_method *compression_method() const { return (Compression_method*)1; } }; From 22f3ebe4bf11acb7b5e854bc4e6b3439af6982f1 Mon Sep 17 00:00:00 2001 From: Anel Husakovic Date: Thu, 11 Jan 2024 15:15:46 +0100 Subject: [PATCH 021/129] MDEV-32235: mysql_json cannot be used on newly created table Closes PR #2839 Reviewer: cvicentiu@mariadb.org --- .../main/mysql_json_table_recreate.result | 18 ++++++++++++------ mysql-test/main/mysql_json_table_recreate.test | 17 +++++++++++++++-- sql/sql_table.cc | 13 +++++++++++++ 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/mysql-test/main/mysql_json_table_recreate.result b/mysql-test/main/mysql_json_table_recreate.result index b2ab19f4fbf5f..a0c2483d3d924 100644 --- a/mysql-test/main/mysql_json_table_recreate.result +++ b/mysql-test/main/mysql_json_table_recreate.result @@ -181,12 +181,7 @@ t1 CREATE TABLE `t1` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci drop table t1; create table t1(j mysql_json); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `j` mysql_json /* JSON from MySQL 5.7 */ CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci -drop table t1; +ERROR HY000: Cannot create table `test`.`t1`: Run mariadb-upgrade, to upgrade table with mysql_json type. create table `testjson` ( `t` json /* JSON from MySQL 5.7*/ CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci; @@ -211,5 +206,16 @@ testjson CREATE TABLE `testjson` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci drop table testjson; # +# MDEV-32235: mysql_json cannot be used on newly created table +# +CREATE TABLE t(j mysql_json); +ERROR HY000: Cannot create table `test`.`t`: Run mariadb-upgrade, to upgrade table with mysql_json type. +CREATE TABLE IF NOT EXISTS t(j mysql_json); +ERROR HY000: Cannot create table `test`.`t`: Run mariadb-upgrade, to upgrade table with mysql_json type. +CREATE OR REPLACE TABLE t(j mysql_json); +ERROR HY000: Cannot create table `test`.`t`: Run mariadb-upgrade, to upgrade table with mysql_json type. +CREATE TEMPORARY TABLE t(j mysql_json); +ERROR HY000: Cannot create table `test`.`t`: Run mariadb-upgrade, to upgrade table with mysql_json type. +# # End of 10.5 tests # diff --git a/mysql-test/main/mysql_json_table_recreate.test b/mysql-test/main/mysql_json_table_recreate.test index 7e9895a1f107f..bf4cac0f90955 100644 --- a/mysql-test/main/mysql_json_table_recreate.test +++ b/mysql-test/main/mysql_json_table_recreate.test @@ -97,9 +97,8 @@ drop table mysql_json_test_big; create table t1(j json); show create table t1; drop table t1; +--error ER_CANT_CREATE_TABLE create table t1(j mysql_json); -show create table t1; -drop table t1; # `json` type should not have character set and collation other than utf8mb4_bin --error ER_PARSE_ERROR create table `testjson` ( @@ -121,6 +120,20 @@ create table `testjson` ( show create table testjson; drop table testjson; + +--echo # +--echo # MDEV-32235: mysql_json cannot be used on newly created table +--echo # + +--error ER_CANT_CREATE_TABLE +CREATE TABLE t(j mysql_json); +--error ER_CANT_CREATE_TABLE +CREATE TABLE IF NOT EXISTS t(j mysql_json); +--error ER_CANT_CREATE_TABLE +CREATE OR REPLACE TABLE t(j mysql_json); +--error ER_CANT_CREATE_TABLE +CREATE TEMPORARY TABLE t(j mysql_json); + --echo # --echo # End of 10.5 tests --echo # diff --git a/sql/sql_table.cc b/sql/sql_table.cc index d68bbd1ac4f69..8088d3e0d70e3 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3657,6 +3657,19 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (sql_field->vcol_info) sql_field->flags&= ~NOT_NULL_FLAG; + if (sql_field->real_field_type() == MYSQL_TYPE_BLOB && + thd->lex->sql_command == SQLCOM_CREATE_TABLE) + { + if (!strcmp(sql_field->type_handler()->name().ptr(), "MYSQL_JSON")) + { + my_printf_error(ER_CANT_CREATE_TABLE, + "Cannot create table %`s.%`s: " + "Run mariadb-upgrade, " + "to upgrade table with mysql_json type.", MYF(0), + alter_info->db.str, alter_info->table_name.str); + DBUG_RETURN(TRUE); + } + } /* Initialize length from its original value (number of characters), which was set in the parser. This is necessary if we're From d0c80c211c1fe3370b68be540bb9113028c6746f Mon Sep 17 00:00:00 2001 From: Dave Gosselin Date: Mon, 8 Jan 2024 11:58:29 -0500 Subject: [PATCH 022/129] MDEV-32090 Test for null-safe equals in join This ticket is fixed by MDEV-32555 and this test captures a different use case. --- mysql-test/main/subselect_nulls_innodb.result | 27 ++++++++++++++++ mysql-test/main/subselect_nulls_innodb.test | 32 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 mysql-test/main/subselect_nulls_innodb.result create mode 100644 mysql-test/main/subselect_nulls_innodb.test diff --git a/mysql-test/main/subselect_nulls_innodb.result b/mysql-test/main/subselect_nulls_innodb.result new file mode 100644 index 0000000000000..2cab41760b314 --- /dev/null +++ b/mysql-test/main/subselect_nulls_innodb.result @@ -0,0 +1,27 @@ +# +# MDEV-32090 Index does not handle null-safe equals operator correctly in join +# +CREATE TEMPORARY TABLE t1 ( +`id` int(10) unsigned NOT NULL, +`number` int(10) unsigned DEFAULT 0, +`name` varchar(47) DEFAULT NULL, +`street` mediumint(8) unsigned DEFAULT NULL, +PRIMARY KEY (`id`), +KEY `streetNumber` (`street`,`number`,`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +INSERT INTO t1 (id, number, name, street) VALUES (100733476, 14, NULL, 1115569); +SELECT +b1.id +FROM +t1 b1 +INNER JOIN t1 b2 ON ( +b1.street = b2.street +AND b1.number <=> b2.number +AND b1.name <=> b2.name +); +id +100733476 +DROP TABLE t1; +# +# End of 10.11 tests +# diff --git a/mysql-test/main/subselect_nulls_innodb.test b/mysql-test/main/subselect_nulls_innodb.test new file mode 100644 index 0000000000000..79d572a2e0e3b --- /dev/null +++ b/mysql-test/main/subselect_nulls_innodb.test @@ -0,0 +1,32 @@ +--source include/have_innodb.inc + +--echo # +--echo # MDEV-32090 Index does not handle null-safe equals operator correctly in join +--echo # + +CREATE TEMPORARY TABLE t1 ( + `id` int(10) unsigned NOT NULL, + `number` int(10) unsigned DEFAULT 0, + `name` varchar(47) DEFAULT NULL, + `street` mediumint(8) unsigned DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `streetNumber` (`street`,`number`,`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +INSERT INTO t1 (id, number, name, street) VALUES (100733476, 14, NULL, 1115569); + +SELECT + b1.id +FROM + t1 b1 + INNER JOIN t1 b2 ON ( + b1.street = b2.street + AND b1.number <=> b2.number + AND b1.name <=> b2.name + ); + +DROP TABLE t1; + +--echo # +--echo # End of 10.11 tests +--echo # From 351a8eecf050886ed3a701ffdf47b5badce72a6b Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Tue, 12 Sep 2023 11:27:54 +0400 Subject: [PATCH 023/129] MDEV-32148 Inefficient WHERE timestamp_column=datetime_const_expr Changing the way how a the following conditions are evaluated: WHERE timestamp_column=datetime_const_expr (for all comparison operators: =, <=>, <, >, <=, >=, <> and for NULLIF) Before the change it was always performed as DATETIME. That was not efficient, as involved per-row TIMESTAMP->DATETIME conversion for timestamp_column. For example, in case of the SYSTEM time zone it involved a localtime_r() call, which is known to be slow. After the change it's performed as TIMESTAMP in many cases. This allows to avoid per-row conversion, as it works the other way around: datetime_const_expr is converted to TIMESTAMP once before the execution stage. Note, datetime_const_expr must be inside monotone continuous periods of the current time zone, i.e. not near these anomalies: - DST changes (spring forward, fall back) - leap seconds --- mysql-test/main/rowid_filter_innodb.result | 2 +- mysql-test/main/timezone2.result | 185 ++++++ mysql-test/main/timezone2.test | 152 +++++ mysql-test/main/type_timestamp.result | 550 +++++++++++++++++- mysql-test/main/type_timestamp.test | 240 ++++++++ mysql-test/suite/versioning/r/cte.result | 8 +- mysql-test/suite/versioning/r/derived.result | 4 +- mysql-test/suite/versioning/r/select.result | 6 +- mysql-test/suite/versioning/t/cte.test | 6 + mysql-test/suite/versioning/t/derived.test | 4 + mysql-test/suite/versioning/t/select.test | 5 + sql/item.cc | 11 + sql/item.h | 18 +- sql/item_cmpfunc.cc | 47 +- sql/item_func.h | 2 + sql/sql_class.cc | 57 ++ sql/sql_class.h | 2 + sql/sql_type.cc | 135 +++++ sql/sql_type.h | 44 +- sql/structs.h | 25 + sql/tztime.cc | 53 ++ sql/tztime.h | 7 + .../mysql-test/spider/r/timestamp.result | 4 +- 23 files changed, 1543 insertions(+), 24 deletions(-) diff --git a/mysql-test/main/rowid_filter_innodb.result b/mysql-test/main/rowid_filter_innodb.result index 276b57a106ca4..24b80881c6e01 100644 --- a/mysql-test/main/rowid_filter_innodb.result +++ b/mysql-test/main/rowid_filter_innodb.result @@ -2917,7 +2917,7 @@ ORDER BY timestamp DESC; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL ixEventWhoisDomainDomain,ixEventWhoisDomainTimestamp NULL NULL NULL 60 22.22 Using where; Using filesort Warnings: -Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`domain` AS `domain`,`test`.`t1`.`registrant_name` AS `registrant_name`,`test`.`t1`.`registrant_organization` AS `registrant_organization`,`test`.`t1`.`registrant_street1` AS `registrant_street1`,`test`.`t1`.`registrant_street2` AS `registrant_street2`,`test`.`t1`.`registrant_street3` AS `registrant_street3`,`test`.`t1`.`registrant_street4` AS `registrant_street4`,`test`.`t1`.`registrant_street5` AS `registrant_street5`,`test`.`t1`.`registrant_city` AS `registrant_city`,`test`.`t1`.`registrant_postal_code` AS `registrant_postal_code`,`test`.`t1`.`registrant_country` AS `registrant_country`,`test`.`t1`.`registrant_email` AS `registrant_email`,`test`.`t1`.`registrant_telephone` AS `registrant_telephone`,`test`.`t1`.`administrative_name` AS `administrative_name`,`test`.`t1`.`administrative_organization` AS `administrative_organization`,`test`.`t1`.`administrative_street1` AS `administrative_street1`,`test`.`t1`.`administrative_street2` AS `administrative_street2`,`test`.`t1`.`administrative_street3` AS `administrative_street3`,`test`.`t1`.`administrative_street4` AS `administrative_street4`,`test`.`t1`.`administrative_street5` AS `administrative_street5`,`test`.`t1`.`administrative_city` AS `administrative_city`,`test`.`t1`.`administrative_postal_code` AS `administrative_postal_code`,`test`.`t1`.`administrative_country` AS `administrative_country`,`test`.`t1`.`administrative_email` AS `administrative_email`,`test`.`t1`.`administrative_telephone` AS `administrative_telephone`,`test`.`t1`.`technical_name` AS `technical_name`,`test`.`t1`.`technical_organization` AS `technical_organization`,`test`.`t1`.`technical_street1` AS `technical_street1`,`test`.`t1`.`technical_street2` AS `technical_street2`,`test`.`t1`.`technical_street3` AS `technical_street3`,`test`.`t1`.`technical_street4` AS `technical_street4`,`test`.`t1`.`technical_street5` AS `technical_street5`,`test`.`t1`.`technical_city` AS `technical_city`,`test`.`t1`.`technical_postal_code` AS `technical_postal_code`,`test`.`t1`.`technical_country` AS `technical_country`,`test`.`t1`.`technical_email` AS `technical_email`,`test`.`t1`.`technical_telephone` AS `technical_telephone`,`test`.`t1`.`json` AS `json`,`test`.`t1`.`timestamp` AS `timestamp` from `test`.`t1` where `test`.`t1`.`domain` = 'www.mailhost.i-dev.fr' and `test`.`t1`.`timestamp` >= ('2017-01-30 08:24:51' + interval -1 month) order by `test`.`t1`.`timestamp` desc +Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`domain` AS `domain`,`test`.`t1`.`registrant_name` AS `registrant_name`,`test`.`t1`.`registrant_organization` AS `registrant_organization`,`test`.`t1`.`registrant_street1` AS `registrant_street1`,`test`.`t1`.`registrant_street2` AS `registrant_street2`,`test`.`t1`.`registrant_street3` AS `registrant_street3`,`test`.`t1`.`registrant_street4` AS `registrant_street4`,`test`.`t1`.`registrant_street5` AS `registrant_street5`,`test`.`t1`.`registrant_city` AS `registrant_city`,`test`.`t1`.`registrant_postal_code` AS `registrant_postal_code`,`test`.`t1`.`registrant_country` AS `registrant_country`,`test`.`t1`.`registrant_email` AS `registrant_email`,`test`.`t1`.`registrant_telephone` AS `registrant_telephone`,`test`.`t1`.`administrative_name` AS `administrative_name`,`test`.`t1`.`administrative_organization` AS `administrative_organization`,`test`.`t1`.`administrative_street1` AS `administrative_street1`,`test`.`t1`.`administrative_street2` AS `administrative_street2`,`test`.`t1`.`administrative_street3` AS `administrative_street3`,`test`.`t1`.`administrative_street4` AS `administrative_street4`,`test`.`t1`.`administrative_street5` AS `administrative_street5`,`test`.`t1`.`administrative_city` AS `administrative_city`,`test`.`t1`.`administrative_postal_code` AS `administrative_postal_code`,`test`.`t1`.`administrative_country` AS `administrative_country`,`test`.`t1`.`administrative_email` AS `administrative_email`,`test`.`t1`.`administrative_telephone` AS `administrative_telephone`,`test`.`t1`.`technical_name` AS `technical_name`,`test`.`t1`.`technical_organization` AS `technical_organization`,`test`.`t1`.`technical_street1` AS `technical_street1`,`test`.`t1`.`technical_street2` AS `technical_street2`,`test`.`t1`.`technical_street3` AS `technical_street3`,`test`.`t1`.`technical_street4` AS `technical_street4`,`test`.`t1`.`technical_street5` AS `technical_street5`,`test`.`t1`.`technical_city` AS `technical_city`,`test`.`t1`.`technical_postal_code` AS `technical_postal_code`,`test`.`t1`.`technical_country` AS `technical_country`,`test`.`t1`.`technical_email` AS `technical_email`,`test`.`t1`.`technical_telephone` AS `technical_telephone`,`test`.`t1`.`json` AS `json`,`test`.`t1`.`timestamp` AS `timestamp` from `test`.`t1` where `test`.`t1`.`domain` = 'www.mailhost.i-dev.fr' and `test`.`t1`.`timestamp` >= TIMESTAMP/*WITH LOCAL TIME ZONE*/'2016-12-30 08:24:51' order by `test`.`t1`.`timestamp` desc SET optimizer_switch=@save_optimizer_switch; DROP TABLE t1; # diff --git a/mysql-test/main/timezone2.result b/mysql-test/main/timezone2.result index 787552d915936..382ed447c9275 100644 --- a/mysql-test/main/timezone2.result +++ b/mysql-test/main/timezone2.result @@ -678,3 +678,188 @@ SELECT a, UNIX_TIMESTAMP(a) FROM t1 WHERE a >= ALL (SELECT * FROM t1); a UNIX_TIMESTAMP(a) 2010-10-31 02:25:25 1288481125 DROP TABLE t1; +# +# MDEV-32148 Inefficient WHERE timestamp_column=datetime_expr +# +# +# Testing a DST change (fall back) +# +SET time_zone='Europe/Moscow'; +SET @first_second_after_dst_fall_back=1288479600; +CREATE TABLE t1 (a TIMESTAMP NULL); +INSERT INTO t1 VALUES ('2001-01-01 10:20:30'),('2001-01-01 10:20:31'); +# +# Optimized (more than 24 hours before the DST fall back) +# +SET timestamp=@first_second_after_dst_fall_back-24*3600-1; +SELECT UNIX_TIMESTAMP(), NOW(); +UNIX_TIMESTAMP() NOW() +1288393199 2010-10-30 02:59:59 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2010-10-30 02:59:59' +# +# Not optimized (24 hours before the DST fall back) +# +SET timestamp=@first_second_after_dst_fall_back-24*3600; +SELECT UNIX_TIMESTAMP(), NOW(); +UNIX_TIMESTAMP() NOW() +1288393200 2010-10-30 03:00:00 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = (current_timestamp()) +# +# Not optimized (less than 24 hours after the DST fall back) +# +SET timestamp=@first_second_after_dst_fall_back+24*3600-1; +SELECT UNIX_TIMESTAMP(), NOW(); +UNIX_TIMESTAMP() NOW() +1288565999 2010-11-01 01:59:59 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = (current_timestamp()) +# +# Optimized (24 hours after the DST fall back) +# +SET timestamp=@first_second_after_dst_fall_back+24*3600; +SELECT UNIX_TIMESTAMP(), NOW(); +UNIX_TIMESTAMP() NOW() +1288566000 2010-11-01 02:00:00 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2010-11-01 02:00:00' +DROP TABLE t1; +SET time_zone=DEFAULT; +# +# Testing a DST change (spring forward) +# +SET time_zone='Europe/Moscow'; +SET @first_second_after_dst_spring_forward=1301180400; +CREATE TABLE t1 (a TIMESTAMP NULL); +INSERT INTO t1 VALUES ('2001-01-01 10:20:30'),('2001-01-01 10:20:31'); +# +# Optimized (more than 24 hours before the DST sprint forward) +# +SET timestamp=@first_second_after_dst_spring_forward-24*3600-1; +SELECT UNIX_TIMESTAMP(), NOW(); +UNIX_TIMESTAMP() NOW() +1301093999 2011-03-26 01:59:59 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2011-03-26 01:59:59' +# +# Not optimized (24 hours before the DST sprint forward) +# +SET timestamp=@first_second_after_dst_spring_forward-24*3600; +SELECT UNIX_TIMESTAMP(), NOW(); +UNIX_TIMESTAMP() NOW() +1301094000 2011-03-26 02:00:00 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = (current_timestamp()) +# +# Not optimized (less than 24 hours after the DST sprint forward) +# +SET timestamp=@first_second_after_dst_spring_forward+24*3600-1; +SELECT UNIX_TIMESTAMP(), NOW(); +UNIX_TIMESTAMP() NOW() +1301266799 2011-03-28 02:59:59 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = (current_timestamp()) +# +# Optimized (24 hours after the DST sprint forward) +# +SET timestamp=@first_second_after_dst_spring_forward+24*3600; +SELECT UNIX_TIMESTAMP(), NOW(); +UNIX_TIMESTAMP() NOW() +1301266800 2011-03-28 03:00:00 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2011-03-28 03:00:00' +DROP TABLE t1; +# +# Testing a leap second +# +SET time_zone='leap/Europe/Moscow'; +SET @leap_second=362793609; +/*The 60th leap second*/ +CREATE TABLE t1 (a TIMESTAMP); +SET timestamp=@leap_second-1; +INSERT INTO t1 VALUES (NOW()); +SET timestamp=@leap_second; +INSERT INTO t1 VALUES (NOW()); +SET timestamp=@leap_second+1; +INSERT INTO t1 VALUES (NOW()); +SELECT UNIX_TIMESTAMP(a), a FROM t1 ORDER BY UNIX_TIMESTAMP(a); +UNIX_TIMESTAMP(a) a +362793608 1981-07-01 03:59:59 +362793609 1981-07-01 03:59:59 +362793610 1981-07-01 04:00:00 +INSERT INTO t1 VALUES ('2001-01-01 10:20:30'); +# +# Optimized (more than 24 hours before the leap second) +# +SET timestamp=@leap_second-24*3600-1; +SELECT UNIX_TIMESTAMP(), NOW(); +UNIX_TIMESTAMP() NOW() +362707208 1981-06-30 03:59:59 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'1981-06-30 03:59:59' +# +# Not optimized (24 hours before the leap second) +# +SET timestamp=@leap_second-24*3600; +SELECT UNIX_TIMESTAMP(), NOW(); +UNIX_TIMESTAMP() NOW() +362707209 1981-06-30 04:00:00 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = (current_timestamp()) +# +# Not optimized (less than 24 hours after the leap second) +# +SET timestamp=@leap_second+24*3600-1; +SELECT UNIX_TIMESTAMP(), NOW(); +UNIX_TIMESTAMP() NOW() +362880008 1981-07-02 03:59:58 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = (current_timestamp()) +# +# Not optimized (24 hours after the leap second) +# +SET timestamp=@leap_second+24*3600; +SELECT UNIX_TIMESTAMP(), NOW(); +UNIX_TIMESTAMP() NOW() +362880009 1981-07-02 03:59:59 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'1981-07-02 03:59:59' +DROP TABLE t1; +SET time_zone=DEFAULT; diff --git a/mysql-test/main/timezone2.test b/mysql-test/main/timezone2.test index b504520390333..09be74089cea8 100644 --- a/mysql-test/main/timezone2.test +++ b/mysql-test/main/timezone2.test @@ -630,3 +630,155 @@ SELECT a, UNIX_TIMESTAMP(a) FROM t1 ORDER BY a; SELECT a, UNIX_TIMESTAMP(a) FROM t1 WHERE a <= ALL (SELECT * FROM t1); SELECT a, UNIX_TIMESTAMP(a) FROM t1 WHERE a >= ALL (SELECT * FROM t1); DROP TABLE t1; + + +--echo # +--echo # MDEV-32148 Inefficient WHERE timestamp_column=datetime_expr +--echo # + +--echo # +--echo # Testing a DST change (fall back) +--echo # + +SET time_zone='Europe/Moscow'; +# '2010-10-31 02:59:59' (1288479599) +# '2010-10-31 02:00:00' (1288479600) +SET @first_second_after_dst_fall_back=1288479600; + +CREATE TABLE t1 (a TIMESTAMP NULL); +INSERT INTO t1 VALUES ('2001-01-01 10:20:30'),('2001-01-01 10:20:31'); + +--echo # +--echo # Optimized (more than 24 hours before the DST fall back) +--echo # + +SET timestamp=@first_second_after_dst_fall_back-24*3600-1; +SELECT UNIX_TIMESTAMP(), NOW(); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); + +--echo # +--echo # Not optimized (24 hours before the DST fall back) +--echo # + +SET timestamp=@first_second_after_dst_fall_back-24*3600; +SELECT UNIX_TIMESTAMP(), NOW(); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); + +--echo # +--echo # Not optimized (less than 24 hours after the DST fall back) +--echo # + +SET timestamp=@first_second_after_dst_fall_back+24*3600-1; +SELECT UNIX_TIMESTAMP(), NOW(); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); + +--echo # +--echo # Optimized (24 hours after the DST fall back) +--echo # + +SET timestamp=@first_second_after_dst_fall_back+24*3600; +SELECT UNIX_TIMESTAMP(), NOW(); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); + +DROP TABLE t1; +SET time_zone=DEFAULT; + + +--echo # +--echo # Testing a DST change (spring forward) +--echo # + +SET time_zone='Europe/Moscow'; +# '2011-03-27 01:59:59' (1301180399) +# '2011-03-27 03:00:00' (1301180400) +SET @first_second_after_dst_spring_forward=1301180400; + +CREATE TABLE t1 (a TIMESTAMP NULL); +INSERT INTO t1 VALUES ('2001-01-01 10:20:30'),('2001-01-01 10:20:31'); + +--echo # +--echo # Optimized (more than 24 hours before the DST sprint forward) +--echo # + +SET timestamp=@first_second_after_dst_spring_forward-24*3600-1; +SELECT UNIX_TIMESTAMP(), NOW(); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); + +--echo # +--echo # Not optimized (24 hours before the DST sprint forward) +--echo # + +SET timestamp=@first_second_after_dst_spring_forward-24*3600; +SELECT UNIX_TIMESTAMP(), NOW(); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); + +--echo # +--echo # Not optimized (less than 24 hours after the DST sprint forward) +--echo # + +SET timestamp=@first_second_after_dst_spring_forward+24*3600-1; +SELECT UNIX_TIMESTAMP(), NOW(); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); + +--echo # +--echo # Optimized (24 hours after the DST sprint forward) +--echo # + +SET timestamp=@first_second_after_dst_spring_forward+24*3600; +SELECT UNIX_TIMESTAMP(), NOW(); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); + +DROP TABLE t1; + +--echo # +--echo # Testing a leap second +--echo # + +SET time_zone='leap/Europe/Moscow'; +SET @leap_second=362793609; /*The 60th leap second*/ + +CREATE TABLE t1 (a TIMESTAMP); +SET timestamp=@leap_second-1; +INSERT INTO t1 VALUES (NOW()); +SET timestamp=@leap_second; +INSERT INTO t1 VALUES (NOW()); +SET timestamp=@leap_second+1; +INSERT INTO t1 VALUES (NOW()); +SELECT UNIX_TIMESTAMP(a), a FROM t1 ORDER BY UNIX_TIMESTAMP(a); +INSERT INTO t1 VALUES ('2001-01-01 10:20:30'); + +--echo # +--echo # Optimized (more than 24 hours before the leap second) +--echo # + +SET timestamp=@leap_second-24*3600-1; +SELECT UNIX_TIMESTAMP(), NOW(); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); + +--echo # +--echo # Not optimized (24 hours before the leap second) +--echo # + +SET timestamp=@leap_second-24*3600; +SELECT UNIX_TIMESTAMP(), NOW(); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); + +--echo # +--echo # Not optimized (less than 24 hours after the leap second) +--echo # + +SET timestamp=@leap_second+24*3600-1; +SELECT UNIX_TIMESTAMP(), NOW(); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); + +--echo # +--echo # Not optimized (24 hours after the leap second) +--echo # + +SET timestamp=@leap_second+24*3600; +SELECT UNIX_TIMESTAMP(), NOW(); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=now(); + +DROP TABLE t1; + +SET time_zone=DEFAULT; diff --git a/mysql-test/main/type_timestamp.result b/mysql-test/main/type_timestamp.result index 78e860661c5ad..6b1e5bb82845c 100644 --- a/mysql-test/main/type_timestamp.result +++ b/mysql-test/main/type_timestamp.result @@ -872,13 +872,13 @@ SELECT * FROM t1 WHERE LENGTH(a)=19 AND a=' 2001-01-01 00:00:00'; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2001-01-01 00:00:00' +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 00:00:00' EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19+RAND() AND a=' 2001-01-01 00:00:00'; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2001-01-01 00:00:00' and (octet_length(TIMESTAMP'2001-01-01 00:00:00')) = 19 + rand() +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 00:00:00' and (octet_length(TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 00:00:00')) = 19 + rand() EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=30+RAND() AND a=' garbage '; id select_type table type possible_keys key key_len ref rows filtered Extra @@ -905,13 +905,13 @@ SELECT * FROM t1 WHERE LENGTH(a)=19 AND a=TIMESTAMP'2001-01-01 00:00:00.000000'; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2001-01-01 00:00:00.000000' +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 00:00:00.000000' EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=30+RAND() AND a=TIMESTAMP'2001-01-01 00:00:00.000000'; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2001-01-01 00:00:00.000000' and (octet_length(TIMESTAMP'2001-01-01 00:00:00')) = 30 + rand() +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 00:00:00.000000' and (octet_length(TIMESTAMP'2001-01-01 00:00:00')) = 30 + rand() DROP TABLE t1; CREATE TABLE t1 (a TIMESTAMP(6));; INSERT INTO t1 VALUES ('2001-01-01 00:00:00.000000'),('2001-01-01 00:00:01.000000'); @@ -930,13 +930,13 @@ SELECT * FROM t1 WHERE LENGTH(a)=26 AND a=TIMESTAMP'2001-01-01 00:00:00.000000'; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2001-01-01 00:00:00.000000' +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 00:00:00.000000' EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=40+RAND() AND a=TIMESTAMP'2001-01-01 00:00:00.000000'; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2001-01-01 00:00:00.000000' and (octet_length(TIMESTAMP'2001-01-01 00:00:00.000000')) = 40 + rand() +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 00:00:00.000000' and (octet_length(TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 00:00:00.000000')) = 40 + rand() DROP TABLE t1; SET timestamp=UNIX_TIMESTAMP('2001-01-01 10:20:30'); CREATE TABLE t1 (a TIMESTAMP);; @@ -956,13 +956,13 @@ SELECT * FROM t1 WHERE LENGTH(a)=19 AND a=TIME'00:00:00'; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2001-01-01 00:00:00' +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 00:00:00' EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=40+RAND() AND a=TIME'00:00:00'; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where Warnings: -Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2001-01-01 00:00:00' and (octet_length(TIMESTAMP'2001-01-01 00:00:00')) = 40 + rand() +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 00:00:00' and (octet_length(TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 00:00:00')) = 40 + rand() DROP TABLE t1; # # End of 10.1 tests @@ -1347,6 +1347,540 @@ SET time_zone=DEFAULT; # End of 10.4 tests # # +# Start of 10.6 tests +# +# +# MDEV-32148 Inefficient WHERE timestamp_column=datetime_expr +# +SET time_zone='+00:00'; +CREATE TABLE t1 (a TIMESTAMP); +INSERT INTO t1 VALUES ('0000-00-00 00:00:00'); +INSERT INTO t1 VALUES ('2001-01-01 10:20:30'), ('2001-01-01 10:20:31'); +# +# Comparison predicates: Bad TIMESTAMP values preserve DATETIME comparison +# +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1001-01-01 10:20:30'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'1001-01-01 10:20:30' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2001-01-00 00:00:00'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2001-01-00 00:00:00' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2040-01-01 10:20:30'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2040-01-01 10:20:30' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='1001-01-01 10:20:30'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'1001-01-01 10:20:30' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='2001-01-00 00:00:00'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Warning 1292 Incorrect datetime value: '2001-01-00 00:00:00' +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = '2001-01-00 00:00:00' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='2040-01-01 10:20:30'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2040-01-01 10:20:30' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=10010101102030; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 10010101102030 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20010100000000; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 20010100000000 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20400101102030; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 20400101102030 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=10010101102030e0; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 10010101102030e0 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20010100000000e0; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 20010100000000e0 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20400101102030e0; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 20400101102030e0 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=10010101102030.0; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 10010101102030.0 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20010100000000.0; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 20010100000000.0 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20400101102030.0; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 20400101102030.0 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=DATE_ADD('2020-01-01 10:20:30', INTERVAL 30 YEAR); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2050-01-01 10:20:30' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE COALESCE(NULL, a)=DATE_ADD('2020-01-01 10:20:30', INTERVAL 30 YEAR); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where coalesce(NULL,`test`.`t1`.`a`) = ('2020-01-01 10:20:30' + interval 30 year) +# +# Comparison predicates: Good TIMESTAMP values switch to TIMESTAMP comparison +# +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'0000-00-00 00:00:00'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'0000-00-00 00:00:00' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2001-01-01 10:20:30'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='0000-00-00 00:00:00'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'0000-00-00 00:00:00' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='2001-01-01 10:20:30'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=0; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'0000-00-00 00:00:00' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20010101102030; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=0e0; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'0000-00-00 00:00:00.000000' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20010101102030e0; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30.000000' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=0.0; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'0000-00-00 00:00:00.0' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20010101102030.0; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30.0' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=DATE_ADD('2001-01-01 10:20:00', INTERVAL 30 SECOND); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE COALESCE(NULL, a)=DATE_ADD('2001-01-01 10:20:00', INTERVAL 30 SECOND); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where coalesce(NULL,`test`.`t1`.`a`) = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30' +# +# Corner cases: DATETIME values before the supported optimization range +# FROM_UNIXTIME(0)..FROM_UNIXTIME(24*3600-1) +# +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1970-01-01 00:00:00'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'1970-01-01 00:00:00' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1970-01-01 23:59:59'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'1970-01-01 23:59:59' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1970-01-01 23:59:59.999999'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'1970-01-01 23:59:59.999999' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1970-01-01 23:59:59.9999999'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1292 Truncated incorrect DATETIME value: '1970-01-01 23:59:59.9999999' +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'1970-01-01 23:59:59.999999' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=FROM_UNIXTIME(24*3600-1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = (from_unixtime(24 * 3600 - 1)) +# +# Corner cases: DATETIME values inside the supported optimization range: +# FROM_UNIXTIME(24*3600) .. FROM_UNIXTIME(0x7FFFFFFF-24*3600) +# +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=FROM_UNIXTIME(24*3600); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'1970-01-02 00:00:00' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1970-01-02 00:00:00'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'1970-01-02 00:00:00' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2038-01-18 03:14:07'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2038-01-18 03:14:07' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=FROM_UNIXTIME(0x7FFFFFFF-24*3600); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2038-01-18 03:14:07' +# +# Corner cases: DATETIME values after the supported optimization range +# FROM_UNIXTIME(0x7FFFFFFF-24*3600+1) .. FROM_UNIXTIME(0x7FFFFFFF) +# +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=FROM_UNIXTIME(0x7FFFFFFF-24*3600+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = (from_unixtime(0x7fffffff - 24 * 3600 + 1)) +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2038-01-18 03:14:08'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2038-01-18 03:14:08' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2038-01-19 03:14:07'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2038-01-19 03:14:07' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=FROM_UNIXTIME(0x7FFFFFFF); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = (from_unixtime(0x7fffffff)) +# +# Corner cases: rounding +# +SET sql_mode=TIME_ROUND_FRACTIONAL; +# +# Not optimized (before the supported range) +# +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1970-01-01 23:59:59.999999'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'1970-01-01 23:59:59.999999' +# +# Optimized +# +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1970-01-01 23:59:59.9999999'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1292 Truncated incorrect DATETIME value: '1970-01-01 23:59:59.9999999' +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'1970-01-02 00:00:00.000000' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2038-01-18 03:14:07.999999'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2038-01-18 03:14:07.999999' +# +# Not optimized (after the supported range) +# +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2038-01-18 03:14:07.9999999'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1292 Truncated incorrect DATETIME value: '2038-01-18 03:14:07.9999999' +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2038-01-18 03:14:08.000000' +SET sql_mode=DEFAULT; +# +# NULLIF: Bad TIMESTAMP values preserve DATETIME comparison +# +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,TIMESTAMP'1001-01-01 10:20:30'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,TIMESTAMP'1001-01-01 10:20:30') +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,TIMESTAMP'2001-01-00 00:00:00'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,TIMESTAMP'2001-01-00 00:00:00') +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,TIMESTAMP'2040-01-01 10:20:30'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,TIMESTAMP'2040-01-01 10:20:30') +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,'1001-01-01 10:20:30'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,'1001-01-01 10:20:30') +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,'2001-01-00 00:00:00'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,'2001-01-00 00:00:00') +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,'2040-01-01 10:20:30'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,'2040-01-01 10:20:30') +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,10010101102030); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,10010101102030) +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20010100000000); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,20010100000000) +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20400101102030); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,20400101102030) +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,10010101102030e0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,10010101102030e0) +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20010100000000e0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,20010100000000e0) +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20400101102030e0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,20400101102030e0) +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,10010101102030.0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,10010101102030.0) +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20010100000000.0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,20010100000000.0) +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20400101102030.0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,20400101102030.0) +# +# NULLIF: Good TIMESTAMP values switch to TIMESTAMP comparison +# +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,TIMESTAMP'0000-00-00 00:00:00'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,TIMESTAMP/*WITH LOCAL TIME ZONE*/'0000-00-00 00:00:00') +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,TIMESTAMP'2001-01-01 10:20:30'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30') +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,'0000-00-00 00:00:00'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,TIMESTAMP/*WITH LOCAL TIME ZONE*/'0000-00-00 00:00:00') +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,'2001-01-01 10:20:30'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30') +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,TIMESTAMP/*WITH LOCAL TIME ZONE*/'0000-00-00 00:00:00') +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20010101102030); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30') +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,0e0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,TIMESTAMP/*WITH LOCAL TIME ZONE*/'0000-00-00 00:00:00.000000') +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20010101102030e0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30.000000') +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,0.0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,TIMESTAMP/*WITH LOCAL TIME ZONE*/'0000-00-00 00:00:00.0') +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20010101102030.0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where nullif(`test`.`t1`.`a`,TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30.0') +# +# Subqueries with bad TIMESTAMP values +# It's not clear from the output if the comparison is done as DATETIME or TIMESTAMP +# +CREATE TABLE t2 (a DATETIME); +INSERT INTO t2 VALUES ('1001-01-01 10:20:30'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=(SELECT MAX(a) FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +2 SUBQUERY t2 system NULL NULL NULL NULL 1 100.00 +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = (/* select#2 */ select max('1001-01-01 10:20:30') from dual) +DROP TABLE t2; +CREATE TABLE t2 (a DATETIME); +INSERT INTO t2 VALUES ('2001-00-00 00:00:00'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=(SELECT MAX(a) FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +2 SUBQUERY t2 system NULL NULL NULL NULL 1 100.00 +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = (/* select#2 */ select max('2001-00-00 00:00:00') from dual) +DROP TABLE t2; +CREATE TABLE t2 (a DATETIME); +INSERT INTO t2 VALUES ('2040-01-01 10:20:30'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=(SELECT MAX(a) FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +2 SUBQUERY t2 system NULL NULL NULL NULL 1 100.00 +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = (/* select#2 */ select max('2040-01-01 10:20:30') from dual) +DROP TABLE t2; +# +# Subqueries with good TIMESTAMP values +# It's not clear from the output if the comparison is done as DATETIME or TIMESTAMP +# +CREATE TABLE t2 (a DATETIME); +INSERT INTO t2 VALUES ('0000-00-00 00:00:00'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=(SELECT MAX(a) FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +2 SUBQUERY t2 system NULL NULL NULL NULL 1 100.00 +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = (/* select#2 */ select max('0000-00-00 00:00:00') from dual) +DROP TABLE t2; +CREATE TABLE t2 (a DATETIME); +INSERT INTO t2 VALUES ('2001-01-01 10:20:30'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=(SELECT MAX(a) FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +2 SUBQUERY t2 system NULL NULL NULL NULL 1 100.00 +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = (/* select#2 */ select max('2001-01-01 10:20:30') from dual) +DROP TABLE t2; +# +# Trivial equality elimination +# Covers Type_handler_timestamp_common::Item_const_eq() +# +EXPLAIN EXTENDED SELECT * FROM t1 +WHERE IF(a='2001-01-01 00:00:00',1,0)=IF(a='2001-01-01 00:00:00',1,0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where if(`test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 00:00:00',1,0) = if(`test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 00:00:00',1,0) +EXECUTE IMMEDIATE 'EXPLAIN EXTENDED SELECT * FROM t1 WHERE (a<=>?)=(a<=>?)' + USING '2001-01-01 00:00:00', '2001-01-01 00:00:00'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where 1 +# +# Equal field propagation: Good TIMESTAMP values switch to TIMESTAMP comparison +# +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19 AND a=TIMESTAMP'2001-01-01 10:20:30'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19 AND a='2001-01-01 10:20:30'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19 AND a=20010101102030; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19 AND a=20010101102030e0; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30.000000' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19 AND a=20010101102030.0; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30.0' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19+RAND() AND a=TIMESTAMP'2001-01-01 10:20:30'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30' and (octet_length(TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30')) = 19 + rand() +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19+RAND() AND a='2001-01-01 10:20:30'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30' and (octet_length(TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30')) = 19 + rand() +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19+RAND() AND a=20010101102030; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30' and (octet_length(TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30')) = 19 + rand() +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19+RAND() AND a=20010101102030e0; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30.000000' and (octet_length(TIMESTAMP'2001-01-01 10:20:30')) = 19 + rand() +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19+RAND() AND a=20010101102030.0; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP/*WITH LOCAL TIME ZONE*/'2001-01-01 10:20:30.0' and (octet_length(TIMESTAMP'2001-01-01 10:20:30')) = 19 + rand() +DROP TABLE t1; +SET time_zone=DEFAULT; +# +# End of 10.6 tests +# +# # MDEV-29225 make explicit_defaults_for_timestamps SESSION variable # set explicit_defaults_for_timestamp=OFF; diff --git a/mysql-test/main/type_timestamp.test b/mysql-test/main/type_timestamp.test index b3dfa19223d06..34276caede4fb 100644 --- a/mysql-test/main/type_timestamp.test +++ b/mysql-test/main/type_timestamp.test @@ -907,6 +907,246 @@ SET time_zone=DEFAULT; --echo # End of 10.4 tests --echo # +--echo # +--echo # Start of 10.6 tests +--echo # + +--echo # +--echo # MDEV-32148 Inefficient WHERE timestamp_column=datetime_expr +--echo # + +SET time_zone='+00:00'; +CREATE TABLE t1 (a TIMESTAMP); +INSERT INTO t1 VALUES ('0000-00-00 00:00:00'); +INSERT INTO t1 VALUES ('2001-01-01 10:20:30'), ('2001-01-01 10:20:31'); + +--echo # +--echo # Comparison predicates: Bad TIMESTAMP values preserve DATETIME comparison +--echo # + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1001-01-01 10:20:30'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2001-01-00 00:00:00'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2040-01-01 10:20:30'; + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='1001-01-01 10:20:30'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='2001-01-00 00:00:00'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='2040-01-01 10:20:30'; + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=10010101102030; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20010100000000; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20400101102030; + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=10010101102030e0; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20010100000000e0; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20400101102030e0; + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=10010101102030.0; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20010100000000.0; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20400101102030.0; + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=DATE_ADD('2020-01-01 10:20:30', INTERVAL 30 YEAR); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE COALESCE(NULL, a)=DATE_ADD('2020-01-01 10:20:30', INTERVAL 30 YEAR); + +--echo # +--echo # Comparison predicates: Good TIMESTAMP values switch to TIMESTAMP comparison +--echo # + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'0000-00-00 00:00:00'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2001-01-01 10:20:30'; + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='0000-00-00 00:00:00'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='2001-01-01 10:20:30'; + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=0; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20010101102030; + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=0e0; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20010101102030e0; + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=0.0; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=20010101102030.0; + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=DATE_ADD('2001-01-01 10:20:00', INTERVAL 30 SECOND); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE COALESCE(NULL, a)=DATE_ADD('2001-01-01 10:20:00', INTERVAL 30 SECOND); + +--echo # +--echo # Corner cases: DATETIME values before the supported optimization range +--echo # FROM_UNIXTIME(0)..FROM_UNIXTIME(24*3600-1) +--echo # + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1970-01-01 00:00:00'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1970-01-01 23:59:59'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1970-01-01 23:59:59.999999'; + +# Truncation of the 7th flactional digit produces a NOTE only without --ps +--disable_ps_protocol +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1970-01-01 23:59:59.9999999'; +--enable_ps_protocol +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=FROM_UNIXTIME(24*3600-1); + +--echo # +--echo # Corner cases: DATETIME values inside the supported optimization range: +--echo # FROM_UNIXTIME(24*3600) .. FROM_UNIXTIME(0x7FFFFFFF-24*3600) +--echo # + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=FROM_UNIXTIME(24*3600); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1970-01-02 00:00:00'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2038-01-18 03:14:07'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=FROM_UNIXTIME(0x7FFFFFFF-24*3600); + +--echo # +--echo # Corner cases: DATETIME values after the supported optimization range +--echo # FROM_UNIXTIME(0x7FFFFFFF-24*3600+1) .. FROM_UNIXTIME(0x7FFFFFFF) +--echo # + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=FROM_UNIXTIME(0x7FFFFFFF-24*3600+1); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2038-01-18 03:14:08'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2038-01-19 03:14:07'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=FROM_UNIXTIME(0x7FFFFFFF); + +--echo # +--echo # Corner cases: rounding +--echo # + +SET sql_mode=TIME_ROUND_FRACTIONAL; + +--echo # +--echo # Not optimized (before the supported range) +--echo # +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1970-01-01 23:59:59.999999'; + +--echo # +--echo # Optimized +--echo # +--disable_ps_protocol +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'1970-01-01 23:59:59.9999999'; +--enable_ps_protocol +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2038-01-18 03:14:07.999999'; + +--echo # +--echo # Not optimized (after the supported range) +--echo # +--disable_ps_protocol +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=TIMESTAMP'2038-01-18 03:14:07.9999999'; +--enable_ps_protocol + +SET sql_mode=DEFAULT; + +--echo # +--echo # NULLIF: Bad TIMESTAMP values preserve DATETIME comparison +--echo # + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,TIMESTAMP'1001-01-01 10:20:30'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,TIMESTAMP'2001-01-00 00:00:00'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,TIMESTAMP'2040-01-01 10:20:30'); + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,'1001-01-01 10:20:30'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,'2001-01-00 00:00:00'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,'2040-01-01 10:20:30'); + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,10010101102030); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20010100000000); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20400101102030); + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,10010101102030e0); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20010100000000e0); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20400101102030e0); + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,10010101102030.0); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20010100000000.0); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20400101102030.0); + +--echo # +--echo # NULLIF: Good TIMESTAMP values switch to TIMESTAMP comparison +--echo # + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,TIMESTAMP'0000-00-00 00:00:00'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,TIMESTAMP'2001-01-01 10:20:30'); + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,'0000-00-00 00:00:00'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,'2001-01-01 10:20:30'); + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,0); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20010101102030); + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,0e0); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20010101102030e0); + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,0.0); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE NULLIF(a,20010101102030.0); + + +--echo # +--echo # Subqueries with bad TIMESTAMP values +--echo # It's not clear from the output if the comparison is done as DATETIME or TIMESTAMP +--echo # + +CREATE TABLE t2 (a DATETIME); +INSERT INTO t2 VALUES ('1001-01-01 10:20:30'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=(SELECT MAX(a) FROM t2); +DROP TABLE t2; + +CREATE TABLE t2 (a DATETIME); +INSERT INTO t2 VALUES ('2001-00-00 00:00:00'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=(SELECT MAX(a) FROM t2); +DROP TABLE t2; + +CREATE TABLE t2 (a DATETIME); +INSERT INTO t2 VALUES ('2040-01-01 10:20:30'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=(SELECT MAX(a) FROM t2); +DROP TABLE t2; + +--echo # +--echo # Subqueries with good TIMESTAMP values +--echo # It's not clear from the output if the comparison is done as DATETIME or TIMESTAMP +--echo # + +CREATE TABLE t2 (a DATETIME); +INSERT INTO t2 VALUES ('0000-00-00 00:00:00'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=(SELECT MAX(a) FROM t2); +DROP TABLE t2; + +CREATE TABLE t2 (a DATETIME); +INSERT INTO t2 VALUES ('2001-01-01 10:20:30'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=(SELECT MAX(a) FROM t2); +DROP TABLE t2; + +--echo # +--echo # Trivial equality elimination +--echo # Covers Type_handler_timestamp_common::Item_const_eq() +--echo # + +EXPLAIN EXTENDED SELECT * FROM t1 +WHERE IF(a='2001-01-01 00:00:00',1,0)=IF(a='2001-01-01 00:00:00',1,0); + +EXECUTE IMMEDIATE 'EXPLAIN EXTENDED SELECT * FROM t1 WHERE (a<=>?)=(a<=>?)' + USING '2001-01-01 00:00:00', '2001-01-01 00:00:00'; + + +--echo # +--echo # Equal field propagation: Good TIMESTAMP values switch to TIMESTAMP comparison +--echo # + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19 AND a=TIMESTAMP'2001-01-01 10:20:30'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19 AND a='2001-01-01 10:20:30'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19 AND a=20010101102030; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19 AND a=20010101102030e0; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19 AND a=20010101102030.0; + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19+RAND() AND a=TIMESTAMP'2001-01-01 10:20:30'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19+RAND() AND a='2001-01-01 10:20:30'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19+RAND() AND a=20010101102030; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19+RAND() AND a=20010101102030e0; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE LENGTH(a)=19+RAND() AND a=20010101102030.0; + +DROP TABLE t1; + +SET time_zone=DEFAULT; + +--echo # +--echo # End of 10.6 tests +--echo # + --echo # --echo # MDEV-29225 make explicit_defaults_for_timestamps SESSION variable --echo # diff --git a/mysql-test/suite/versioning/r/cte.result b/mysql-test/suite/versioning/r/cte.result index 11d478ac456ad..4a9eba5600672 100644 --- a/mysql-test/suite/versioning/r/cte.result +++ b/mysql-test/suite/versioning/r/cte.result @@ -40,7 +40,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 3 UNION e ALL NULL NULL NULL NULL 4 100.00 Using where NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 with ancestors as (/* select#2 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` where `test`.`e`.`name` = 'bill' and `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1` union /* select#3 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` where `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1`)/* select#1 */ select `ancestors`.`emp_id` AS `emp_id`,`ancestors`.`name` AS `name`,`ancestors`.`mgr` AS `mgr`,`ancestors`.`salary` AS `salary` from `ancestors` +Note 1003 with ancestors as (/* select#2 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` where `test`.`e`.`name` = 'bill' and `test`.`e`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`e`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' union /* select#3 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` where `test`.`e`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`e`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu')/* select#1 */ select `ancestors`.`emp_id` AS `emp_id`,`ancestors`.`name` AS `name`,`ancestors`.`mgr` AS `mgr`,`ancestors`.`salary` AS `salary` from `ancestors` select row_start into @ts_2 from emp where name="john"; explain extended /* All report to 'Bill' */ with recursive @@ -64,7 +64,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 3 RECURSIVE UNION ref key0 key0 5 test.e.mgr 1 100.00 NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 with recursive ancestors as (/* select#2 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` where `test`.`e`.`name` = 'bill' and `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1` union /* select#3 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` join `ancestors` `a` where `a`.`emp_id` = `test`.`e`.`mgr` and `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1`)/* select#1 */ select `ancestors`.`emp_id` AS `emp_id`,`ancestors`.`name` AS `name`,`ancestors`.`mgr` AS `mgr`,`ancestors`.`salary` AS `salary` from `ancestors` +Note 1003 with recursive ancestors as (/* select#2 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` where `test`.`e`.`name` = 'bill' and `test`.`e`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`e`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' union /* select#3 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` join `ancestors` `a` where `a`.`emp_id` = `test`.`e`.`mgr` and `test`.`e`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`e`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu')/* select#1 */ select `ancestors`.`emp_id` AS `emp_id`,`ancestors`.`name` AS `name`,`ancestors`.`mgr` AS `mgr`,`ancestors`.`salary` AS `salary` from `ancestors` /* All report to 'Bill' */ with recursive ancestors @@ -105,7 +105,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 3 RECURSIVE UNION ref key0 key0 5 test.e.mgr 1 100.00 NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 with recursive ancestors as (/* select#2 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` where `test`.`e`.`name` = 'bill' and `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1` union /* select#3 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` join `ancestors` `a` where `a`.`emp_id` = `test`.`e`.`mgr` and `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1`)/* select#1 */ select `ancestors`.`emp_id` AS `emp_id`,`ancestors`.`name` AS `name`,`ancestors`.`mgr` AS `mgr`,`ancestors`.`salary` AS `salary` from `ancestors` +Note 1003 with recursive ancestors as (/* select#2 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` where `test`.`e`.`name` = 'bill' and `test`.`e`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`e`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' union /* select#3 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` join `ancestors` `a` where `a`.`emp_id` = `test`.`e`.`mgr` and `test`.`e`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`e`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu')/* select#1 */ select `ancestors`.`emp_id` AS `emp_id`,`ancestors`.`name` AS `name`,`ancestors`.`mgr` AS `mgr`,`ancestors`.`salary` AS `salary` from `ancestors` with recursive ancestors as @@ -146,7 +146,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 3 RECURSIVE UNION ref key0 key0 5 test.e.mgr 1 100.00 NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 with recursive ancestors as (/* select#2 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` where `test`.`e`.`name` = 'bill' and `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1` union /* select#3 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` join `ancestors` `a` where `a`.`emp_id` = `test`.`e`.`mgr` and `test`.`e`.`row_end` > @`ts_1` and `test`.`e`.`row_start` <= @`ts_1`)/* select#1 */ select `test`.`emp`.`name` AS `name` from `test`.`emp` semi join (`ancestors`) where `ancestors`.`emp_id` = `test`.`emp`.`emp_id` and `test`.`emp`.`row_end` = TIMESTAMP'2038-01-19 03:14:07.999999' +Note 1003 with recursive ancestors as (/* select#2 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` where `test`.`e`.`name` = 'bill' and `test`.`e`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`e`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' union /* select#3 */ select `test`.`e`.`emp_id` AS `emp_id`,`test`.`e`.`name` AS `name`,`test`.`e`.`mgr` AS `mgr`,`test`.`e`.`salary` AS `salary` from `test`.`emp` FOR SYSTEM_TIME AS OF TIMESTAMP @`ts_1` `e` join `ancestors` `a` where `a`.`emp_id` = `test`.`e`.`mgr` and `test`.`e`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`e`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu')/* select#1 */ select `test`.`emp`.`name` AS `name` from `test`.`emp` semi join (`ancestors`) where `ancestors`.`emp_id` = `test`.`emp`.`emp_id` and `test`.`emp`.`row_end` = TIMESTAMP'2038-01-19 03:14:07.999999' with recursive ancestors as diff --git a/mysql-test/suite/versioning/r/derived.result b/mysql-test/suite/versioning/r/derived.result index 700c92a8d5c56..50cfb4020d0de 100644 --- a/mysql-test/suite/versioning/r/derived.result +++ b/mysql-test/suite/versioning/r/derived.result @@ -211,12 +211,12 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 Using where 1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where; Using join buffer (flat, BNL join) Query A: -Note 1003 select `test`.`t1`.`x` AS `x`,`test`.`t1`.`y` AS `y1`,`test`.`t2`.`x` AS `x2`,`test`.`t2`.`y` AS `y2` from `test`.`t1` FOR SYSTEM_TIME AS OF TIMESTAMP current_timestamp(6) join `test`.`t2` FOR SYSTEM_TIME AS OF TIMESTAMP current_timestamp(6) where `test`.`t1`.`x` = `test`.`t2`.`x` and `test`.`t2`.`row_end` > (current_timestamp(6)) and `test`.`t2`.`row_start` <= (current_timestamp(6)) and `test`.`t1`.`row_end` > (current_timestamp(6)) and `test`.`t1`.`row_start` <= (current_timestamp(6)) +Note 1003 select `test`.`t1`.`x` AS `x`,`test`.`t1`.`y` AS `y1`,`test`.`t2`.`x` AS `x2`,`test`.`t2`.`y` AS `y2` from `test`.`t1` FOR SYSTEM_TIME AS OF TIMESTAMP current_timestamp(6) join `test`.`t2` FOR SYSTEM_TIME AS OF TIMESTAMP current_timestamp(6) where `test`.`t1`.`x` = `test`.`t2`.`x` and `test`.`t2`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`t2`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`t1`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`t1`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 Using where 1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where; Using join buffer (flat, BNL join) Query B: -Note 1003 select `test`.`t1`.`x` AS `x`,`test`.`t1`.`y` AS `y1`,`test`.`t2`.`x` AS `x2`,`test`.`t2`.`y` AS `y2` from `test`.`t1` FOR SYSTEM_TIME AS OF TIMESTAMP current_timestamp(6) join `test`.`t2` FOR SYSTEM_TIME AS OF TIMESTAMP current_timestamp(6) where `test`.`t1`.`x` = `test`.`t2`.`x` and `test`.`t2`.`row_end` > (current_timestamp(6)) and `test`.`t2`.`row_start` <= (current_timestamp(6)) and `test`.`t1`.`row_end` > (current_timestamp(6)) and `test`.`t1`.`row_start` <= (current_timestamp(6)) +Note 1003 select `test`.`t1`.`x` AS `x`,`test`.`t1`.`y` AS `y1`,`test`.`t2`.`x` AS `x2`,`test`.`t2`.`y` AS `y2` from `test`.`t1` FOR SYSTEM_TIME AS OF TIMESTAMP current_timestamp(6) join `test`.`t2` FOR SYSTEM_TIME AS OF TIMESTAMP current_timestamp(6) where `test`.`t1`.`x` = `test`.`t2`.`x` and `test`.`t2`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`t2`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`t1`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`t1`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' Fine result: queries A and B are equal. ## LEFT JOIN: t1, t2 versioned select * from ( diff --git a/mysql-test/suite/versioning/r/select.result b/mysql-test/suite/versioning/r/select.result index 36ea54bb08e75..c4e8df1a74372 100644 --- a/mysql-test/suite/versioning/r/select.result +++ b/mysql-test/suite/versioning/r/select.result @@ -155,21 +155,21 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 Using where 1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where; Using join buffer (flat, BNL join) Warnings: -Note 1003 select `test`.`t1`.`x` AS `IJ2_x1`,`test`.`t1`.`y` AS `y1`,`test`.`t2`.`x` AS `x2`,`test`.`t2`.`y` AS `y2` from `test`.`t1` FOR SYSTEM_TIME AS OF TIMESTAMP @`t0` join `test`.`t2` FOR SYSTEM_TIME AS OF TIMESTAMP @`t0` where `test`.`t1`.`x` = `test`.`t2`.`x` and `test`.`t2`.`row_end` > @`t0` and `test`.`t2`.`row_start` <= @`t0` and `test`.`t1`.`row_end` > @`t0` and `test`.`t1`.`row_start` <= @`t0` +Note 1003 select `test`.`t1`.`x` AS `IJ2_x1`,`test`.`t1`.`y` AS `y1`,`test`.`t2`.`x` AS `x2`,`test`.`t2`.`y` AS `y2` from `test`.`t1` FOR SYSTEM_TIME AS OF TIMESTAMP @`t0` join `test`.`t2` FOR SYSTEM_TIME AS OF TIMESTAMP @`t0` where `test`.`t1`.`x` = `test`.`t2`.`x` and `test`.`t2`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`t2`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`t1`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`t1`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' explain extended select * from (select t1.x as LJ2_x1, t1.y as y1, t2.x as x2, t2.y as y2 from t1 left join t2 on t1.x = t2.x) for system_time as of timestamp @t0 as t; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where 1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 Using where; Using join buffer (flat, BNL join) Warnings: -Note 1003 select `test`.`t1`.`x` AS `LJ2_x1`,`test`.`t1`.`y` AS `y1`,`test`.`t2`.`x` AS `x2`,`test`.`t2`.`y` AS `y2` from `test`.`t1` FOR SYSTEM_TIME AS OF TIMESTAMP @`t0` left join `test`.`t2` FOR SYSTEM_TIME AS OF TIMESTAMP @`t0` on(`test`.`t2`.`x` = `test`.`t1`.`x` and `test`.`t2`.`row_end` > @`t0` and `test`.`t2`.`row_start` <= @`t0`) where `test`.`t1`.`row_end` > @`t0` and `test`.`t1`.`row_start` <= @`t0` +Note 1003 select `test`.`t1`.`x` AS `LJ2_x1`,`test`.`t1`.`y` AS `y1`,`test`.`t2`.`x` AS `x2`,`test`.`t2`.`y` AS `y2` from `test`.`t1` FOR SYSTEM_TIME AS OF TIMESTAMP @`t0` left join `test`.`t2` FOR SYSTEM_TIME AS OF TIMESTAMP @`t0` on(`test`.`t2`.`x` = `test`.`t1`.`x` and `test`.`t2`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`t2`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu') where `test`.`t1`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`t1`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' explain extended select * from (select t1.x as RJ2_x1, t1.y as y1, t2.x as x2, t2.y as y2 from t1 right join t2 on t1.x = t2.x) for system_time as of timestamp @t0 as t; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 Using where 1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where; Using join buffer (flat, BNL join) Warnings: -Note 1003 select `test`.`t1`.`x` AS `RJ2_x1`,`test`.`t1`.`y` AS `y1`,`test`.`t2`.`x` AS `x2`,`test`.`t2`.`y` AS `y2` from `test`.`t2` FOR SYSTEM_TIME AS OF TIMESTAMP @`t0` left join `test`.`t1` FOR SYSTEM_TIME AS OF TIMESTAMP @`t0` on(`test`.`t1`.`x` = `test`.`t2`.`x` and `test`.`t1`.`row_end` > @`t0` and `test`.`t1`.`row_start` <= @`t0`) where `test`.`t2`.`row_end` > @`t0` and `test`.`t2`.`row_start` <= @`t0` +Note 1003 select `test`.`t1`.`x` AS `RJ2_x1`,`test`.`t1`.`y` AS `y1`,`test`.`t2`.`x` AS `x2`,`test`.`t2`.`y` AS `y2` from `test`.`t2` FOR SYSTEM_TIME AS OF TIMESTAMP @`t0` left join `test`.`t1` FOR SYSTEM_TIME AS OF TIMESTAMP @`t0` on(`test`.`t1`.`x` = `test`.`t2`.`x` and `test`.`t1`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`t1`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu') where `test`.`t2`.`row_end` > TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' and `test`.`t2`.`row_start` <= TIMESTAMP/*WITH LOCAL TIME ZONE*/'YYYY-MM-DD hh:ss:mm:.uuuuuu' select * from (select t1.x as IJ2_x1, t1.y as y1, t2.x as x2, t2.y as y2 from t1 inner join t2 on t1.x = t2.x) for system_time as of timestamp @t0 as t; IJ2_x1 y1 x2 y2 diff --git a/mysql-test/suite/versioning/t/cte.test b/mysql-test/suite/versioning/t/cte.test index 025e1b2319d60..f57f7d14cefc3 100644 --- a/mysql-test/suite/versioning/t/cte.test +++ b/mysql-test/suite/versioning/t/cte.test @@ -5,6 +5,8 @@ if (`SELECT $PS_PROTOCOL != 0`) --source include/have_innodb.inc --source include/default_optimizer_switch.inc +--let $replace_regex_tsltz6= /TIMESTAMP..WITH LOCAL TIME ZONE..'....-..-.. ..:..:..[.]......'/TIMESTAMP\/*WITH LOCAL TIME ZONE*\/'YYYY-MM-DD hh:ss:mm:.uuuuuu'/ + SET @saved_stats_persistent = @@GLOBAL.innodb_stats_persistent; SET GLOBAL innodb_stats_persistent = OFF; @@ -42,6 +44,7 @@ select row_start into @ts_1 from emp where name="jane"; update emp set mgr=30 where name ="john"; +--replace_regex $replace_regex_tsltz6 explain extended with ancestors as ( select e.emp_id, e.name, e.mgr, e.salary from emp as e where name = 'bill' @@ -68,6 +71,7 @@ as ) select * from ancestors; +--replace_regex $replace_regex_tsltz6 eval explain extended $q; eval $q; @@ -86,6 +90,7 @@ as ) select * from ancestors for system_time as of timestamp @ts_1; +--replace_regex $replace_regex_tsltz6 eval explain extended $q; eval $q; @@ -104,6 +109,7 @@ as ) select name from emp where emp_id in (select emp_id from ancestors for system_time as of timestamp @ts_1); +--replace_regex $replace_regex_tsltz6 eval explain extended $q; eval $q; diff --git a/mysql-test/suite/versioning/t/derived.test b/mysql-test/suite/versioning/t/derived.test index 9d96856f01f71..f599ede97c154 100644 --- a/mysql-test/suite/versioning/t/derived.test +++ b/mysql-test/suite/versioning/t/derived.test @@ -1,5 +1,7 @@ --source include/default_optimizer_switch.inc +--let $replace_regex_tsltz6= /TIMESTAMP..WITH LOCAL TIME ZONE..'....-..-.. ..:..:..[.]......'/TIMESTAMP\/*WITH LOCAL TIME ZONE*\/'YYYY-MM-DD hh:ss:mm:.uuuuuu'/ + create table emp ( emp_id int, @@ -164,6 +166,7 @@ select * from ( select t1.x, t1.y as y1, t2.x as x2, t2.y as y2 from t1 join t2 on t1.x = t2.x) for system_time as of now() as t; +--replace_regex $replace_regex_tsltz6 let $a=`show warnings`; --echo Query A: echo $a; @@ -174,6 +177,7 @@ select * from ( from t1 for system_time as of now() join t2 for system_time as of now() on t1.x = t2.x) as t; +--replace_regex $replace_regex_tsltz6 let $b=`show warnings`; --echo Query B: echo $b; diff --git a/mysql-test/suite/versioning/t/select.test b/mysql-test/suite/versioning/t/select.test index 5603d1a3a12e3..b9a836f708e60 100644 --- a/mysql-test/suite/versioning/t/select.test +++ b/mysql-test/suite/versioning/t/select.test @@ -6,6 +6,8 @@ if (`SELECT $PS_PROTOCOL != 0`) --source suite/versioning/common.inc --source include/default_optimizer_switch.inc +--let $replace_regex_tsltz6= /TIMESTAMP..WITH LOCAL TIME ZONE..'....-..-.. ..:..:..[.]......'/TIMESTAMP\/*WITH LOCAL TIME ZONE*\/'YYYY-MM-DD hh:ss:mm:.uuuuuu'/ + SET @saved_stats_persistent = @@GLOBAL.innodb_stats_persistent; SET GLOBAL innodb_stats_persistent = OFF; @@ -98,11 +100,14 @@ delete from t1; delete from t2; #384 +--replace_regex $replace_regex_tsltz6 explain extended select * from (select t1.x as IJ2_x1, t1.y as y1, t2.x as x2, t2.y as y2 from t1 inner join t2 on t1.x = t2.x) for system_time as of timestamp @t0 as t; +--replace_regex $replace_regex_tsltz6 explain extended select * from (select t1.x as LJ2_x1, t1.y as y1, t2.x as x2, t2.y as y2 from t1 left join t2 on t1.x = t2.x) for system_time as of timestamp @t0 as t; #383 +--replace_regex $replace_regex_tsltz6 explain extended select * from (select t1.x as RJ2_x1, t1.y as y1, t2.x as x2, t2.y as y2 from t1 right join t2 on t1.x = t2.x) for system_time as of timestamp @t0 as t; diff --git a/sql/item.cc b/sql/item.cc index a385cb67f3009..f3e358f93e459 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -7332,6 +7332,17 @@ void Item_datetime_literal::print(String *str, enum_query_type query_type) } +void Item_timestamp_literal::print(String *str, enum_query_type query_type) +{ + str->append(STRING_WITH_LEN("TIMESTAMP/*WITH LOCAL TIME ZONE*/'")); + char buf[MAX_DATE_STRING_REP_LENGTH]; + Datetime dt= m_value.to_datetime(current_thd); + int length= my_datetime_to_str(dt.get_mysql_time(), buf, decimals); + str->append(buf, length); + str->append('\''); +} + + Item *Item_datetime_literal::clone_item(THD *thd) { return new (thd->mem_root) Item_datetime_literal(thd, &cached_time, decimals); diff --git a/sql/item.h b/sql/item.h index 16ba451442197..3915d8a7ca069 100644 --- a/sql/item.h +++ b/sql/item.h @@ -5005,9 +5005,21 @@ class Item_timestamp_literal: public Item_literal public: Item_timestamp_literal(THD *thd) :Item_literal(thd) - { } + { + collation= DTCollation_numeric(); + } + Item_timestamp_literal(THD *thd, + const Timestamp_or_zero_datetime &value, + decimal_digits_t dec) + :Item_literal(thd), + m_value(value) + { + collation= DTCollation_numeric(); + decimals= dec; + } const Type_handler *type_handler() const override { return &type_handler_timestamp2; } + void print(String *str, enum_query_type query_type) override; int save_in_field(Field *field, bool) override { Timestamp_or_zero_datetime_native native(m_value, decimals); @@ -5039,6 +5051,10 @@ class Item_timestamp_literal: public Item_literal { return m_value.to_native(to, decimals); } + const Timestamp_or_zero_datetime &value() const + { + return m_value; + } void set_value(const Timestamp_or_zero_datetime &value) { m_value= value; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index b324a8f5e6a38..d1891818cd6d0 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -392,6 +392,9 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item, This directly contradicts the manual (number and a string should be compared as doubles), but seems to provide more "intuitive" behavior in some cases (but less intuitive in others). + + This method should be moved to Type_handler::convert_item_for_comparison() + eventually. */ void Item_func::convert_const_compared_to_int_field(THD *thd) { @@ -412,6 +415,43 @@ void Item_func::convert_const_compared_to_int_field(THD *thd) } +bool Item_func::aggregate_args2_for_comparison_with_conversion( + THD *thd, + Type_handler_hybrid_field_type *th) +{ + DBUG_ASSERT(arg_count >= 2); + for (bool done= false ; !done ; ) + { + if (th->aggregate_for_comparison(func_name_cstring(), args, 2, false)) + return true; + if (thd->lex->is_ps_or_view_context_analysis()) + return false; + done= true; + for (uint subject= 0; subject < 2; subject++) + { + uint other_side= subject == 0 ? 1 : 0; + /* See comment in convert_const_to_int() */ + if (!args[subject]->with_sum_func() && + args[subject]->can_eval_in_optimize()) + { + Item *item= th->type_handler()->convert_item_for_comparison(thd, + args[subject], + args[other_side]); + if (!item) + return true; // An error happened, e.g. EOM + if (item != args[subject]) + { + thd->change_item_tree(&args[subject], item); + done= false; // Aggregate again, using the replacement item + break; + } + } + } + } + return false; +} + + /* Iterate through arguments and compare them to the original arguments in "old_args". If some argument was replaced: @@ -487,7 +527,7 @@ bool Item_bool_rowready_func2::fix_length_and_dec(THD *thd) Item_args old_args(args[0], args[1]); convert_const_compared_to_int_field(thd); Type_handler_hybrid_field_type tmp; - if (tmp.aggregate_for_comparison(func_name_cstring(), args, 2, false) || + if (aggregate_args2_for_comparison_with_conversion(thd, &tmp) || tmp.type_handler()->Item_bool_rowready_func2_fix_length_and_dec(thd, this)) { @@ -2804,7 +2844,10 @@ Item_func_nullif::fix_length_and_dec(THD *thd) set_maybe_null(); m_arg0= args[0]; convert_const_compared_to_int_field(thd); - if (cmp.set_cmp_func(thd, this, &args[0], &args[1], true/*set_null*/)) + Type_handler_hybrid_field_type tmp; + if (aggregate_args2_for_comparison_with_conversion(thd, &tmp) || + cmp.set_cmp_func(thd, this, tmp.type_handler(), + &args[0], &args[1], true/*set_null*/)) return true; /* A special code for EXECUTE..PREPARE. diff --git a/sql/item_func.h b/sql/item_func.h index e1d67a73134ae..5ec0e0f060861 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -83,6 +83,8 @@ class Item_func :public Item_func_or_sum return print_sql_mode_qualified_name(to, query_type, func_name_cstring()); } + bool aggregate_args2_for_comparison_with_conversion(THD *thd, + Type_handler_hybrid_field_type *th); public: // Print an error message for a builtin-schema qualified function call diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 9535bf1fee52c..6d290995c99ac 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1207,6 +1207,63 @@ void thd_gmt_sec_to_TIME(MYSQL_THD thd, MYSQL_TIME *ltime, my_time_t t) } +/* + @brief + Convert a non-zero DATETIME to its safe timeval based representation, + which guarantees a safe roundtrip DATETIME->timeval->DATETIME, + e.g. to optimize: + + WHERE timestamp_arg0 = datetime_arg1 + + in the way that we replace "datetime_arg1" to its TIMESTAMP equivalent + "timestamp_arg1" and switch from DATETIME comparison to TIMESTAMP comparison: + + WHERE timestamp_arg0 = timestamp_arg1 + + This helps to avoid slow TIMESTAMP->DATETIME data type conversion + for timestamp_arg0 per row. + + @detail + Round trip is possible if the input "YYYY-MM-DD hh:mm:ss" value + satisfies the following conditions: + + 1. TIME_to_gmt_sec() returns no errors or warnings, + which means the input value + a. has no zeros in YYYYMMDD + b. fits into the TIMESTAMP range + c. does not fall into the spring forward gap + (because values inside gaps get adjusted to the beginning of the gap) + + 2. The my_time_t value returned by TIME_to_gmt_sec() must not be + near a DST change or near a leap second, to avoid anomalies: + - "YYYY-MM-DD hh:mm:ss" has more than one matching my_time_t values + - "YYYY-MM-DD hh:mm:ss" has no matching my_time_t values + (e.g. fall into the spring forward gap) + + @param dt The DATETIME value to convert. + Must not be zero '0000-00-00 00:00:00.000000'. + (The zero value must be handled by the caller). + + @return The conversion result + @retval If succeeded, non-NULL Timeval value. + @retval Timeval_null value representing SQL NULL if the argument + does not have a safe replacement. +*/ +Timeval_null +THD::safe_timeval_replacement_for_nonzero_datetime(const Datetime &dt) +{ + used|= THD::TIME_ZONE_USED; + DBUG_ASSERT(non_zero_date(dt.get_mysql_time())); + uint error= 0; + const MYSQL_TIME *ltime= dt.get_mysql_time(); + my_time_t sec= variables.time_zone->TIME_to_gmt_sec(ltime, &error); + if (error /* (1) */ || + !variables.time_zone->is_monotone_continuous_around(sec) /* (2) */) + return Timeval_null(); + return Timeval_null(sec, ltime->second_part); +} + + #ifdef _WIN32 extern "C" my_thread_id next_thread_id_noinline() { diff --git a/sql/sql_class.h b/sql/sql_class.h index bca0edd859ed9..2b959196db8d1 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -4283,6 +4283,8 @@ class THD: public THD_count, /* this must be first */ utime_after_query= current_utime(); } + Timeval_null safe_timeval_replacement_for_nonzero_datetime(const Datetime &); + /** Update server status after execution of a top level statement. Currently only checks if a query was slow, and assigns diff --git a/sql/sql_type.cc b/sql/sql_type.cc index c68809ec784af..926d83c3fc943 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -7686,6 +7686,104 @@ Item *Type_handler_row:: return NULL; } +/***************************************************************************/ + +/* + Check if in a predicate like: + + WHERE timestamp_arg=datetime_arg + + we can replace DATETIME comparison to TIMESTAMP comparison, + to avoid slow TIMESTAMP->DATETIME data type conversion per row. + + TIMESTAMP and DATETIME are compared as DATETIME historically. + This may be inefficient, because involves a conversion of + the TIMESTAMP side to DATETIME per row. + The conversion happens in Timezone::gmt_sec_to_TIME(). + E.g. in case of the SYSTEM timezone, it calls localtime_r(), + which is known to be slow. + + It's generally not possible to compare TIMESTAMP and DATETIME + as TIMESTAMP without behavior change, because: + - DATETIME has a wider range. + - Two different TIMESTAMP values can have the same DATETIME value + near the "fall back" DST change, as well as for leap seconds. + - There are DATETIME gaps during the "spring forward" DST switch. + + However, if the DATETIME side is a constant, then we can compare + it to TIMESTAMP as TIMESTAMP in many cases. The DATETIME argument can + be converted once to TIMESTAMP, so no data type conversion will + happen per row. This is faster for big tables. + + The comparison predicates must satisfy the following conditions: + 1. There must be a proper data type combination: + - other must be of the TIMESTAMP data type + - subject must be of the DATETIME data type, + or can convert to DATETIME. + 2. subject must be a constant + 3. subject must convert to TIMESTAMP safely + (without time zone anomalies near its value) +*/ + +Item * +Type_handler_datetime_common::convert_item_for_comparison( + THD *thd, + Item *subject, + const Item *counterpart) + const +{ + if (!dynamic_cast( + counterpart->type_handler()) || + !subject->type_handler()->can_return_date()) + return subject; + + struct Count_handler : public Internal_error_handler + { + uint hit= 0; + bool handle_condition(THD *thd, + uint sql_errno, + const char *sqlstate, + Sql_condition::enum_warning_level *level, + const char *msg, + Sql_condition **cond_hdl) + { + hit++; + return *level == Sql_condition::WARN_LEVEL_WARN; + } + } cnt_handler; + + // Suppress and count warnings + thd->push_internal_handler(&cnt_handler); + Datetime dt(thd, subject, Timestamp::DatetimeOptions(thd)); + thd->pop_internal_handler(); + + if (!dt.is_valid_datetime() || cnt_handler.hit) + { + /* + SQL NULL DATETIME, or a DATETIME with zeros in YYYYMMDD, + or warnings during DATETIME evaluation. + */ + return subject; + } + + // '0000-00-00 00:00:00' is a special valid MariaDB TIMESTAMP value + if (!non_zero_date(dt.get_mysql_time())) + return new (thd->mem_root) Item_timestamp_literal(thd, + Timestamp_or_zero_datetime::zero(), + subject->datetime_precision(thd)); + + const Timeval_null tv(thd->safe_timeval_replacement_for_nonzero_datetime(dt)); + if (tv.is_null()) + return subject; // Time zone anomalies found around "dt" + + // Should be safe to convert + const Timestamp_or_zero_datetime ts(Timestamp(tv.to_timeval())); + return new (thd->mem_root) Item_timestamp_literal(thd, + ts, + subject->datetime_precision(thd)); +} + + /***************************************************************************/ static const char* item_name(Item *a, String *str) @@ -8686,6 +8784,43 @@ Type_handler_temporal_result::Item_const_eq(const Item_const *a, b->get_type_all_attributes_from_const()->decimals); } + +/* + @brief + Check if two costant timestamp values are identical. + + @return + true <=> *a and *b are identical +*/ +bool +Type_handler_timestamp_common::Item_const_eq(const Item_const *a, + const Item_const *b, + bool binary_cmp) const +{ + /* + In a condition like: + WHERE IF(a='2001-01-01 00:00:00',1,0)=IF(a='2001-01-01 00:00:00',1,0); + Item_func_eq::fix_length_and_dec() calls get_timestamp_item_for_comparison() + which replaces string literals '2001-01-01 00:00:00' with + Item_timestamp_literal instances, which later during remove_eq_conds() + come to here. + + Note, Item_param bound to TIMESTAMP is not detected here, + so trivial conditions of this kind do not get eliminated: + DECLARE ts TIMESTAMP DEFAULT (SELECT MAX(ts_col) FROM t1); + EXECUTE IMMEDIATE + 'SELECT * FROM t1 WHERE COALESCE(ts_col,?)<=>COALESCE(ts_col,?)' + USING ts, ts; + It should be fixed by MDEV-14271. + */ + const Item_timestamp_literal *ta, *tb; + if (!(ta= dynamic_cast(a)) || + !(tb= dynamic_cast(b))) + return false; + return !ta->value().cmp(tb->value()); +} + + /***************************************************************************/ const Type_handler * diff --git a/sql/sql_type.h b/sql/sql_type.h index 8215a3ee75930..d41282ff9f914 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -2837,6 +2837,11 @@ class Timestamp: protected Timeval class Timestamp_or_zero_datetime: protected Timestamp { bool m_is_zero_datetime; +public: + static Timestamp_or_zero_datetime zero() + { + return Timestamp_or_zero_datetime(Timestamp(0, 0), true); + } public: Timestamp_or_zero_datetime() :Timestamp(0,0), m_is_zero_datetime(true) @@ -2845,7 +2850,7 @@ class Timestamp_or_zero_datetime: protected Timestamp :Timestamp(native.length() ? Timestamp(native) : Timestamp(0,0)), m_is_zero_datetime(native.length() == 0) { } - Timestamp_or_zero_datetime(const Timestamp &tm, bool is_zero_datetime) + Timestamp_or_zero_datetime(const Timestamp &tm, bool is_zero_datetime= false) :Timestamp(tm), m_is_zero_datetime(is_zero_datetime) { } Timestamp_or_zero_datetime(THD *thd, const MYSQL_TIME *ltime, uint *err_code); @@ -2869,6 +2874,11 @@ class Timestamp_or_zero_datetime: protected Timestamp return 1; return Timestamp::cmp(other); } + const Timestamp &to_timestamp() const + { + DBUG_ASSERT(!is_zero_datetime()); + return *this; + } bool to_TIME(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate) const; /* Convert to native format: @@ -4206,6 +4216,33 @@ class Type_handler virtual Item *make_const_item_for_comparison(THD *thd, Item *src, const Item *cmp) const= 0; + /** + When aggregating function arguments for comparison + (e.g. for =, <, >, <=, >=, NULLIF), in some cases we rewrite + arguments. For example, if the predicate + timestamp_expr0 = datetime_const_expr1 + decides to compare arguments as DATETIME, + we can try to rewrite datetime_const_expr1 to a TIMESTAMP constant + and perform the comparison as TIMESTAMP, which is faster because + does not have to perform TIMESTAMP->DATETIME data type conversion per row. + + "this" is the type handler that is used to compare + "subject" and "counterpart" (DATETIME in the above example). + @param thd the current thread + @param subject the comparison side that we want try to rewrite + @param counterpart the other comparison side + @retval subject, if the subject does not need to be rewritten + @retval NULL in case of error (e.g. EOM) + @retval Otherwise, a pointer to a new Item which can + be used as a replacement for the subject. + */ + virtual Item *convert_item_for_comparison(THD *thd, + Item *subject, + const Item *counterpart) const + { + return subject; + } + virtual Item_cache *Item_get_cache(THD *thd, const Item *item) const= 0; virtual Item *make_constructor_item(THD *thd, List *args) const { @@ -6524,6 +6561,9 @@ class Type_handler_datetime_common: public Type_handler_temporal_with_date } String *print_item_value(THD *thd, Item *item, String *str) const override; Item_cache *Item_get_cache(THD *thd, const Item *item) const override; + Item *convert_item_for_comparison(THD *thd, + Item *subject, + const Item *counterpart) const override; String *Item_func_min_max_val_str(Item_func_min_max *, String *) const override; double Item_func_min_max_val_real(Item_func_min_max *) const override; longlong Item_func_min_max_val_int(Item_func_min_max *) const override; @@ -6695,6 +6735,8 @@ class Type_handler_timestamp_common: public Type_handler_temporal_with_date my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, my_decimal *) const override; bool set_comparator_func(THD *thd, Arg_comparator *cmp) const override; + bool Item_const_eq(const Item_const *a, const Item_const *b, + bool binary_cmp) const override; bool Item_hybrid_func_fix_attributes(THD *thd, const LEX_CSTRING &name, Type_handler_hybrid_field_type *, diff --git a/sql/structs.h b/sql/structs.h index c8351efd8bfc0..029543ceb0117 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -1035,4 +1035,29 @@ class Timeval: public timeval }; +/* + A value that's either a Timeval or SQL NULL +*/ + +class Timeval_null: protected Timeval +{ + bool m_is_null; +public: + Timeval_null() + :Timeval(0, 0), + m_is_null(true) + { } + Timeval_null(const my_time_t sec, ulong usec) + :Timeval(sec, usec), + m_is_null(false) + { } + const Timeval & to_timeval() const + { + DBUG_ASSERT(!m_is_null); + return *this; + } + bool is_null() const { return m_is_null; } +}; + + #endif /* STRUCTS_INCLUDED */ diff --git a/sql/tztime.cc b/sql/tztime.cc index 9a1f8c6fc52b5..0ccf3cc76eb90 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -2426,6 +2426,59 @@ void Time_zone::adjust_leap_second(MYSQL_TIME *t) t->second= 59; } + +/* + @brief + Check if timestamp<->datetime conversion is guaranteed to be reversible + around the given time value. +*/ + +bool Time_zone::is_monotone_continuous_around(my_time_t sec) const +{ + const my_time_t width= 24 * 60 * 60; + /* + Let's check that the TIMESTAMP range [sec - width, sec + width] + converts back to a DATETIME range [dtmin, dtmax], and the difference + (dtmax-dtmin), calculated using DATETIME arithmetics, + is also equal to exactly 2*width. + + This should almost guarantee that the TIMESTAMP range around sec + is monotone and continuous and thus has no DST changes or leap seconds. + + It's still possible that there are two or more timezone offset changes + in the given range. But there are no such time zones according to + our knowledge. + Note, DST changes and leap seconds do not interfere on a short time_t range: + - Leap seconds happen in winter and summer + - DST changes happen in spring and fall + + The largest time zone change that ever happened in a single place + was 24 hours: + - from SST (UTC-11) - the American Samoa Time Zone + - to WST (UTC+13) - the Independent State of Samoa Time Zone + The Independent State of Samoa used SST until it moved across + the International Date Line at the end of 29 December 2011, to WST. + It is now 24 hours ahead of American Samoa + (and 25 hours in Southern hemisphere summer). + Let's use 24 hours as width (48 hours range total). + */ + + if (sec < width || sec > TIMESTAMP_MAX_VALUE - width) + return false; + + MYSQL_TIME dtmin, dtmax; + gmt_sec_to_TIME(&dtmin, sec - width); + gmt_sec_to_TIME(&dtmax, sec + width); + + ulonglong seconds; + ulong useconds; + if (calc_time_diff(&dtmax, &dtmin, 1, &seconds, &useconds)) + return false; // dtmax is smaller than dtmin, should not happen. + if (seconds != (ulonglong) width * 2) + return false; // Anomalies found (DST changes or leap seconds) + return true; +} + #endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */ diff --git a/sql/tztime.h b/sql/tztime.h index 3dabbe8d11834..3c394014946df 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -80,6 +80,13 @@ class Time_zone: public Sql_alloc */ virtual ~Time_zone() = default; + /** + Check if the time zone does not have any anomalies around "sec", such as: + - DST changes (spring forward, fall back) + - leap seconds (the 60-th second) + */ + bool is_monotone_continuous_around(my_time_t sec) const; + protected: static inline void adjust_leap_second(MYSQL_TIME *t); }; diff --git a/storage/spider/mysql-test/spider/r/timestamp.result b/storage/spider/mysql-test/spider/r/timestamp.result index 4618d9207bf87..a717860ab6c32 100644 --- a/storage/spider/mysql-test/spider/r/timestamp.result +++ b/storage/spider/mysql-test/spider/r/timestamp.result @@ -257,7 +257,7 @@ select t0.`col_a` `col_a`,t0.`col_dt` `col_dt`,t0.`col_ts` `col_ts`,(unix_timest select t0.`col_a` `col_a`,t0.`col_dt` `col_dt`,t0.`col_ts` `col_ts`,(unix_timestamp(t0.`col_ts`)) `unix_timestamp(col_ts)` from `ts_test_remote`.`tbl_a` t0 where (_latin1'2018-10-28 01:30:00' > t0.`col_ts`) select t0.`col_a` `col_a`,t0.`col_dt` `col_dt`,t0.`col_ts` `col_ts`,(unix_timestamp(t0.`col_ts`)) `unix_timestamp(col_ts)` from `ts_test_remote`.`tbl_a` t0 where (t0.`col_ts` between _latin1'2018-10-28 00:30:00' and _latin1'2018-10-28 01:30:00') select t0.`col_a` `col_a`,t0.`col_dt` `col_dt`,t0.`col_ts` `col_ts`,(unix_timestamp(t0.`col_ts`)) `unix_timestamp(col_ts)` from `ts_test_remote`.`tbl_a` t0 where ((t0.`col_ts` >= _latin1'2018-10-28 00:30:00') and (t0.`col_ts` <= _latin1'2018-10-28 01:30:00')) -select t0.`col_a` `col_a`,t0.`col_dt` `col_dt`,t0.`col_ts` `col_ts`,(unix_timestamp(t0.`col_ts`)) `unix_timestamp(col_ts)` from `ts_test_remote`.`tbl_a` t0 where (t0.`col_ts` > '2018-03-25 01:00:00') +select t0.`col_a` `col_a`,t0.`col_dt` `col_dt`,t0.`col_ts` `col_ts`,(unix_timestamp(t0.`col_ts`)) `unix_timestamp(col_ts)` from `ts_test_remote`.`tbl_a` t0 where (t0.`col_ts` > _latin1'2018-03-25 01:00:00') select t0.`col_a` `col_a`,t0.`col_dt` `col_dt`,t0.`col_ts` `col_ts`,(unix_timestamp(t0.`col_ts`)) `unix_timestamp(col_ts)` from `ts_test_remote`.`tbl_a` t0 where (t0.`col_ts` > '1970-01-01 00:00:01') SELECT argument FROM mysql.general_log WHERE command_type != 'Execute' AND argument LIKE '%select %' SELECT col_a, col_dt, col_ts, unix_timestamp(col_ts) FROM tbl_a ORDER BY col_a; @@ -344,7 +344,7 @@ select t0.`col_a` `col_a`,t0.`col_dt` `col_dt`,t0.`col_ts` `col_ts`,(unix_timest select t0.`col_a` `col_a`,t0.`col_dt` `col_dt`,t0.`col_ts` `col_ts`,(unix_timestamp(t0.`col_ts`)) `unix_timestamp(col_ts)` from `ts_test_remote`.`tbl_a` t0 where (_latin1'2018-10-28 01:30:00' > t0.`col_ts`) select t0.`col_a` `col_a`,t0.`col_dt` `col_dt`,t0.`col_ts` `col_ts`,(unix_timestamp(t0.`col_ts`)) `unix_timestamp(col_ts)` from `ts_test_remote`.`tbl_a` t0 where (t0.`col_ts` between _latin1'2018-10-28 00:30:00' and _latin1'2018-10-28 01:30:00') select t0.`col_a` `col_a`,t0.`col_dt` `col_dt`,t0.`col_ts` `col_ts`,(unix_timestamp(t0.`col_ts`)) `unix_timestamp(col_ts)` from `ts_test_remote`.`tbl_a` t0 where ((t0.`col_ts` >= _latin1'2018-10-28 00:30:00') and (t0.`col_ts` <= _latin1'2018-10-28 01:30:00')) -select t0.`col_a` `col_a`,t0.`col_dt` `col_dt`,t0.`col_ts` `col_ts`,(unix_timestamp(t0.`col_ts`)) `unix_timestamp(col_ts)` from `ts_test_remote`.`tbl_a` t0 where (t0.`col_ts` > '2018-03-25 01:00:00') +select t0.`col_a` `col_a`,t0.`col_dt` `col_dt`,t0.`col_ts` `col_ts`,(unix_timestamp(t0.`col_ts`)) `unix_timestamp(col_ts)` from `ts_test_remote`.`tbl_a` t0 where (t0.`col_ts` > _latin1'2018-03-25 01:00:00') select t0.`col_a` `col_a`,t0.`col_dt` `col_dt`,t0.`col_ts` `col_ts`,(unix_timestamp(t0.`col_ts`)) `unix_timestamp(col_ts)` from `ts_test_remote`.`tbl_a` t0 where (t0.`col_ts` > '1970-01-01 00:00:01') SELECT argument FROM mysql.general_log WHERE command_type != 'Execute' AND argument LIKE '%select %' SELECT col_a, col_dt, col_ts, unix_timestamp(col_ts) FROM tbl_a ORDER BY col_a; From 8b5c1d5afa33521d7a5024b58a7b8197e60bfd33 Mon Sep 17 00:00:00 2001 From: Anel Husakovic Date: Fri, 12 Jan 2024 15:20:25 +0100 Subject: [PATCH 024/129] Revert "MDEV-32235: mysql_json cannot be used on newly created table" This reverts commit 22f3ebe4bf11acb7b5e854bc4e6b3439af6982f1. --- .../main/mysql_json_table_recreate.result | 18 ++++++------------ mysql-test/main/mysql_json_table_recreate.test | 17 ++--------------- sql/sql_table.cc | 13 ------------- 3 files changed, 8 insertions(+), 40 deletions(-) diff --git a/mysql-test/main/mysql_json_table_recreate.result b/mysql-test/main/mysql_json_table_recreate.result index a0c2483d3d924..b2ab19f4fbf5f 100644 --- a/mysql-test/main/mysql_json_table_recreate.result +++ b/mysql-test/main/mysql_json_table_recreate.result @@ -181,7 +181,12 @@ t1 CREATE TABLE `t1` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci drop table t1; create table t1(j mysql_json); -ERROR HY000: Cannot create table `test`.`t1`: Run mariadb-upgrade, to upgrade table with mysql_json type. +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `j` mysql_json /* JSON from MySQL 5.7 */ CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +drop table t1; create table `testjson` ( `t` json /* JSON from MySQL 5.7*/ CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci; @@ -206,16 +211,5 @@ testjson CREATE TABLE `testjson` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci drop table testjson; # -# MDEV-32235: mysql_json cannot be used on newly created table -# -CREATE TABLE t(j mysql_json); -ERROR HY000: Cannot create table `test`.`t`: Run mariadb-upgrade, to upgrade table with mysql_json type. -CREATE TABLE IF NOT EXISTS t(j mysql_json); -ERROR HY000: Cannot create table `test`.`t`: Run mariadb-upgrade, to upgrade table with mysql_json type. -CREATE OR REPLACE TABLE t(j mysql_json); -ERROR HY000: Cannot create table `test`.`t`: Run mariadb-upgrade, to upgrade table with mysql_json type. -CREATE TEMPORARY TABLE t(j mysql_json); -ERROR HY000: Cannot create table `test`.`t`: Run mariadb-upgrade, to upgrade table with mysql_json type. -# # End of 10.5 tests # diff --git a/mysql-test/main/mysql_json_table_recreate.test b/mysql-test/main/mysql_json_table_recreate.test index bf4cac0f90955..7e9895a1f107f 100644 --- a/mysql-test/main/mysql_json_table_recreate.test +++ b/mysql-test/main/mysql_json_table_recreate.test @@ -97,8 +97,9 @@ drop table mysql_json_test_big; create table t1(j json); show create table t1; drop table t1; ---error ER_CANT_CREATE_TABLE create table t1(j mysql_json); +show create table t1; +drop table t1; # `json` type should not have character set and collation other than utf8mb4_bin --error ER_PARSE_ERROR create table `testjson` ( @@ -120,20 +121,6 @@ create table `testjson` ( show create table testjson; drop table testjson; - ---echo # ---echo # MDEV-32235: mysql_json cannot be used on newly created table ---echo # - ---error ER_CANT_CREATE_TABLE -CREATE TABLE t(j mysql_json); ---error ER_CANT_CREATE_TABLE -CREATE TABLE IF NOT EXISTS t(j mysql_json); ---error ER_CANT_CREATE_TABLE -CREATE OR REPLACE TABLE t(j mysql_json); ---error ER_CANT_CREATE_TABLE -CREATE TEMPORARY TABLE t(j mysql_json); - --echo # --echo # End of 10.5 tests --echo # diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 8088d3e0d70e3..d68bbd1ac4f69 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3657,19 +3657,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (sql_field->vcol_info) sql_field->flags&= ~NOT_NULL_FLAG; - if (sql_field->real_field_type() == MYSQL_TYPE_BLOB && - thd->lex->sql_command == SQLCOM_CREATE_TABLE) - { - if (!strcmp(sql_field->type_handler()->name().ptr(), "MYSQL_JSON")) - { - my_printf_error(ER_CANT_CREATE_TABLE, - "Cannot create table %`s.%`s: " - "Run mariadb-upgrade, " - "to upgrade table with mysql_json type.", MYF(0), - alter_info->db.str, alter_info->table_name.str); - DBUG_RETURN(TRUE); - } - } /* Initialize length from its original value (number of characters), which was set in the parser. This is necessary if we're From 8a763c014ede4adad9c84852269b5af61845c0d9 Mon Sep 17 00:00:00 2001 From: Anel Husakovic Date: Fri, 12 Jan 2024 15:39:38 +0100 Subject: [PATCH 025/129] MDEV-32235: mysql_json cannot be used on newly created table - Closes PR #2839 - Usage of `Column_definition_fix_attributes()` suggested by Alexandar Barkov - thanks bar, that is better than hook in server code (reverted 22f3ebe4bf11) - This method is called after parsing the data type: * in `CREATE/ALTER TABLE` * in SP: return data type, parameter data type, variable data type - We want to disallow all these use cases of MYSQL_JSON. - Reviewer: bar@mariadb.com cvicentiu@mariadb.org --- .../main/mysql_json_table_recreate.result | 38 ++++++++++++++--- .../main/mysql_json_table_recreate.test | 41 ++++++++++++++++++- plugin/type_mysql_json/type.cc | 5 +++ 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/mysql-test/main/mysql_json_table_recreate.result b/mysql-test/main/mysql_json_table_recreate.result index b2ab19f4fbf5f..a61377fe21d57 100644 --- a/mysql-test/main/mysql_json_table_recreate.result +++ b/mysql-test/main/mysql_json_table_recreate.result @@ -30,6 +30,12 @@ show create table mysql_json_test; ERROR HY000: Table rebuild required. Please do "ALTER TABLE `test.mysql_json_test` FORCE" or dump/reload to fix it! select * from mysql_json_test; ERROR HY000: Table rebuild required. Please do "ALTER TABLE `test.mysql_json_test` FORCE" or dump/reload to fix it! +CREATE TABLE t2 AS SELECT * FROM mysql_json_test; +ERROR HY000: Table rebuild required. Please do "ALTER TABLE `test.mysql_json_test` FORCE" or dump/reload to fix it! +CREATE TABLE t2 (a mysql_json /*new column*/) AS SELECT * FROM mysql_json_test; +ERROR HY000: 'MYSQL_JSON' is not allowed in this context +CREATE TABLE t2 (actual mysql_json /*existing column*/) AS SELECT * FROM mysql_json_test; +ERROR HY000: 'MYSQL_JSON' is not allowed in this context LOCK TABLES mysql_json_test WRITE; ERROR HY000: Table rebuild required. Please do "ALTER TABLE `test.mysql_json_test` FORCE" or dump/reload to fix it! alter table mysql_json_test force; @@ -181,12 +187,7 @@ t1 CREATE TABLE `t1` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci drop table t1; create table t1(j mysql_json); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `j` mysql_json /* JSON from MySQL 5.7 */ CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci -drop table t1; +ERROR HY000: 'MYSQL_JSON' is not allowed in this context create table `testjson` ( `t` json /* JSON from MySQL 5.7*/ CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci; @@ -211,5 +212,30 @@ testjson CREATE TABLE `testjson` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci drop table testjson; # +# MDEV-32235: mysql_json cannot be used on newly created table +# +CREATE TABLE t(j mysql_json); +ERROR HY000: 'MYSQL_JSON' is not allowed in this context +CREATE TABLE IF NOT EXISTS t(j mysql_json); +ERROR HY000: 'MYSQL_JSON' is not allowed in this context +CREATE OR REPLACE TABLE t(j mysql_json); +ERROR HY000: 'MYSQL_JSON' is not allowed in this context +CREATE TEMPORARY TABLE t(j mysql_json); +ERROR HY000: 'MYSQL_JSON' is not allowed in this context +CREATE TABLE t1 (a TEXT); +ALTER TABLE t1 MODIFY a mysql_json; +ERROR HY000: 'MYSQL_JSON' is not allowed in this context +DROP TABLE t1; +CREATE FUNCTION f1() RETURNS mysql_json RETURN NULL; +ERROR HY000: 'MYSQL_JSON' is not allowed in this context +CREATE FUNCTION f1(a mysql_json) RETURNS INT RETURN 0; +ERROR HY000: 'MYSQL_JSON' is not allowed in this context +CREATE PROCEDURE p1() +BEGIN +DECLARE a mysql_json; +END; +$$ +ERROR HY000: 'MYSQL_JSON' is not allowed in this context +# # End of 10.5 tests # diff --git a/mysql-test/main/mysql_json_table_recreate.test b/mysql-test/main/mysql_json_table_recreate.test index 7e9895a1f107f..a6f1d3194aeb1 100644 --- a/mysql-test/main/mysql_json_table_recreate.test +++ b/mysql-test/main/mysql_json_table_recreate.test @@ -51,6 +51,13 @@ show create table mysql_json_test; --error ER_TABLE_NEEDS_REBUILD select * from mysql_json_test; +--error ER_TABLE_NEEDS_REBUILD +CREATE TABLE t2 AS SELECT * FROM mysql_json_test; +--error ER_NOT_ALLOWED_IN_THIS_CONTEXT +CREATE TABLE t2 (a mysql_json /*new column*/) AS SELECT * FROM mysql_json_test; +--error ER_NOT_ALLOWED_IN_THIS_CONTEXT +CREATE TABLE t2 (actual mysql_json /*existing column*/) AS SELECT * FROM mysql_json_test; + --error ER_TABLE_NEEDS_REBUILD LOCK TABLES mysql_json_test WRITE; @@ -97,9 +104,8 @@ drop table mysql_json_test_big; create table t1(j json); show create table t1; drop table t1; +--error ER_NOT_ALLOWED_IN_THIS_CONTEXT create table t1(j mysql_json); -show create table t1; -drop table t1; # `json` type should not have character set and collation other than utf8mb4_bin --error ER_PARSE_ERROR create table `testjson` ( @@ -121,6 +127,37 @@ create table `testjson` ( show create table testjson; drop table testjson; +--echo # +--echo # MDEV-32235: mysql_json cannot be used on newly created table +--echo # + +--error ER_NOT_ALLOWED_IN_THIS_CONTEXT +CREATE TABLE t(j mysql_json); +--error ER_NOT_ALLOWED_IN_THIS_CONTEXT +CREATE TABLE IF NOT EXISTS t(j mysql_json); +--error ER_NOT_ALLOWED_IN_THIS_CONTEXT +CREATE OR REPLACE TABLE t(j mysql_json); +--error ER_NOT_ALLOWED_IN_THIS_CONTEXT +CREATE TEMPORARY TABLE t(j mysql_json); + +CREATE TABLE t1 (a TEXT); +--error ER_NOT_ALLOWED_IN_THIS_CONTEXT +ALTER TABLE t1 MODIFY a mysql_json; +DROP TABLE t1; + +--error ER_NOT_ALLOWED_IN_THIS_CONTEXT +CREATE FUNCTION f1() RETURNS mysql_json RETURN NULL; +--error ER_NOT_ALLOWED_IN_THIS_CONTEXT +CREATE FUNCTION f1(a mysql_json) RETURNS INT RETURN 0; +DELIMITER $$; +--error ER_NOT_ALLOWED_IN_THIS_CONTEXT +CREATE PROCEDURE p1() +BEGIN + DECLARE a mysql_json; +END; +$$ +DELIMITER ;$$ + --echo # --echo # End of 10.5 tests --echo # diff --git a/plugin/type_mysql_json/type.cc b/plugin/type_mysql_json/type.cc index c915e0312bbc3..f84f76381a09a 100644 --- a/plugin/type_mysql_json/type.cc +++ b/plugin/type_mysql_json/type.cc @@ -37,6 +37,11 @@ class Type_handler_mysql_json: public Type_handler_blob Field *make_table_field(MEM_ROOT *, const LEX_CSTRING *, const Record_addr &, const Type_all_attributes &, TABLE_SHARE *) const override; + bool Column_definition_fix_attributes(Column_definition *c) const override + { + my_error(ER_NOT_ALLOWED_IN_THIS_CONTEXT, MYF(0), "MYSQL_JSON"); + return true; + } void Column_definition_reuse_fix_attributes(THD *thd, Column_definition *def, const Field *field) const override; From 5b0a4159ef1717545ccd37c08db0cba0eef00e5a Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Fri, 5 Jan 2024 13:44:49 +0100 Subject: [PATCH 026/129] Fix test failures on s390x in test following main.column_compression_rpl The problem is the test is skipped after sourcing include/master-slave.inc. This leaves the slave threads running after the test is skipped, causing a following test to fail during rpl setup. Also rename have_normal_bzip.inc to the more appropriate _zlib. Signed-off-by: Kristian Nielsen --- .../include/{have_normal_bzip.inc => have_normal_zlib.inc} | 4 ++-- mysql-test/main/column_compression.test | 2 +- mysql-test/main/column_compression_rpl.test | 2 +- mysql-test/main/func_compress.test | 2 +- mysql-test/main/mysqlbinlog_row_compressed.test | 2 +- mysql-test/main/mysqlbinlog_stmt_compressed.test | 2 +- mysql-test/suite/compat/oracle/t/column_compression.test | 2 +- mysql-test/suite/innodb/t/row_size_error_log_warnings_3.test | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) rename mysql-test/include/{have_normal_bzip.inc => have_normal_zlib.inc} (67%) diff --git a/mysql-test/include/have_normal_bzip.inc b/mysql-test/include/have_normal_zlib.inc similarity index 67% rename from mysql-test/include/have_normal_bzip.inc rename to mysql-test/include/have_normal_zlib.inc index 36c06274398c8..a4531e687bcec 100644 --- a/mysql-test/include/have_normal_bzip.inc +++ b/mysql-test/include/have_normal_zlib.inc @@ -1,9 +1,9 @@ --source include/have_compress.inc -# Test that the system is using the default/standard bzip library. +# Test that the system is using the default/standard zlib library. # If not, we have to skip the test as the compression lengths displayed # in the test will not match the results from used compression library. if (`select length(COMPRESS(space(5000))) != 33`) { - skip Test skipped as standard bzip is needed; + skip Test skipped as standard zlib is needed; } diff --git a/mysql-test/main/column_compression.test b/mysql-test/main/column_compression.test index c628e8f9cca86..de59cf5c7c82a 100644 --- a/mysql-test/main/column_compression.test +++ b/mysql-test/main/column_compression.test @@ -1,6 +1,6 @@ --source include/have_innodb.inc --source include/have_csv.inc ---source include/have_normal_bzip.inc +--source include/have_normal_zlib.inc let $MYSQLD_DATADIR= `select @@datadir`; diff --git a/mysql-test/main/column_compression_rpl.test b/mysql-test/main/column_compression_rpl.test index df8e889016b8b..18992f33f3c76 100644 --- a/mysql-test/main/column_compression_rpl.test +++ b/mysql-test/main/column_compression_rpl.test @@ -1,6 +1,6 @@ --source include/have_innodb.inc +--source include/have_normal_zlib.inc --source include/master-slave.inc ---source include/have_normal_bzip.inc --let $engine_type= myisam --let $engine_type2= innodb diff --git a/mysql-test/main/func_compress.test b/mysql-test/main/func_compress.test index 221dddd59f558..d18af140ed385 100644 --- a/mysql-test/main/func_compress.test +++ b/mysql-test/main/func_compress.test @@ -1,5 +1,5 @@ -- source include/have_compress.inc --- source include/have_normal_bzip.inc +-- source include/have_normal_zlib.inc # # Test for compress and uncompress functions: # diff --git a/mysql-test/main/mysqlbinlog_row_compressed.test b/mysql-test/main/mysqlbinlog_row_compressed.test index f28068618304c..f493c4db2cd60 100644 --- a/mysql-test/main/mysqlbinlog_row_compressed.test +++ b/mysql-test/main/mysqlbinlog_row_compressed.test @@ -4,7 +4,7 @@ --source include/have_log_bin.inc --source include/have_binlog_format_row.inc ---source include/have_normal_bzip.inc +--source include/have_normal_zlib.inc # # diff --git a/mysql-test/main/mysqlbinlog_stmt_compressed.test b/mysql-test/main/mysqlbinlog_stmt_compressed.test index 400f21f5ab926..1f1591e7a00d5 100644 --- a/mysql-test/main/mysqlbinlog_stmt_compressed.test +++ b/mysql-test/main/mysqlbinlog_stmt_compressed.test @@ -4,7 +4,7 @@ --source include/have_log_bin.inc --source include/have_binlog_format_statement.inc ---source include/have_normal_bzip.inc +--source include/have_normal_zlib.inc # # # mysqlbinlog: compressed query event diff --git a/mysql-test/suite/compat/oracle/t/column_compression.test b/mysql-test/suite/compat/oracle/t/column_compression.test index 01d4977ba961f..e8d55000bc62e 100644 --- a/mysql-test/suite/compat/oracle/t/column_compression.test +++ b/mysql-test/suite/compat/oracle/t/column_compression.test @@ -1,6 +1,6 @@ --source include/have_innodb.inc --source include/have_csv.inc ---source include/have_normal_bzip.inc +--source include/have_normal_zlib.inc SET sql_mode=ORACLE; diff --git a/mysql-test/suite/innodb/t/row_size_error_log_warnings_3.test b/mysql-test/suite/innodb/t/row_size_error_log_warnings_3.test index dab9bcfa86429..24029a48aa430 100644 --- a/mysql-test/suite/innodb/t/row_size_error_log_warnings_3.test +++ b/mysql-test/suite/innodb/t/row_size_error_log_warnings_3.test @@ -1,7 +1,7 @@ --source include/have_innodb.inc --source include/have_sequence.inc --source include/innodb_page_size_small.inc ---source include/have_normal_bzip.inc +--source include/have_normal_zlib.inc call mtr.add_suppression("InnoDB: Cannot add field .* in table .* because after adding it, the row size is .* which is greater than maximum allowed size (.*) for a record on index leaf page."); From 48e4962c44fa5cbdafb6d1ed9564f308a57eeedc Mon Sep 17 00:00:00 2001 From: Oleg Smirnov Date: Thu, 6 Jul 2023 11:55:40 +0700 Subject: [PATCH 027/129] MDEV-29298 INSERT ... SELECT Does not produce an optimizer trace Add INSERT ... SELECT to the list of commands that can be traced Approved by Sergei Petrunia (sergey@mariadb.com) --- mysql-test/main/opt_trace.result | 12 ++++++++++++ mysql-test/main/opt_trace.test | 14 ++++++++++++++ sql/opt_trace.cc | 3 ++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/opt_trace.result b/mysql-test/main/opt_trace.result index 5766b8ead4231..66f081c65904c 100644 --- a/mysql-test/main/opt_trace.result +++ b/mysql-test/main/opt_trace.result @@ -9231,5 +9231,17 @@ json_detailed(json_extract(trace, '$**.in_to_subquery_conversion')) ] set in_predicate_conversion_threshold=@tmp; drop table t0; +# +# MDEV-29298: INSERT ... SELECT Does not produce an optimizer trace +# +create table t1 (a int, b int); +create table t2 (a int, b int); +insert into t1 values (1,1), (2,2), (3,3), (4,4), (5,5); +set optimizer_trace=1; +insert into t2 select * from t1 where a<= b and a>4; +select QUERY, LENGTH(trace)>1 from information_schema.optimizer_trace; +QUERY LENGTH(trace)>1 +insert into t2 select * from t1 where a<= b and a>4 1 +drop table t1, t2; # End of 10.5 tests set optimizer_trace='enabled=off'; diff --git a/mysql-test/main/opt_trace.test b/mysql-test/main/opt_trace.test index 43539fc764bc5..e7851fc62c57f 100644 --- a/mysql-test/main/opt_trace.test +++ b/mysql-test/main/opt_trace.test @@ -861,5 +861,19 @@ set in_predicate_conversion_threshold=@tmp; drop table t0; --enable_view_protocol +--echo # +--echo # MDEV-29298: INSERT ... SELECT Does not produce an optimizer trace +--echo # +create table t1 (a int, b int); +create table t2 (a int, b int); +insert into t1 values (1,1), (2,2), (3,3), (4,4), (5,5); +set optimizer_trace=1; + +insert into t2 select * from t1 where a<= b and a>4; + +select QUERY, LENGTH(trace)>1 from information_schema.optimizer_trace; + +drop table t1, t2; + --echo # End of 10.5 tests set optimizer_trace='enabled=off'; diff --git a/sql/opt_trace.cc b/sql/opt_trace.cc index ddec6d5ed2df3..4bd545495328f 100644 --- a/sql/opt_trace.cc +++ b/sql/opt_trace.cc @@ -103,7 +103,8 @@ inline bool sql_command_can_be_traced(enum enum_sql_command sql_command) sql_command == SQLCOM_UPDATE || sql_command == SQLCOM_DELETE || sql_command == SQLCOM_DELETE_MULTI || - sql_command == SQLCOM_UPDATE_MULTI; + sql_command == SQLCOM_UPDATE_MULTI || + sql_command == SQLCOM_INSERT_SELECT; } void opt_trace_print_expanded_query(THD *thd, SELECT_LEX *select_lex, From 82f27ea5a49798d5d89479db8715faa71f2a87ab Mon Sep 17 00:00:00 2001 From: Anel Husakovic Date: Thu, 11 Jan 2024 12:54:16 +0100 Subject: [PATCH 028/129] MDEV-33187: Make mariadb-hotcopy compatible with DBI:MariaDB --- scripts/mysqlhotcopy.sh | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/scripts/mysqlhotcopy.sh b/scripts/mysqlhotcopy.sh index 44abcfec0553d..ebf82e46e039b 100644 --- a/scripts/mysqlhotcopy.sh +++ b/scripts/mysqlhotcopy.sh @@ -189,21 +189,38 @@ $opt{quiet} = 0 if $opt{debug}; $opt{allowold} = 1 if $opt{keepold}; # --- connect to the database --- +## Socket takes precedence. my $dsn; -$dsn = ";host=" . (defined($opt{host}) ? $opt{host} : "localhost"); -$dsn .= ";port=$opt{port}" if $opt{port}; -$dsn .= ";mariadb_socket=$opt{socket}" if $opt{socket}; +my $prefix= 'mysql'; -# use mariadb_read_default_group=mysqlhotcopy so that [client] and -# [mysqlhotcopy] groups will be read from standard options files. +if (eval {DBI->install_driver("MariaDB")}) { + $dsn ="DBI:MariaDB:;"; + $prefix= 'mariadb'; +} +else { + $dsn = "DBI:mysql:;"; +} -my $dbh = DBI->connect("DBI:MariaDB:$dsn;mariadb_read_default_group=mysqlhotcopy", - $opt{user}, $opt{password}, +if ($opt{socket} and -S $opt{socket}) +{ + $dsn .= "${prefix}_socket=$opt{socket}"; +} +else { - RaiseError => 1, - PrintError => 0, - AutoCommit => 1, -}); + $dsn .= "host=" . $opt{host}; + if ($opt{host} ne "localhost") + { + $dsn .= ";port=". $opt{port}; + } +} + +$dsn .= ";mariadb_read_default_group=mysqlhotcopy"; + +# use mariadb_read_default_group=mysqlhotcopy so that [client] and +# [mysqlhotcopy] groups will be read from standard options files. +# make the connection to MariaDB +my $dbh= DBI->connect($dsn, $opt{user}, $opt{password}, { RaiseError => 1, PrintError => 0}) || + die("Can't make a connection to the MariaDB server.\n The error: $DBI::errstr"); # --- check that checkpoint table exists if specified --- if ( $opt{checkpoint} ) { From 7702e481df4a4ccce84558dbebccbc5a2fc64fd1 Mon Sep 17 00:00:00 2001 From: Paul Szabo Date: Fri, 12 Jan 2024 16:02:02 +0100 Subject: [PATCH 029/129] MDEV-30259: mariadb-hotcopy fails for performance_schema Signed-off-by: Paul Szabo psz@maths.usyd.edu.au www.maths.usyd.edu.au/u/psz School of Mathematics and Statistics University of Sydney Australia --- scripts/mysqlhotcopy.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/mysqlhotcopy.sh b/scripts/mysqlhotcopy.sh index ebf82e46e039b..78399ac3599f5 100644 --- a/scripts/mysqlhotcopy.sh +++ b/scripts/mysqlhotcopy.sh @@ -288,6 +288,7 @@ if ( defined $opt{regexp} ) { $sth_dbs->execute; while ( my ($db_name) = $sth_dbs->fetchrow_array ) { next if $db_name =~ m/^information_schema$/i; + next if $db_name =~ m/^performance_schema$/i; push @db_desc, { 'src' => $db_name, 't_regex' => $t_regex } if ( $db_name =~ m/$opt{regexp}/o ); } } From 78ea9ee4f26d7927cfcadb482a0031e2e40c7684 Mon Sep 17 00:00:00 2001 From: Paul Szabo Date: Fri, 12 Jan 2024 16:03:41 +0100 Subject: [PATCH 030/129] MDEV-33187: mariadb-hotcopy fails for sys Signed-off-by: Paul Szabo psz@maths.usyd.edu.au www.maths.usyd.edu.au/u/psz School of Mathematics and Statistics University of Sydney Australia --- scripts/mysqlhotcopy.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/mysqlhotcopy.sh b/scripts/mysqlhotcopy.sh index 78399ac3599f5..d0821e663c4a6 100644 --- a/scripts/mysqlhotcopy.sh +++ b/scripts/mysqlhotcopy.sh @@ -289,6 +289,7 @@ if ( defined $opt{regexp} ) { while ( my ($db_name) = $sth_dbs->fetchrow_array ) { next if $db_name =~ m/^information_schema$/i; next if $db_name =~ m/^performance_schema$/i; + next if $db_name =~ m/^sys$/i; push @db_desc, { 'src' => $db_name, 't_regex' => $t_regex } if ( $db_name =~ m/$opt{regexp}/o ); } } From ee30491e508e6c8c19c899f713662ec2e5042aaa Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Wed, 15 Nov 2023 11:09:26 +0200 Subject: [PATCH 031/129] MDEV-32111: Debian Sid/Trixie will not have libncurses 5 anymore Upstream Debian Sid which will become Debian Trixie (13) have dropped NCurses version 5 and changed dev package name just libncurses-dev --- debian/control | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/debian/control b/debian/control index 2bbd71d2ac92d..eb6a5cba16cfa 100644 --- a/debian/control +++ b/debian/control @@ -26,8 +26,7 @@ Build-Depends: bison, libjudy-dev, libkrb5-dev, liblz4-dev, - libncurses5-dev (>= 5.0-6~), - libncurses5-dev:native (>= 5.0-6~), + libncurses-dev, libnuma-dev [linux-any], libpam0g-dev, libpcre2-dev, From 653cb195d30bca678b5b175157f0f34206c3761d Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Mon, 15 Jan 2024 13:01:22 +0530 Subject: [PATCH 032/129] MDEV-26740 Inplace alter rebuild increases file size PageBulk::init(): Unnecessary reserves the extent before allocating a page for bulk insert. btr_page_alloc() capable of handing the extending of tablespace. --- .../suite/innodb_zip/r/innochecksum_3.result | 2 -- storage/innobase/btr/btr0bulk.cc | 17 ++++------------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/mysql-test/suite/innodb_zip/r/innochecksum_3.result b/mysql-test/suite/innodb_zip/r/innochecksum_3.result index 7de90721d64b4..89830fbbf62b8 100644 --- a/mysql-test/suite/innodb_zip/r/innochecksum_3.result +++ b/mysql-test/suite/innodb_zip/r/innochecksum_3.result @@ -173,7 +173,6 @@ Filename::tab#.ibd #::# | Index page | index id=#, page level=#, No. of records=#, garbage=#, - #::# | Index page | index id=#, page level=#, No. of records=#, garbage=#, - #::# | Index page | index id=#, page level=#, No. of records=#, garbage=#, - -#::# | Freshly allocated page | - # Variables used by page type dump for ibdata1 Variables (--variable-name=value) @@ -208,7 +207,6 @@ Filename::tab#.ibd #::# | Index page | index id=#, page level=#, No. of records=#, garbage=#, - #::# | Index page | index id=#, page level=#, No. of records=#, garbage=#, - #::# | Index page | index id=#, page level=#, No. of records=#, garbage=#, - -#::# | Freshly allocated page | - [6]: check the valid lower bound values for option # allow-mismatches,page,start-page,end-page [9]: check the both short and long options "page" and "start-page" when diff --git a/storage/innobase/btr/btr0bulk.cc b/storage/innobase/btr/btr0bulk.cc index f7afc575ed5a6..a39a96b4b04e3 100644 --- a/storage/innobase/btr/btr0bulk.cc +++ b/storage/innobase/btr/btr0bulk.cc @@ -70,24 +70,15 @@ PageBulk::init() alloc_mtr.start(); m_index->set_modified(alloc_mtr); - ulint n_reserved; - bool success; - success = fsp_reserve_free_extents(&n_reserved, - m_index->table->space, - 1, FSP_NORMAL, &alloc_mtr); - if (!success) { - alloc_mtr.commit(); - m_mtr.commit(); - return(DB_OUT_OF_FILE_SPACE); - } - /* Allocate a new page. */ new_block = btr_page_alloc(m_index, 0, FSP_UP, m_level, &alloc_mtr, &m_mtr); - m_index->table->space->release_free_extents(n_reserved); - alloc_mtr.commit(); + if (!new_block) { + m_mtr.commit(); + return DB_OUT_OF_FILE_SPACE; + } new_page = buf_block_get_frame(new_block); new_page_zip = buf_block_get_page_zip(new_block); From caad34df549b91b654c69bf3b5644277be783d31 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Mon, 15 Jan 2024 14:08:27 +0530 Subject: [PATCH 033/129] MDEV-32968 InnoDB fails to restore tablespace first page from doublewrite buffer when page is empty - InnoDB fails to find the space id from the page0 of the tablespace. In that case, InnoDB can use doublewrite buffer to recover the page0 and write into the file. - buf_dblwr_t::init_or_load_pages(): Loads only the pages which are valid.(page lsn >= checkpoint). To do that, InnoDB has to open the redo log before system tablespace, read the latest checkpoint information. recv_dblwr_t::find_first_page(): 1) Iterate the doublewrite buffer pages and find the 0th page 2) Read the tablespace flags, space id from the 0th page. 3) Read the 1st, 2nd and 3rd page from tablespace file and compare the space id with the space id which is stored in doublewrite buffer. 4) If it matches then we can write into the file. 5) Return space which matches the pages from the file. SysTablespace::read_lsn_and_check_flags(): Remove the retry logic for validating the first page. After restoring the first page from doublewrite buffer, assign tablespace flags by reading the first page. recv_recovery_read_max_checkpoint(): Reads the maximum checkpoint information from log file recv_recovery_from_checkpoint_start(): Avoid reading the checkpoint header information from log file Datafile::validate_first_page(): Throw error in case of first page validation fails. --- mysql-test/suite/innodb/r/doublewrite.result | 25 +++- .../suite/innodb/r/log_file_name.result | 7 +- mysql-test/suite/innodb/t/doublewrite.test | 38 ++++- .../suite/innodb/t/doublewrite_debug.test | 1 + mysql-test/suite/innodb/t/log_file_name.test | 9 +- storage/innobase/buf/buf0dblwr.cc | 11 +- storage/innobase/fsp/fsp0file.cc | 21 ++- storage/innobase/fsp/fsp0sysspace.cc | 17 +-- storage/innobase/include/buf0dblwr.h | 3 +- storage/innobase/include/log0recv.h | 18 ++- storage/innobase/log/log0recv.cc | 125 ++++++++++++---- storage/innobase/srv/srv0start.cc | 137 +++++++++--------- 12 files changed, 276 insertions(+), 136 deletions(-) diff --git a/mysql-test/suite/innodb/r/doublewrite.result b/mysql-test/suite/innodb/r/doublewrite.result index 7763aa9932218..ceb01ac480746 100644 --- a/mysql-test/suite/innodb/r/doublewrite.result +++ b/mysql-test/suite/innodb/r/doublewrite.result @@ -1,7 +1,7 @@ # # MDEV-32242 innodb.doublewrite test case always is skipped # -create table t1 (f1 int primary key, f2 blob) engine=innodb; +create table t1 (f1 int primary key, f2 blob) stats_persistent=0, engine=innodb; start transaction; insert into t1 values(1, repeat('#',12)); insert into t1 values(2, repeat('+',12)); @@ -19,6 +19,7 @@ XA PREPARE 'x'; disconnect dml; connection default; flush table t1 for export; +# Kill the server # restart FOUND 1 /InnoDB: Restoring page \[page id: space=[1-9][0-9]*, page number=0\] of datafile/ in mysqld.1.err FOUND 1 /InnoDB: Recovered page \[page id: space=[1-9][0-9]*, page number=3\]/ in mysqld.1.err @@ -33,5 +34,27 @@ f1 f2 3 //////////// 4 ------------ 5 ............ +connect dml,localhost,root,,; +XA START 'x'; +insert into t1 values (6, repeat('%', @@innodb_page_size/2)); +XA END 'x'; +XA PREPARE 'x'; +disconnect dml; +connection default; +flush table t1 for export; +# Kill the server +# restart +FOUND 2 /InnoDB: Restoring page \[page id: space=[1-9][0-9]*, page number=0\] of datafile/ in mysqld.1.err +XA ROLLBACK 'x'; +check table t1; +Table Op Msg_type Msg_text +test.t1 check status OK +select f1, f2 from t1; +f1 f2 +1 ############ +2 ++++++++++++ +3 //////////// +4 ------------ +5 ............ drop table t1; # End of 10.5 tests diff --git a/mysql-test/suite/innodb/r/log_file_name.result b/mysql-test/suite/innodb/r/log_file_name.result index 42b988ed3ca39..3d48a24d217de 100644 --- a/mysql-test/suite/innodb/r/log_file_name.result +++ b/mysql-test/suite/innodb/r/log_file_name.result @@ -1,3 +1,4 @@ +call mtr.add_suppression("InnoDB: Header page consists of zero bytes in datafile:"); SET GLOBAL innodb_file_per_table=ON; FLUSH TABLES; CREATE TABLE t1(a INT PRIMARY KEY) ENGINE=InnoDB; @@ -89,7 +90,7 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES WHERE engine = 'innodb' AND support IN ('YES', 'DEFAULT', 'ENABLED'); ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS -FOUND 1 /\[Note\] InnoDB: Header page consists of zero bytes in datafile: .*u1.ibd/ in mysqld.1.err +FOUND 1 /\[ERROR\] InnoDB: Header page consists of zero bytes in datafile: .*u1.ibd/ in mysqld.1.err FOUND 1 /\[ERROR\] InnoDB: Datafile .*u1.*\. Cannot determine the space ID from the first 64 pages/ in mysqld.1.err NOT FOUND /\[Note\] InnoDB: Cannot read first page of .*u2.ibd/ in mysqld.1.err # Fault 7: Missing or wrong data file and innodb_force_recovery @@ -98,11 +99,11 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES WHERE engine = 'innodb' AND support IN ('YES', 'DEFAULT', 'ENABLED'); ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS -FOUND 1 /\[Note\] InnoDB: Header page consists of zero bytes in datafile: .*u1.ibd/ in mysqld.1.err +FOUND 1 /\[ERROR\] InnoDB: Header page consists of zero bytes in datafile: .*u1.ibd/ in mysqld.1.err FOUND 1 /InnoDB: At LSN: \d+: unable to open file .*u[1-5].ibd for tablespace/ in mysqld.1.err FOUND 1 /\[ERROR\] InnoDB: Cannot replay rename of tablespace \d+ from '.*u4.ibd' to '.*u6.ibd' because the target file exists/ in mysqld.1.err # restart: --innodb-force-recovery=1 -FOUND 1 /\[Note\] InnoDB: Header page consists of zero bytes in datafile: .*u1.ibd/ in mysqld.1.err +FOUND 1 /\[ERROR\] InnoDB: Header page consists of zero bytes in datafile: .*u1.ibd/ in mysqld.1.err FOUND 1 /InnoDB: At LSN: \d+: unable to open file .*u[1-5].ibd for tablespace/ in mysqld.1.err FOUND 1 /\[Warning\] InnoDB: Tablespace \d+ was not found at .*u[1-5].ibd, and innodb_force_recovery was set. All redo log for this tablespace will be ignored!/ in mysqld.1.err # restart diff --git a/mysql-test/suite/innodb/t/doublewrite.test b/mysql-test/suite/innodb/t/doublewrite.test index 89e6577b43405..541e4d23a1914 100644 --- a/mysql-test/suite/innodb/t/doublewrite.test +++ b/mysql-test/suite/innodb/t/doublewrite.test @@ -17,6 +17,7 @@ call mtr.add_suppression("InnoDB: A bad Space ID was found in datafile"); call mtr.add_suppression("InnoDB: Checksum mismatch in datafile: "); call mtr.add_suppression("InnoDB: Inconsistent tablespace ID in .*t1\\.ibd"); call mtr.add_suppression("\\[Warning\\] Found 1 prepared XA transactions"); +call mtr.add_suppression("InnoDB: Header page consists of zero bytes in datafile:"); --enable_query_log let INNODB_PAGE_SIZE=`select @@innodb_page_size`; @@ -24,7 +25,7 @@ let MYSQLD_DATADIR=`select @@datadir`; let ALGO=`select @@innodb_checksum_algorithm`; let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err; -create table t1 (f1 int primary key, f2 blob) engine=innodb; +create table t1 (f1 int primary key, f2 blob) stats_persistent=0, engine=innodb; start transaction; insert into t1 values(1, repeat('#',12)); @@ -38,7 +39,7 @@ commit work; SET GLOBAL innodb_fast_shutdown = 0; let $shutdown_timeout=; --source include/restart_mysqld.inc - +--source ../include/no_checkpoint_start.inc connect (dml,localhost,root,,); XA START 'x'; insert into t1 values (6, repeat('%', @@innodb_page_size/2)); @@ -50,8 +51,8 @@ connection default; flush table t1 for export; let $restart_parameters=; -let $shutdown_timeout=0; ---source include/shutdown_mysqld.inc +--let CLEANUP_IF_CHECKPOINT=drop table t1, unexpected_checkpoint; +--source ../include/no_checkpoint_end.inc perl; use IO::Handle; @@ -119,6 +120,35 @@ let SEARCH_PATTERN=InnoDB: Recovered page \[page id: space=[1-9][0-9]*, page num XA ROLLBACK 'x'; check table t1; select f1, f2 from t1; + +--source ../include/no_checkpoint_start.inc +connect (dml,localhost,root,,); +XA START 'x'; +insert into t1 values (6, repeat('%', @@innodb_page_size/2)); +XA END 'x'; +XA PREPARE 'x'; +disconnect dml; +connection default; + +flush table t1 for export; + +let $restart_parameters=; +--source ../include/no_checkpoint_end.inc + +# Zero out the first page in file and try to recover from dblwr +perl; +use IO::Handle; +open(FILE, "+<", "$ENV{'MYSQLD_DATADIR'}test/t1.ibd") or die; +syswrite(FILE, chr(0) x $ENV{INNODB_PAGE_SIZE}); +close FILE; +EOF + +--source include/start_mysqld.inc +let SEARCH_PATTERN=InnoDB: Restoring page \[page id: space=[1-9][0-9]*, page number=0\] of datafile; +--source include/search_pattern_in_file.inc +XA ROLLBACK 'x'; +check table t1; +select f1, f2 from t1; drop table t1; --echo # End of 10.5 tests diff --git a/mysql-test/suite/innodb/t/doublewrite_debug.test b/mysql-test/suite/innodb/t/doublewrite_debug.test index 1bff8b4e07fe9..3d96a74e698fe 100644 --- a/mysql-test/suite/innodb/t/doublewrite_debug.test +++ b/mysql-test/suite/innodb/t/doublewrite_debug.test @@ -17,6 +17,7 @@ call mtr.add_suppression("Plugin 'InnoDB' (init function returned error|registra call mtr.add_suppression("InnoDB: A bad Space ID was found in datafile"); call mtr.add_suppression("InnoDB: Checksum mismatch in datafile: "); call mtr.add_suppression("InnoDB: Inconsistent tablespace ID in .*t1\\.ibd"); +call mtr.add_suppression("InnoDB: Header page consists of zero bytes in datafile:"); --enable_query_log let INNODB_PAGE_SIZE=`select @@innodb_page_size`; diff --git a/mysql-test/suite/innodb/t/log_file_name.test b/mysql-test/suite/innodb/t/log_file_name.test index 11eca0a7a1250..494d256cd1131 100644 --- a/mysql-test/suite/innodb/t/log_file_name.test +++ b/mysql-test/suite/innodb/t/log_file_name.test @@ -7,6 +7,8 @@ # Embedded server does not support crashing --source include/not_embedded.inc +call mtr.add_suppression("InnoDB: Header page consists of zero bytes in datafile:"); + SET GLOBAL innodb_file_per_table=ON; FLUSH TABLES; @@ -172,6 +174,7 @@ call mtr.add_suppression("InnoDB: Table test/u[123] in the InnoDB data dictionar call mtr.add_suppression("InnoDB: Cannot replay rename of tablespace.*"); call mtr.add_suppression("InnoDB: Attempted to open a previously opened tablespace"); call mtr.add_suppression("InnoDB: Recovery cannot access file"); +call mtr.add_suppression("InnoDB: Cannot read first page in datafile:"); FLUSH TABLES; --enable_query_log @@ -215,7 +218,7 @@ EOF --source include/start_mysqld.inc eval $check_no_innodb; -let SEARCH_PATTERN= \[Note\] InnoDB: Header page consists of zero bytes in datafile: .*u1.ibd; +let SEARCH_PATTERN= \[ERROR\] InnoDB: Header page consists of zero bytes in datafile: .*u1.ibd; --source include/search_pattern_in_file.inc let SEARCH_PATTERN= \[ERROR\] InnoDB: Datafile .*u1.*\. Cannot determine the space ID from the first 64 pages; @@ -240,7 +243,7 @@ let SEARCH_PATTERN= \[Note\] InnoDB: Cannot read first page of .*u2.ibd; --source include/start_mysqld.inc eval $check_no_innodb; -let SEARCH_PATTERN= \[Note\] InnoDB: Header page consists of zero bytes in datafile: .*u1.ibd; +let SEARCH_PATTERN= \[ERROR\] InnoDB: Header page consists of zero bytes in datafile: .*u1.ibd; --source include/search_pattern_in_file.inc let SEARCH_PATTERN= InnoDB: At LSN: \d+: unable to open file .*u[1-5].ibd for tablespace; @@ -253,7 +256,7 @@ let SEARCH_PATTERN= \[ERROR\] InnoDB: Cannot replay rename of tablespace \d+ fro --source include/restart_mysqld.inc -let SEARCH_PATTERN= \[Note\] InnoDB: Header page consists of zero bytes in datafile: .*u1.ibd; +let SEARCH_PATTERN= \[ERROR\] InnoDB: Header page consists of zero bytes in datafile: .*u1.ibd; --source include/search_pattern_in_file.inc let SEARCH_PATTERN= InnoDB: At LSN: \d+: unable to open file .*u[1-5].ibd for tablespace; diff --git a/storage/innobase/buf/buf0dblwr.cc b/storage/innobase/buf/buf0dblwr.cc index 57ec3553b8dbe..d5c954dff89b6 100644 --- a/storage/innobase/buf/buf0dblwr.cc +++ b/storage/innobase/buf/buf0dblwr.cc @@ -324,11 +324,14 @@ dberr_t buf_dblwr_t::init_or_load_pages(pfs_os_file_t file, const char *path) os_file_flush(file); } else - for (ulint i= 0; i < size * 2; i++, page += srv_page_size) - if (mach_read_from_8(my_assume_aligned<8>(page + FIL_PAGE_LSN))) - /* Each valid page header must contain a nonzero FIL_PAGE_LSN field. */ + { + alignas(8) char checkpoint[8]; + mach_write_to_8(checkpoint, log_sys.next_checkpoint_lsn); + for (auto i= size * 2; i--; page += srv_page_size) + if (memcmp_aligned<8>(page + FIL_PAGE_LSN, checkpoint, 8) >= 0) + /* Valid pages are not older than the log checkpoint. */ recv_sys.dblwr.add(page); - + } err= DB_SUCCESS; goto func_exit; } diff --git a/storage/innobase/fsp/fsp0file.cc b/storage/innobase/fsp/fsp0file.cc index f0ead3b80a37a..653c848953a2f 100644 --- a/storage/innobase/fsp/fsp0file.cc +++ b/storage/innobase/fsp/fsp0file.cc @@ -475,9 +475,16 @@ Datafile::validate_for_recovery() err = find_space_id(); if (err != DB_SUCCESS || m_space_id == 0) { - ib::error() << "Datafile '" << m_filepath << "' is" - " corrupted. Cannot determine the space ID from" - " the first 64 pages."; + + m_space_id = recv_sys.dblwr.find_first_page( + m_filepath, m_handle); + + if (m_space_id) goto free_first_page; + + sql_print_error( + "InnoDB: Datafile '%s' is corrupted." + " Cannot determine the space ID from" + " the first 64 pages.", m_filepath); return(err); } @@ -485,7 +492,7 @@ Datafile::validate_for_recovery() m_space_id, m_filepath, m_handle)) { return(DB_CORRUPTION); } - +free_first_page: /* Free the previously read first page and then re-validate. */ free_first_page(); err = validate_first_page(0); @@ -531,9 +538,9 @@ Datafile::validate_first_page(lsn_t* flush_lsn) if (error_txt != NULL) { err_exit: - ib::info() << error_txt << " in datafile: " << m_filepath - << ", Space ID:" << m_space_id << ", Flags: " - << m_flags; + sql_print_error("InnoDB: %s in datafile: %s, Space ID: %zu, " + "Flags: %zu", error_txt, m_filepath, + m_space_id, m_flags); m_is_valid = false; free_first_page(); return(DB_CORRUPTION); diff --git a/storage/innobase/fsp/fsp0sysspace.cc b/storage/innobase/fsp/fsp0sysspace.cc index a7bb417c502f1..eb6e8d1166eb5 100644 --- a/storage/innobase/fsp/fsp0sysspace.cc +++ b/storage/innobase/fsp/fsp0sysspace.cc @@ -574,7 +574,7 @@ SysTablespace::read_lsn_and_check_flags(lsn_t* flushed_lsn) } err = it->read_first_page( - m_ignore_read_only ? false : srv_read_only_mode); + m_ignore_read_only && srv_read_only_mode); if (err != DB_SUCCESS) { return(err); @@ -588,20 +588,17 @@ SysTablespace::read_lsn_and_check_flags(lsn_t* flushed_lsn) /* Check the contents of the first page of the first datafile. */ - for (int retry = 0; retry < 2; ++retry) { + err = it->validate_first_page(flushed_lsn); - err = it->validate_first_page(flushed_lsn); - - if (err != DB_SUCCESS - && (retry == 1 - || recv_sys.dblwr.restore_first_page( + if (err != DB_SUCCESS) { + if (recv_sys.dblwr.restore_first_page( it->m_space_id, it->m_filepath, - it->handle()))) { - + it->handle())) { it->close(); - return(err); } + err = it->read_first_page( + m_ignore_read_only && srv_read_only_mode); } /* Make sure the tablespace space ID matches the diff --git a/storage/innobase/include/buf0dblwr.h b/storage/innobase/include/buf0dblwr.h index f82baeec89a40..99a41d069ef35 100644 --- a/storage/innobase/include/buf0dblwr.h +++ b/storage/innobase/include/buf0dblwr.h @@ -103,7 +103,8 @@ class buf_dblwr_t If we are upgrading from a version before MySQL 4.1, then this function performs the necessary update operations to support innodb_file_per_table. If we are in a crash recovery, this function - loads the pages from double write buffer into memory. + loads the pages from double write buffer which are not older than + the checkpoint into memory. @param file File handle @param path Path name of file @return DB_SUCCESS or error code */ diff --git a/storage/innobase/include/log0recv.h b/storage/innobase/include/log0recv.h index 34211392ba399..aca9f994acbbd 100644 --- a/storage/innobase/include/log0recv.h +++ b/storage/innobase/include/log0recv.h @@ -49,6 +49,11 @@ recv_find_max_checkpoint(ulint* max_field) ATTRIBUTE_COLD void recv_recover_page(fil_space_t* space, buf_page_t* bpage) MY_ATTRIBUTE((nonnull)); +/** Read the latest checkpoint information from log file +and store it in log_sys.next_checkpoint and recv_sys.mlog_checkpoint_lsn +@return error code or DB_SUCCESS */ +dberr_t recv_recovery_read_max_checkpoint(); + /** Start recovering from a redo log checkpoint. @param[in] flush_lsn FIL_PAGE_FILE_FLUSH_LSN of first system tablespace page @@ -141,7 +146,18 @@ struct recv_dblwr_t @param file tablespace file handle @return whether the operation failed */ bool restore_first_page( - ulint space_id, const char *name, os_file_t file); + ulint space_id, const char *name, pfs_os_file_t file); + + /** Restore the first page of the given tablespace from + doublewrite buffer. + 1) Find the page which has page_no as 0 + 2) Read first 3 pages from tablespace file + 3) Compare the space_ids from the pages with page0 which + was retrieved from doublewrite buffer + @param name tablespace filepath + @param file tablespace file handle + @return space_id or 0 in case of error */ + uint32_t find_first_page(const char *name, pfs_os_file_t file); typedef std::deque > list; diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index 05120871b0ac0..52649419ec708 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -3415,6 +3415,46 @@ recv_init_crash_recovery_spaces(bool rescan, bool& missing_tablespace) return DB_SUCCESS; } +dberr_t recv_recovery_read_max_checkpoint() +{ + ut_ad(srv_operation <= SRV_OPERATION_EXPORT_RESTORED || + srv_operation == SRV_OPERATION_RESTORE || + srv_operation == SRV_OPERATION_RESTORE_EXPORT); + ut_d(mysql_mutex_lock(&buf_pool.mutex)); + ut_ad(UT_LIST_GET_LEN(buf_pool.LRU) == 0); + ut_ad(UT_LIST_GET_LEN(buf_pool.unzip_LRU) == 0); + ut_d(mysql_mutex_unlock(&buf_pool.mutex)); + + if (srv_force_recovery >= SRV_FORCE_NO_LOG_REDO) + { + sql_print_information("InnoDB: innodb_force_recovery=6 skips redo log apply"); + return DB_SUCCESS; + } + + mysql_mutex_lock(&log_sys.mutex); + ulint max_cp; + dberr_t err= recv_find_max_checkpoint(&max_cp); + + if (err != DB_SUCCESS) + recv_sys.recovered_lsn= log_sys.get_lsn(); + else + { + byte* buf= log_sys.checkpoint_buf; + err= log_sys.log.read(max_cp, {buf, OS_FILE_LOG_BLOCK_SIZE}); + if (err == DB_SUCCESS) + { + log_sys.next_checkpoint_no= + mach_read_from_8(buf + LOG_CHECKPOINT_NO); + log_sys.next_checkpoint_lsn= + mach_read_from_8(buf + LOG_CHECKPOINT_LSN); + recv_sys.mlog_checkpoint_lsn= + mach_read_from_8(buf + LOG_CHECKPOINT_END_LSN); + } + } + mysql_mutex_unlock(&log_sys.mutex); + return err; +} + /** Start recovering from a redo log checkpoint. @param[in] flush_lsn FIL_PAGE_FILE_FLUSH_LSN of first system tablespace page @@ -3422,12 +3462,8 @@ of first system tablespace page dberr_t recv_recovery_from_checkpoint_start(lsn_t flush_lsn) { - ulint max_cp_field; - lsn_t checkpoint_lsn; bool rescan = false; - ib_uint64_t checkpoint_no; lsn_t contiguous_lsn; - byte* buf; dberr_t err = DB_SUCCESS; ut_ad(srv_operation <= SRV_OPERATION_EXPORT_RESTORED @@ -3445,27 +3481,11 @@ recv_recovery_from_checkpoint_start(lsn_t flush_lsn) return(DB_SUCCESS); } - recv_sys.recovery_on = true; - mysql_mutex_lock(&log_sys.mutex); - - err = recv_find_max_checkpoint(&max_cp_field); - - if (err != DB_SUCCESS) { - - recv_sys.recovered_lsn = log_sys.get_lsn(); - mysql_mutex_unlock(&log_sys.mutex); - return(err); - } - - buf = log_sys.checkpoint_buf; - if ((err= log_sys.log.read(max_cp_field, {buf, OS_FILE_LOG_BLOCK_SIZE}))) { - mysql_mutex_unlock(&log_sys.mutex); - return(err); - } - - checkpoint_lsn = mach_read_from_8(buf + LOG_CHECKPOINT_LSN); - checkpoint_no = mach_read_from_8(buf + LOG_CHECKPOINT_NO); + uint64_t checkpoint_no= log_sys.next_checkpoint_no; + lsn_t checkpoint_lsn= log_sys.next_checkpoint_lsn; + const lsn_t end_lsn= recv_sys.mlog_checkpoint_lsn; + recv_sys.recovery_on = true; /* Start reading the log from the checkpoint lsn. The variable contiguous_lsn contains an lsn up to which the log is known to @@ -3474,8 +3494,6 @@ recv_recovery_from_checkpoint_start(lsn_t flush_lsn) ut_ad(RECV_SCAN_SIZE <= srv_log_buffer_size); - const lsn_t end_lsn = mach_read_from_8( - buf + LOG_CHECKPOINT_END_LSN); ut_ad(recv_sys.pages.empty()); contiguous_lsn = checkpoint_lsn; @@ -3845,8 +3863,52 @@ byte *recv_dblwr_t::find_page(const page_id_t page_id, return result; } +uint32_t recv_dblwr_t::find_first_page(const char *name, pfs_os_file_t file) +{ + os_offset_t file_size= os_file_get_size(file); + if (file_size != (os_offset_t) -1) + { + for (const page_t *page : pages) + { + uint32_t space_id= page_get_space_id(page); + if (page_get_page_no(page) > 0 || space_id == 0) +next_page: + continue; + uint32_t flags= mach_read_from_4( + FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page); + page_id_t page_id(space_id, 0); + size_t page_size= fil_space_t::physical_size(flags); + if (file_size < 4 * page_size) + goto next_page; + byte *read_page= + static_cast(aligned_malloc(3 * page_size, page_size)); + /* Read 3 pages from the file and match the space id + with the space id which is stored in + doublewrite buffer page. */ + if (os_file_read(IORequestRead, file, read_page, page_size, + 3 * page_size) != DB_SUCCESS) + goto next_page; + for (ulint j= 0; j <= 2; j++) + { + byte *cur_page= read_page + j * page_size; + if (buf_is_zeroes(span(cur_page, page_size))) + return 0; + if (mach_read_from_4(cur_page + FIL_PAGE_OFFSET) != j + 1 || + memcmp(cur_page + FIL_PAGE_SPACE_ID, + page + FIL_PAGE_SPACE_ID, 4) || + buf_page_is_corrupted(false, cur_page, flags)) + goto next_page; + } + if (!restore_first_page(space_id, name, file)) + return space_id; + break; + } + } + return 0; +} + bool recv_dblwr_t::restore_first_page(ulint space_id, const char *name, - os_file_t file) + pfs_os_file_t file) { const page_id_t page_id(space_id, 0); const byte* page= find_page(page_id); @@ -3854,10 +3916,11 @@ bool recv_dblwr_t::restore_first_page(ulint space_id, const char *name, { /* If the first page of the given user tablespace is not there in the doublewrite buffer, then the recovery is going to fail - now. Hence this is treated as error. */ - ib::error() - << "Corrupted page " << page_id << " of datafile '" - << name <<"' could not be found in the doublewrite buffer."; + now. Report error only when doublewrite buffer is not empty */ + if (pages.size()) + ib::error() << "Corrupted page " << page_id << " of datafile '" + << name <<"' could not be found in the " + <<"doublewrite buffer."; return true; } diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index 56e41616d1de5..0760b8ca5fd67 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -297,9 +297,6 @@ static dberr_t create_log_file(bool create_new_db, lsn_t lsn, log_sys.log.create(); log_sys.log.open_file(logfile0); - if (!fil_system.sys_space->open(create_new_db)) { - return DB_ERROR; - } /* Create a log checkpoint. */ mysql_mutex_lock(&log_sys.mutex); @@ -1274,40 +1271,6 @@ dberr_t srv_start(bool create_new_db) /* Check if undo tablespaces and redo log files exist before creating a new system tablespace */ - if (create_new_db) { - err = srv_check_undo_redo_logs_exists(); - if (err != DB_SUCCESS) { - return(srv_init_abort(DB_ERROR)); - } - recv_sys.debug_free(); - } - - /* Open or create the data files. */ - ulint sum_of_new_sizes; - - err = srv_sys_space.open_or_create( - false, create_new_db, &sum_of_new_sizes, &flushed_lsn); - - switch (err) { - case DB_SUCCESS: - break; - case DB_CANNOT_OPEN_FILE: - ib::error() - << "Could not open or create the system tablespace. If" - " you tried to add new data files to the system" - " tablespace, and it failed here, you should now" - " edit innodb_data_file_path in my.cnf back to what" - " it was, and remove the new ibdata files InnoDB" - " created in this failed attempt. InnoDB only wrote" - " those files full of zeros, but did not yet use" - " them in any way. But be careful: do not remove" - " old data files which contain your precious data!"; - /* fall through */ - default: - /* Other errors might come from Datafile::validate_first_page() */ - return(srv_init_abort(err)); - } - srv_log_file_size_requested = srv_log_file_size; if (innodb_encrypt_temporary_tables && !log_crypt_init()) { @@ -1317,18 +1280,17 @@ dberr_t srv_start(bool create_new_db) std::string logfile0; bool create_new_log = create_new_db; if (create_new_db) { + err = srv_check_undo_redo_logs_exists(); + if (err != DB_SUCCESS) { + return(srv_init_abort(DB_ERROR)); + } + recv_sys.debug_free(); flushed_lsn = log_sys.get_lsn(); log_sys.set_flushed_lsn(flushed_lsn); err = create_log_file(true, flushed_lsn, logfile0); if (err != DB_SUCCESS) { - for (Tablespace::const_iterator - i = srv_sys_space.begin(); - i != srv_sys_space.end(); i++) { - os_file_delete(innodb_data_file_key, - i->filepath()); - } return(srv_init_abort(err)); } } else { @@ -1343,51 +1305,84 @@ dberr_t srv_start(bool create_new_db) } create_new_log = srv_log_file_size == 0; - if (create_new_log) { - if (flushed_lsn < lsn_t(1000)) { - ib::error() - << "Cannot create log file because" - " data files are corrupt or the" - " database was not shut down cleanly" - " after creating the data files."; - return srv_init_abort(DB_ERROR); - } + if (!create_new_log) { + srv_log_file_found = log_file_found; - srv_log_file_size = srv_log_file_size_requested; + log_sys.log.open_file(get_log_file_path()); - err = create_log_file(false, flushed_lsn, logfile0); + log_sys.log.create(); - if (err == DB_SUCCESS) { - err = create_log_file_rename(flushed_lsn, - logfile0); + if (!log_set_capacity( + srv_log_file_size_requested)) { + return(srv_init_abort(DB_ERROR)); } + /* Enable checkpoints in the page cleaner. */ + recv_sys.recovery_on = false; + + err= recv_recovery_read_max_checkpoint(); + if (err != DB_SUCCESS) { - return(srv_init_abort(err)); + return srv_init_abort(err); } - - /* Suppress the message about - crash recovery. */ - flushed_lsn = log_sys.get_lsn(); - goto file_checked; } + } + + /* Open or create the data files. */ + ulint sum_of_new_sizes; + + err = srv_sys_space.open_or_create( + false, create_new_db, &sum_of_new_sizes, &flushed_lsn); + + switch (err) { + case DB_SUCCESS: + break; + case DB_CANNOT_OPEN_FILE: + sql_print_error("InnoDB: Could not open or create the system" + " tablespace. If you tried to add new data files" + " to the system tablespace, and it failed here," + " you should now edit innodb_data_file_path" + " in my.cnf back to what it was, and remove the" + " new ibdata files InnoDB created in this failed" + " attempt. InnoDB only wrote those files full of" + " zeros, but did not yet use them in any way. But" + " be careful: do not remove old data files which" + " contain your precious data!"); + /* fall through */ + default: + /* Other errors might come from Datafile::validate_first_page() */ + return(srv_init_abort(err)); + } - srv_log_file_found = log_file_found; + if (!create_new_db && create_new_log) { + if (flushed_lsn < lsn_t(1000)) { + sql_print_error( + "InnoDB: Cannot create log file because" + " data files are corrupt or the" + " database was not shut down cleanly" + " after creating the data files."); + return srv_init_abort(DB_ERROR); + } - log_sys.log.open_file(get_log_file_path()); + srv_log_file_size = srv_log_file_size_requested; - log_sys.log.create(); + err = create_log_file(false, flushed_lsn, logfile0); + if (err == DB_SUCCESS) { + err = create_log_file_rename(flushed_lsn, logfile0); + } - if (!log_set_capacity(srv_log_file_size_requested)) { - return(srv_init_abort(DB_ERROR)); + if (err != DB_SUCCESS) { + return(srv_init_abort(err)); } - /* Enable checkpoints in the page cleaner. */ - recv_sys.recovery_on = false; + /* Suppress the message about + crash recovery. */ + flushed_lsn = log_sys.get_lsn(); + goto file_checked; } file_checked: - /* Open log file and data files in the systemtablespace: we keep + /* Open data files in the systemtablespace: we keep them open until database shutdown */ ut_d(fil_system.sys_space->recv_size = srv_sys_space_size_debug); From 3b32110ac4fd3e7e06eddb0189f78cba84c94b6f Mon Sep 17 00:00:00 2001 From: Ian Gilfillan Date: Mon, 15 Jan 2024 18:59:58 +0200 Subject: [PATCH 034/129] Update 11.1 HELP --- scripts/fill_help_tables.sql | 1706 +++++++++++++++++----------------- 1 file changed, 855 insertions(+), 851 deletions(-) diff --git a/scripts/fill_help_tables.sql b/scripts/fill_help_tables.sql index 876662dae7756..0eb727d895625 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 25 July 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 11.0 from the MariaDB Knowledge Base on 25 July 2023.','',''); +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 15 January 2024.','',''); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (2,9,'HELP_VERSION','Help Contents generated for MariaDB 11.1 from the MariaDB Knowledge Base on 15 January 2024.','',''); 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/'); @@ -133,7 +133,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 (47,4,'ATAN2','Syntax\n------\n\nATAN(Y,X), ATAN2(Y,X)\n\nDescription\n-----------\n\nReturns the arc tangent of the two variables X and Y. It is similar to\ncalculating the arc tangent of Y / X, except that the signs of both arguments\nare used to determine the quadrant of the result.\n\nExamples\n--------\n\nSELECT ATAN(-2,2);\n+---------------------+\n| ATAN(-2,2) |\n+---------------------+\n| -0.7853981633974483 |\n+---------------------+\n\nSELECT ATAN2(PI(),0);\n+--------------------+\n| ATAN2(PI(),0) |\n+--------------------+\n| 1.5707963267948966 |\n+--------------------+\n\nURL: https://mariadb.com/kb/en/atan2/','','https://mariadb.com/kb/en/atan2/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (48,4,'CEIL','Syntax\n------\n\nCEIL(X)\n\nDescription\n-----------\n\nCEIL() is a synonym for CEILING().\n\nURL: https://mariadb.com/kb/en/ceil/','','https://mariadb.com/kb/en/ceil/'); insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (49,4,'CEILING','Syntax\n------\n\nCEILING(X)\n\nDescription\n-----------\n\nReturns the smallest integer value not less than X.\n\nExamples\n--------\n\nSELECT CEILING(1.23);\n+---------------+\n| CEILING(1.23) |\n+---------------+\n| 2 |\n+---------------+\n\nSELECT CEILING(-1.23);\n+----------------+\n| CEILING(-1.23) |\n+----------------+\n| -1 |\n+----------------+\n\nURL: https://mariadb.com/kb/en/ceiling/','','https://mariadb.com/kb/en/ceiling/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (50,4,'CONV','Syntax\n------\n\nCONV(N,from_base,to_base)\n\nDescription\n-----------\n\nConverts numbers between different number bases. Returns a string\nrepresentation of the number N, converted from base from_base to base to_base.\n\nReturns NULL if any argument is NULL, or if the second or third argument are\nnot in the allowed range.\n\nThe argument N is interpreted as an integer, but may be specified as an\ninteger or a string. The minimum base is 2 and the maximum base is 36. If\nto_base is a negative number, N is regarded as a signed number. Otherwise, N\nis treated as unsigned. CONV() works with 64-bit precision.\n\nSome shortcuts for this function are also available: BIN(), OCT(), HEX(),\nUNHEX(). Also, MariaDB allows binary literal values and hexadecimal literal\nvalues.\n\nExamples\n--------\n\nSELECT CONV(\'a\',16,2);\n+----------------+\n| CONV(\'a\',16,2) |\n+----------------+\n| 1010 |\n+----------------+\n\nSELECT CONV(\'6E\',18,8);\n+-----------------+\n| CONV(\'6E\',18,8) |\n+-----------------+\n| 172 |\n+-----------------+\n\nSELECT CONV(-17,10,-18);\n+------------------+\n| CONV(-17,10,-18) |\n+------------------+\n| -H |\n+------------------+\n\nSELECT CONV(12+\'10\'+\'10\'+0xa,10,10);\n+------------------------------+\n| CONV(12+\'10\'+\'10\'+0xa,10,10) |\n+------------------------------+\n| 42 |\n+------------------------------+\n\nURL: https://mariadb.com/kb/en/conv/','','https://mariadb.com/kb/en/conv/'); +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (50,4,'CONV','Syntax\n------\n\nCONV(N,from_base,to_base)\n\nDescription\n-----------\n\nConverts numbers between different number bases. Returns a string\nrepresentation of the number N, converted from base from_base to base to_base.\n\nReturns NULL if any argument is NULL, or if the second or third argument are\nnot in the allowed range.\n\nThe argument N is interpreted as an integer, but may be specified as an\ninteger or a string. The minimum base is 2 and the maximum base is 36 (prior\nto MariaDB 11.4.0) or 62 (from MariaDB 11.4.0). If to_base is a negative\nnumber, N is regarded as a signed number. Otherwise, N is treated as unsigned.\nCONV() works with 64-bit precision.\n\nSome shortcuts for this function are also available: BIN(), OCT(), HEX(),\nUNHEX(). Also, MariaDB allows binary literal values and hexadecimal literal\nvalues.\n\nExamples\n--------\n\nSELECT CONV(\'a\',16,2);\n+----------------+\n| CONV(\'a\',16,2) |\n+----------------+\n| 1010 |\n+----------------+\n\nSELECT CONV(\'6E\',18,8);\n+-----------------+\n| CONV(\'6E\',18,8) |\n+-----------------+\n| 172 |\n+-----------------+\n\nSELECT CONV(-17,10,-18);\n+------------------+\n| CONV(-17,10,-18) |\n+------------------+\n| -H |\n+------------------+\n\nSELECT CONV(12+\'10\'+\'10\'+0xa,10,10);\n+------------------------------+\n| CONV(12+\'10\'+\'10\'+0xa,10,10) |\n+------------------------------+\n| 42 |\n+------------------------------+\n\nURL: https://mariadb.com/kb/en/conv/','','https://mariadb.com/kb/en/conv/'); 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/'); @@ -181,7 +181,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 (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. Note that\nunlike the global read_only mode, READ_ONLY ADMIN (and SUPER before MariaDB\n10.11.0) privilege doesn\'t allow writes and DDL statements on temporary tables\nare not allowed either.\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,'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 (note that the variable\nhas been renamed transaction_isolation from MariaDB 11.1.1, to match the\noption, and the old name deprecated).\n\nSELECT @@GLOBAL.tx_isolation, @@tx_isolation;\n\nFrom MariaDB 11.1.1:\n\nSELECT @@GLOBAL.transaction_isolation, @@transaction_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. Note that\nunlike the global read_only mode, READ_ONLY ADMIN (and SUPER before MariaDB\n10.11.0) privilege doesn\'t allow writes and DDL statements on temporary tables\nare not allowed either.\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 (97,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 only the next (not started)\ntransaction\n performed within the current session. After that it reverts to using the\nsession value.\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 [mariadb] section of an option file:\n\n[mariadb]\ntransaction-isolation = REPEATABLE-READ\nTo determine the global and session transaction isolation levels at runtime,\ncheck the value of the tx_isolation system variable (note that the variable\nhas been renamed transaction_isolation from MariaDB 11.1.1, to match the\noption, and the old name deprecated).\n\nSELECT @@GLOBAL.tx_isolation, @@tx_isolation;\n\nFrom MariaDB 11.1.1:\n\nSELECT @@GLOBAL.transaction_isolation, @@transaction_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 MariaDB 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. Note that\nunlike the global read_only mode, READ_ONLY ADMIN (and SUPER before MariaDB\n10.11.0) privilege doesn\'t allow writes and DDL statements on temporary tables\nare not allowed either.\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 (98,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:\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 (99,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 (100,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/'); @@ -189,16 +189,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 (102,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 (103,8,'WAIT and NOWAIT','Extended syntax so that it is possible to set innodb_lock_wait_timeout and\nlock_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 (104,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 (105,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 mariadb-admin 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. On Linux, the loopback interface (127.0.0.1) will not\nmatch \'localhost\' as it is not considered a local connection: this means that\nonly connections via UNIX-domain sockets will match \'localhost\'.\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 mariadb-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 = 105; -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (106,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 = 106; +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (105,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\nVIA and WITH are synonyms.\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 mariadb-admin 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. On Linux, the loopback interface (127.0.0.1) will not\nmatch \'localhost\' as it is not considered a local connection: this means that\nonly connections via UNIX-domain sockets will match \'localhost\'.\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 mariadb-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\nNote that FLUSH PRIVILEGES is only needed if one modifies the mysql tables\ndirectly. It is not needed when using CREATE USER, DROP USER, GRANT etc.\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 = 105; +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (106,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\nFor renaming an existing account (user name and/or host), see RENAME USER.\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 |','','https://mariadb.com/kb/en/alter-user/'); +update help_topic set description = CONCAT(description, '\n| | 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\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 = 106; insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (107,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 (108,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\nGlobal privileges do not take effect immediately and are only applied to\nconnections created after the GRANT statement was executed.\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 mariadb-admin processlist. If you have the PROCESS privilege, you can see\nall threads. 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 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','','https://mariadb.com/kb/en/grant/'); -update help_topic set description = CONCAT(description, '\n* sync_relay_log_info.\n\nAdded in MariaDB 10.5.2.\n\nSET USER\n--------\n\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 mariadb-admin 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 mariadb-admin debug command. Also, this permission allows\nthe user 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 (MDEV-21743). The\nprivileges are:\n\n* SET USER\n* FEDERATED ADMIN\n* CONNECTION ADMIN\n* REPLICATION SLAVE ADMIN\n* BINLOG ADMIN\n* BINLOG REPLAY\n* REPLICA MONITOR\n* BINLOG MONITOR\n* REPLICATION MASTER ADMIN\n* READ_ONLY ADMIN\n\nHowever, the smaller privileges are still a part of the SUPER grant in MariaDB\n10.5.2. From MariaDB 11.0.1 onwards, these grants are no longer a part of\nSUPER and need to be granted separately (MDEV-29668).\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 (MDEV-29596).\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 mariadb-upgrade |\n| | will 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 |\n| | TRIGGER, DROP TRIGGER, and SHOW CREATE |\n| | TRIGGER statements. |\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 |') WHERE help_topic_id = 108; -update help_topic set description = CONCAT(description, '\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 |\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 mariadb-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') WHERE help_topic_id = 108; -update help_topic set description = CONCAT(description, '\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+--------------------------------------+--------------------------------------+\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 mariadb-admin 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 = 108; -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (109,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 (108,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 | PACKAGE BODY\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\nGlobal privileges do not take effect immediately and are only applied to\nconnections created after the GRANT statement was executed.\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 mariadb-admin processlist. If you have the PROCESS privilege, you can see\nall threads. 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 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','','https://mariadb.com/kb/en/grant/'); +update help_topic set description = CONCAT(description, '\n* sync_relay_log, and\n* sync_relay_log_info.\n\nAdded in MariaDB 10.5.2.\n\nSET USER\n--------\n\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 mariadb-admin 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 mariadb-admin debug command. Also, this permission allows\nthe user 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 (MDEV-21743). The\nprivileges are:\n\n* SET USER\n* FEDERATED ADMIN\n* CONNECTION ADMIN\n* REPLICATION SLAVE ADMIN\n* BINLOG ADMIN\n* BINLOG REPLAY\n* REPLICA MONITOR\n* BINLOG MONITOR\n* REPLICATION MASTER ADMIN\n* READ_ONLY ADMIN\n\nHowever, the smaller privileges are still a part of the SUPER grant in MariaDB\n10.5.2. From MariaDB 11.0.1 onwards, these grants are no longer a part of\nSUPER and need to be granted separately (MDEV-29668).\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 (MDEV-29596).\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| SHOW CREATE ROUTINE | Permit viewing the SHOW CREATE |\n| | definition statement of a routine, for |\n| | example SHOW CREATE FUNCTION, even if |\n| | not the routine owner. From MariaDB |\n| | 11.3.0. |\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 mariadb-upgrade |\n| | will 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 | Required to run the CREATE TRIGGER, |\n| | DROP TRIGGER, and SHOW CREATE TRIGGER |\n| | statements. When another user |\n| | activates a trigger (running INSERT, |\n| | UPDATE, or DELETE statements on the |\n| | associated table), for the trigger to |\n| | execute, the user that defined the |\n| | trigger should have the TRIGGER |\n| | privilege for the table. The user |\n| | running the INSERT, UPDATE, or DELETE |\n| | statements on the table is not |\n| | required to have the TRIGGER privilege. |\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. |') WHERE help_topic_id = 108; +update help_topic set description = CONCAT(description, '\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 |\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 mariadb-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') WHERE help_topic_id = 108; +update help_topic set description = CONCAT(description, '\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+--------------------------------------+--------------------------------------+\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 mariadb-admin 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 = 108; +insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (109,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\nFor modifying an existing account, see ALTER USER.\n\nExamples\n--------\n\nCREATE USER \'donald\', \'mickey\';\nRENAME USER \'donald\' TO \'duck\'@\'localhost\', \'mickey\' TO \'mouse\'@\'localhost\';\n\nRenaming the host component of a user\n\nRENAME USER \'foo\'@\'1.2.3.4\' TO \'foo\'@\'10.20.30.40\';\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 (110,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 (111,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 (112,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/'); @@ -223,728 +223,731 @@ 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 (131,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 (132,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 (133,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 (134,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 (135,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 (136,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 (137,12,'RANDOM_BYTES','MariaDB starting with 10.10.0\n-----------------------------\nThe RANDOM_BYTES function generates a binary string of random bytes. It was\nadded in MariaDB 10.10.0.\n\nSyntax\n------\n\nRANDOM_BYTES(length)\n\nDescription\n-----------\n\nGiven a length from 1 to 1024, generates a binary string of length consisting\nof random bytes generated by the SSL library\'s random number generator.\n\nSee the RAND_bytes() function documentation of your SSL library for\ninformation on the random number generator. In the case of OpenSSL, a\ncryptographically secure pseudo random generator (CSPRNG) is used.\n\nStatements containing the RANDOM_BYTES function are unsafe for statement-based\nreplication.\n\nAn error occurs if length is outside the range 1 to 1024.\n\nURL: https://mariadb.com/kb/en/random_bytes/','','https://mariadb.com/kb/en/random_bytes/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (138,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 (139,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 (140,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 (141,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 (142,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 (143,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 (144,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 (145,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 (146,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 (147,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 (148,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 (149,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 mariadb-admin 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. timeout\nsupports microseconds.\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 (150,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 (151,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 (152,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 (153,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 (154,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 (155,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 (156,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 (157,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 (158,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 (159,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 (160,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 (161,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 (162,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 (163,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 (164,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 (165,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 (166,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 (167,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 (168,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 (169,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 (170,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 (171,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 (172,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 (173,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 (174,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 (175,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 (176,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 (177,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 (178,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 (179,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 (180,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 (181,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 (182,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 (183,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 (184,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 (185,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 (186,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 (187,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 (188,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 (189,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 (190,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 (191,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 (192,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 mariadb 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 (193,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 (194,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 (195,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 (196,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 (197,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 (198,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 (199,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 (200,17,'DATABASE','Syntax\n------\n\nDATABASE()\nSCHEMA()\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 mariadb 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 (201,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 (202,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 (203,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 (204,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 (205,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\nl_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\nl_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\nl_1f2f\nFROM t1;\n\n+------+------+------+--------+--------+--------+--------+--------+--------+\n| i | f_1f | l_1f | f_1p1f | l_1p1f | f_2p1p | l_2p1p | f_1f2f | l_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 (206,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 (207,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 (208,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 mariadb 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 (209,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 (210,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 (211,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 (212,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 (213,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 |\n| | (libmariadbd). |\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\nOne can fool these applications by setting the version string from the command\nline 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 (214,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 (215,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 (216,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 (217,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 (218,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 (219,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 (220,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 (221,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 (222,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 (223,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 (224,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 (225,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 (226,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 (227,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 (228,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 (229,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 (230,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 (231,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 (232,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 (233,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 (234,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 (235,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 (236,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 (237,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 (238,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 (239,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 (240,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 (241,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 (242,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 (243,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 (244,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 (245,21,'ANALYZE TABLE','Syntax\n------\n\nANALYZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name [,tbl_name ...]\n [PERSISTENT FOR\n { ALL\n | COLUMNS ([col_name [,col_name ...]]) INDEXES ([index_name [,index_name\n...]])\n }\n ]\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\nExamples\n--------\n\n-- update all engine-independent statistics for all columns and indexes\nANALYZE TABLE tbl PERSISTENT FOR ALL;\n\n-- update specific columns and indexes:\nANALYZE TABLE tbl PERSISTENT FOR COLUMNS (col1,col2,...) INDEXES\n(idx1,idx2,...);\n\n-- empty lists are allowed:\nANALYZE TABLE tbl PERSISTENT FOR COLUMNS (col1,col2,...) INDEXES ();\nANALYZE TABLE tbl PERSISTENT FOR COLUMNS () INDEXES (idx1,idx2,...);\n\n-- the following will only update mysql.table_stats fields:\nANALYZE TABLE tbl PERSISTENT FOR COLUMNS () INDEXES ();\n\n-- when use_stat_tables is set to \'COMPLEMENTARY\' or \'PREFERABLY\', \n-- a simple ANALYZE TABLE collects engine-independent statistics for all\ncolumns and indexes.\nSET SESSION use_stat_tables=\'COMPLEMENTARY\';\nANALYZE TABLE tbl;\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 (246,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 mariadb-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 InnoDB, Aria, |\n| NDE | and MyISAM, verify for each row that all its keys exists, and for |\n| | those index keys, they point back to the primary clustered key. |\n| | This may take a long time on large tables. This option was |\n| | previously ignored by InnoDB before MariaDB 10.6.11, MariaDB |\n| | 10.7.7, MariaDB 10.8.6 and MariaDB 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 (247,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 mariadb-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 (248,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 (249,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\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\nOPTIMIZE TABLE statements are not logged to the binary log if read_only is\nset. 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 (250,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 (251,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 mariadb-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 (252,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 (253,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\nmariadb-upgrade command 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 (254,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 (255,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 (256,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 (257,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 (258,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 (259,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 (260,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 (261,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 (262,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 (263,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 (264,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 (265,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 (266,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 (267,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 (268,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 (269,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 (270,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 (271,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\nBLOB and TEXT columns can both be assigned a DEFAULT value.\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\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 (272,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\nBLOB 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 (273,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 (274,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 (275,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 (276,23,'INET4','MariaDB starting with 10.10.0\n-----------------------------\nThe INET4 data type was added in MariaDB 10.10.0\n\nSyntax\n------\n\nINET4\n\nDescription\n-----------\n\nINET4 is a data type to store IPv4 addresses, as 4-byte binary strings.\n\nExamples\n--------\n\nCREATE OR REPLACE TABLE t1 (a INET4);\n\nINSERT INTO t1 VALUES(\'0.0.0.0\'), (\'255.10.0.0\'), (\'255.255.255.255\');\n\nINSERT INTO t1 VALUES (0xa0000001);\nINSERT INTO t1 VALUES (0xf0000000);\nINSERT INTO t1 VALUES (0xff000001);\n\nSELECT HEX(a), a FROM t1 ORDER BY a;\n+----------+-----------------+\n| HEX(a) | a |\n+----------+-----------------+\n| 00000000 | 0.0.0.0 |\n| A0000001 | 160.0.0.1 |\n| F0000000 | 240.0.0.0 |\n| FF000001 | 255.0.0.1 |\n| FF0A0000 | 255.10.0.0 |\n| FFFFFFFF | 255.255.255.255 |\n+----------+-----------------+\n\nURL: https://mariadb.com/kb/en/inet4/','','https://mariadb.com/kb/en/inet4/'); -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (277,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 = 277; -insert into help_topic (help_topic_id,help_category_id,name,description,example,url) values (278,23,'JSON Data Type','The JSON alias was added to make it possible to use JSON columns in statement\nbased replication from MySQL to MariaDB and to make it possible for MariaDB to\nread 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 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 (279,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 (280,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 (281,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 (282,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 (283,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 (284,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 (285,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 (286,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 (287,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 (288,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 (289,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 (290,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 (291,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 (292,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 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 mariadb-dump. 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 (293,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\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 (294,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 (298,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 (299,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 mariadb client for\nmore on 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 (300,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 (301,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 (302,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 (303,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 (304,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 <