aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/libmysql_r/sql
diff options
context:
space:
mode:
authorhcpp <hcpp@ydb.tech>2023-11-08 12:09:41 +0300
committerhcpp <hcpp@ydb.tech>2023-11-08 12:56:14 +0300
commita361f5b98b98b44ea510d274f6769164640dd5e1 (patch)
treec47c80962c6e2e7b06798238752fd3da0191a3f6 /contrib/libs/libmysql_r/sql
parent9478806fde1f4d40bd5a45e7cbe77237dab613e9 (diff)
downloadydb-a361f5b98b98b44ea510d274f6769164640dd5e1.tar.gz
metrics have been added
Diffstat (limited to 'contrib/libs/libmysql_r/sql')
-rw-r--r--contrib/libs/libmysql_r/sql/auth/i_sha2_password_common.h165
-rw-r--r--contrib/libs/libmysql_r/sql/auth/password.cc348
-rw-r--r--contrib/libs/libmysql_r/sql/auth/sha2_password_common.cc450
-rw-r--r--contrib/libs/libmysql_r/sql/auth/sha2_password_common.h34
-rw-r--r--contrib/libs/libmysql_r/sql/dd/object_id.h42
-rw-r--r--contrib/libs/libmysql_r/sql/dd/string_type.h95
-rw-r--r--contrib/libs/libmysql_r/sql/dd/types/object_table.h186
-rw-r--r--contrib/libs/libmysql_r/sql/discrete_interval.h208
-rw-r--r--contrib/libs/libmysql_r/sql/handler.h6919
-rw-r--r--contrib/libs/libmysql_r/sql/key.h345
-rw-r--r--contrib/libs/libmysql_r/sql/key_spec.h302
-rw-r--r--contrib/libs/libmysql_r/sql/lock.h69
-rw-r--r--contrib/libs/libmysql_r/sql/log_event.h4197
-rw-r--r--contrib/libs/libmysql_r/sql/malloc_allocator.h149
-rw-r--r--contrib/libs/libmysql_r/sql/mdl.h1756
-rw-r--r--contrib/libs/libmysql_r/sql/mem_root_array.h447
-rw-r--r--contrib/libs/libmysql_r/sql/memroot_allocator.h153
-rw-r--r--contrib/libs/libmysql_r/sql/partition_element.h175
-rw-r--r--contrib/libs/libmysql_r/sql/partition_info.h638
-rw-r--r--contrib/libs/libmysql_r/sql/psi_memory_key.h189
-rw-r--r--contrib/libs/libmysql_r/sql/query_options.h125
-rw-r--r--contrib/libs/libmysql_r/sql/rpl_constants.h75
-rw-r--r--contrib/libs/libmysql_r/sql/rpl_gtid.h3914
-rw-r--r--contrib/libs/libmysql_r/sql/rpl_reporting.h175
-rw-r--r--contrib/libs/libmysql_r/sql/rpl_tblmap.h99
-rw-r--r--contrib/libs/libmysql_r/sql/rpl_utility.h531
-rw-r--r--contrib/libs/libmysql_r/sql/select_lex_visitor.h57
-rw-r--r--contrib/libs/libmysql_r/sql/sql_bitmap.h228
-rw-r--r--contrib/libs/libmysql_r/sql/sql_cmd.h247
-rw-r--r--contrib/libs/libmysql_r/sql/sql_const.h454
-rw-r--r--contrib/libs/libmysql_r/sql/sql_data_change.h327
-rw-r--r--contrib/libs/libmysql_r/sql/sql_list.h828
-rw-r--r--contrib/libs/libmysql_r/sql/sql_plist.h269
-rw-r--r--contrib/libs/libmysql_r/sql/sql_plugin.h197
-rw-r--r--contrib/libs/libmysql_r/sql/sql_plugin_ref.h138
-rw-r--r--contrib/libs/libmysql_r/sql/stateless_allocator.h170
-rw-r--r--contrib/libs/libmysql_r/sql/stream_cipher.h231
-rw-r--r--contrib/libs/libmysql_r/sql/thr_malloc.h45
38 files changed, 24977 insertions, 0 deletions
diff --git a/contrib/libs/libmysql_r/sql/auth/i_sha2_password_common.h b/contrib/libs/libmysql_r/sql/auth/i_sha2_password_common.h
new file mode 100644
index 0000000000..69b289097d
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/auth/i_sha2_password_common.h
@@ -0,0 +1,165 @@
+/*
+Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License, version 2.0,
+as published by the Free Software Foundation.
+
+This program is also distributed with certain software (including
+but not limited to OpenSSL) that is licensed under separate terms,
+as designated in a particular file or component or in included license
+documentation. The authors of MySQL hereby grant you an additional
+permission to link the program and your derivative works with the
+separately licensed software that they have included with MySQL.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License, version 2.0, for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef I_SHA2_PASSWORD_COMMON_INCLUDED
+#define I_SHA2_PASSWORD_COMMON_INCLUDED
+
+#include <openssl/evp.h>
+#include "openssl/ossl_typ.h"
+#include "sha2.h" /* SHA256_DIGEST_LENGTH */
+
+#include <string>
+
+/**
+ @file sql/auth/i_sha2_password_common.h
+ Classes for caching_sha2_authentication plugin
+*/
+
+/**
+ @defgroup auth_caching_sha2_auth caching_sha2_authentication information
+ @{
+*/
+namespace sha2_password {
+/* Digest length for caching_sha2_authentication plugin */
+const unsigned int CACHING_SHA2_DIGEST_LENGTH = SHA256_DIGEST_LENGTH;
+
+/**
+ Supported digest information
+*/
+
+enum class Digest_info { SHA256_DIGEST = 0, DIGEST_LAST };
+
+/**
+ Interface for cryptographic digest generation
+*/
+
+class Generate_digest {
+ public:
+ virtual bool update_digest(const void *src, unsigned int length) = 0;
+ virtual bool retrieve_digest(unsigned char *digest, unsigned int length) = 0;
+ virtual void scrub() = 0;
+ virtual ~Generate_digest() {}
+};
+
+/**
+ SHA256 digest generator
+ @sa Generate_digest
+ @sa Digest_info
+*/
+
+class SHA256_digest : public Generate_digest {
+ public:
+ SHA256_digest();
+ ~SHA256_digest();
+
+ bool update_digest(const void *src, unsigned int length);
+ bool retrieve_digest(unsigned char *digest, unsigned int length);
+ void scrub();
+ bool all_ok() { return m_ok; }
+
+ private:
+ void init();
+ void deinit();
+
+ private:
+ /** Digest output buffer */
+ unsigned char m_digest[CACHING_SHA2_DIGEST_LENGTH];
+ /** Digest context */
+ EVP_MD_CTX *md_context;
+ /** Status */
+ bool m_ok;
+};
+
+/**
+ Scramble generator
+ Responsible for generating scramble of following format:
+ XOR(SHA2(m_src), SHA2(SHA2(SHA2(m_src)), m_rnd))
+ @sa SHA256_digest
+ @sa Digest_info
+*/
+
+class Generate_scramble {
+ public:
+ Generate_scramble(const std::string source, const std::string rnd,
+ Digest_info digest_type = Digest_info::SHA256_DIGEST);
+
+ ~Generate_scramble();
+
+ bool scramble(unsigned char *scramble, unsigned int scramble_length);
+
+ private:
+ /** plaintext source string */
+ std::string m_src;
+ /** random string */
+ std::string m_rnd;
+ /** Type of digest */
+ Digest_info m_digest_type;
+ /** Digest generator class */
+ Generate_digest *m_digest_generator;
+ /** length of the digest */
+ unsigned int m_digest_length;
+};
+
+/**
+ Scramble validator
+ Expects scramble to be:
+ XOR(SHA2(m_src), SHA2(SHA2(SHA2(m_src)), m_rnd))
+ Validates it against:
+ SHA2(SHA2(m_src)) and random string
+
+ @sa Generate_scramble
+ @sa SHA256_digest
+ @sa Digest_info
+*/
+
+class Validate_scramble {
+ public:
+ Validate_scramble(const unsigned char *scramble, const unsigned char *known,
+ const unsigned char *rnd, unsigned int rnd_length,
+ Digest_info digest_type = Digest_info::SHA256_DIGEST);
+
+ ~Validate_scramble();
+
+ bool validate();
+
+ private:
+ /** scramble to be validated */
+ const unsigned char *m_scramble;
+ /** SHA2(SHA2(plaintext_password)) */
+ const unsigned char *m_known;
+ /** random string */
+ const unsigned char *m_rnd;
+ /** random string length*/
+ unsigned int m_rnd_length;
+ /** Type of digest */
+ Digest_info m_digest_type;
+ /** Digest generator class */
+ Generate_digest *m_digest_generator;
+ /** length of the digest */
+ unsigned int m_digest_length;
+};
+} // namespace sha2_password
+
+/** @} (end of auth_caching_sha2_auth) */
+
+#endif // !I_SHA2_PASSWORD_INCLUDED
diff --git a/contrib/libs/libmysql_r/sql/auth/password.cc b/contrib/libs/libmysql_r/sql/auth/password.cc
new file mode 100644
index 0000000000..547829c0c2
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/auth/password.cc
@@ -0,0 +1,348 @@
+/*
+ Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ Without limiting anything contained in the foregoing, this file,
+ which is part of C Driver for MySQL (Connector/C), is also subject to the
+ Universal FOSS Exception, version 1.0, a copy of which can be found at
+ http://oss.oracle.com/licenses/universal-foss-exception.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/* password checking routines */
+/*****************************************************************************
+ The main idea is that no password are sent between client & server on
+ connection and that no password are saved in mysql in a decodable form.
+
+ On connection a random string is generated and sent to the client.
+ The client generates a new string with a random generator inited with
+ the hash values from the password and the sent string.
+ This 'check' string is sent to the server where it is compared with
+ a string generated from the stored hash_value of the password and the
+ random string.
+
+ The password is saved (in user.authentication_string).
+
+ Example:
+ SET PASSWORD for test = 'haha'
+ This saves a hashed number as a string in the authentication_string field.
+
+ The new authentication is performed in following manner:
+
+ SERVER: public_seed=generate_user_salt()
+ send(public_seed)
+
+ CLIENT: recv(public_seed)
+ hash_stage1=sha1("password")
+ hash_stage2=sha1(hash_stage1)
+ reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
+
+ // this three steps are done in scramble()
+
+ send(reply)
+
+
+ SERVER: recv(reply)
+ hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
+ candidate_hash2=sha1(hash_stage1)
+ check(candidate_hash2==hash_stage2)
+
+ // this three steps are done in check_scramble()
+
+*****************************************************************************/
+
+#include <string.h>
+#include <sys/types.h>
+
+#include "crypt_genhash_impl.h"
+#include "m_string.h"
+#include "my_inttypes.h"
+#include "my_macros.h"
+#include "mysql_com.h"
+#include "sha1.h"
+
+void randominit(struct rand_struct *rand_st, ulong seed1,
+ ulong seed2) { /* For mysql 3.21.# */
+ rand_st->max_value = 0x3FFFFFFFL;
+ rand_st->max_value_dbl = (double)rand_st->max_value;
+ rand_st->seed1 = seed1 % rand_st->max_value;
+ rand_st->seed2 = seed2 % rand_st->max_value;
+}
+
+/*
+ Generate binary hash from raw text string
+ Used for Pre-4.1 password handling
+ SYNOPSIS
+ hash_password()
+ result OUT store hash in this location
+ password IN plain text password to build hash
+ password_len IN password length (password may be not null-terminated)
+*/
+
+void hash_password(ulong *result, const char *password, uint password_len) {
+ ulong nr = 1345345333L, add = 7, nr2 = 0x12345671L;
+ ulong tmp;
+ const char *password_end = password + password_len;
+ for (; password < password_end; password++) {
+ if (*password == ' ' || *password == '\t')
+ continue; /* skip space in password */
+ tmp = (ulong)(uchar)*password;
+ nr ^= (((nr & 63) + add) * tmp) + (nr << 8);
+ nr2 += (nr2 << 8) ^ nr;
+ add += tmp;
+ }
+ result[0] = nr & (((ulong)1L << 31) - 1L); /* Don't use sign bit (str2int) */
+ ;
+ result[1] = nr2 & (((ulong)1L << 31) - 1L);
+}
+
+static inline uint8 char_val(uint8 X) {
+ return (uint)(X >= '0' && X <= '9'
+ ? X - '0'
+ : X >= 'A' && X <= 'Z' ? X - 'A' + 10 : X - 'a' + 10);
+}
+
+/* Character to use as version identifier for version 4.1 */
+
+#define PVERSION41_CHAR '*'
+
+/*
+ Convert given octet sequence to asciiz string of hex characters;
+ str..str+len and 'to' may not overlap.
+ SYNOPSIS
+ octet2hex()
+ buf OUT output buffer. Must be at least 2*len+1 bytes
+ str, len IN the beginning and the length of the input string
+
+ RETURN
+ buf+len*2
+*/
+
+char *octet2hex(char *to, const char *str, uint len) {
+ const char *str_end = str + len;
+ for (; str != str_end; ++str) {
+ *to++ = _dig_vec_upper[((uchar)*str) >> 4];
+ *to++ = _dig_vec_upper[((uchar)*str) & 0x0F];
+ }
+ *to = '\0';
+ return to;
+}
+
+/*
+ Convert given asciiz string of hex (0..9 a..f) characters to octet
+ sequence.
+ SYNOPSIS
+ hex2octet()
+ to OUT buffer to place result; must be at least len/2 bytes
+ str, len IN begin, length for character string; str and to may not
+ overlap; len % 2 == 0
+*/
+
+static void hex2octet(uint8 *to, const char *str, uint len) {
+ const char *str_end = str + len;
+ while (str < str_end) {
+ char tmp = char_val(*str++);
+ *to++ = (tmp << 4) | char_val(*str++);
+ }
+}
+
+/*
+ Encrypt/Decrypt function used for password encryption in authentication.
+ Simple XOR is used here but it is OK as we crypt random strings. Note,
+ that XOR(s1, XOR(s1, s2)) == s2, XOR(s1, s2) == XOR(s2, s1)
+ SYNOPSIS
+ my_crypt()
+ to OUT buffer to hold crypted string; must be at least len bytes
+ long; to and s1 (or s2) may be the same.
+ s1, s2 IN input strings (of equal length)
+ len IN length of s1 and s2
+*/
+
+static void my_crypt(char *to, const uchar *s1, const uchar *s2, uint len) {
+ const uint8 *s1_end = s1 + len;
+ while (s1 < s1_end) *to++ = *s1++ ^ *s2++;
+}
+
+#if defined(HAVE_OPENSSL)
+extern "C" void my_make_scrambled_password(char *to, const char *password,
+ size_t pass_len) {
+ char salt[CRYPT_SALT_LENGTH + 1];
+
+ generate_user_salt(salt, CRYPT_SALT_LENGTH + 1);
+ my_crypt_genhash(to, CRYPT_MAX_PASSWORD_SIZE, password, pass_len, salt, 0);
+}
+#endif
+/**
+ Compute two stage SHA1 hash of the password :
+
+ hash_stage1=sha1("password")
+ hash_stage2=sha1(hash_stage1)
+
+ @param [in] password Password string.
+ @param [in] pass_len Length of the password.
+ @param [out] hash_stage1 sha1(password)
+ @param [out] hash_stage2 sha1(hash_stage1)
+*/
+
+inline static void compute_two_stage_sha1_hash(const char *password,
+ size_t pass_len,
+ uint8 *hash_stage1,
+ uint8 *hash_stage2) {
+ /* Stage 1: hash password */
+ compute_sha1_hash(hash_stage1, password, pass_len);
+
+ /* Stage 2 : hash first stage's output. */
+ compute_sha1_hash(hash_stage2, (const char *)hash_stage1, SHA1_HASH_SIZE);
+}
+
+/*
+ MySQL 4.1.1 password hashing: SHA conversion (see RFC 2289, 3174) twice
+ applied to the password string, and then produced octet sequence is
+ converted to hex string.
+ The result of this function is stored in the database.
+ SYNOPSIS
+ my_make_scrambled_password_sha1()
+ buf OUT buffer of size 2*SHA1_HASH_SIZE + 2 to store hex string
+ password IN password string
+ pass_len IN length of password string
+*/
+
+void my_make_scrambled_password_sha1(char *to, const char *password,
+ size_t pass_len) {
+ uint8 hash_stage2[SHA1_HASH_SIZE];
+
+ /* Two stage SHA1 hash of the password. */
+ compute_two_stage_sha1_hash(password, pass_len, (uint8 *)to, hash_stage2);
+
+ /* convert hash_stage2 to hex string */
+ *to++ = PVERSION41_CHAR;
+ octet2hex(to, (const char *)hash_stage2, SHA1_HASH_SIZE);
+}
+
+/*
+ Wrapper around my_make_scrambled_password() to maintain client lib ABI
+ compatibility.
+ In server code usage of my_make_scrambled_password() is preferred to
+ avoid strlen().
+ SYNOPSIS
+ make_scrambled_password()
+ buf OUT buffer of size 2*SHA1_HASH_SIZE + 2 to store hex string
+ password IN NULL-terminated password string
+*/
+
+void make_scrambled_password(char *to, const char *password) {
+ my_make_scrambled_password_sha1(to, password, strlen(password));
+}
+
+/**
+ Produce an obscure octet sequence from password and random
+ string, received from the server. This sequence corresponds to the
+ password, but password can not be easily restored from it. The sequence
+ is then sent to the server for validation. Trailing zero is not stored
+ in the buf as it is not needed.
+ This function is used by client to create authenticated reply to the
+ server's greeting.
+
+ @param[out] to store scrambled string here. The buf must be at least
+ SHA1_HASH_SIZE bytes long.
+ @param message random message, must be exactly SCRAMBLE_LENGTH long and
+ NULL-terminated.
+ @param password users' password, NULL-terminated
+*/
+
+void scramble(char *to, const char *message, const char *password) {
+ uint8 hash_stage1[SHA1_HASH_SIZE];
+ uint8 hash_stage2[SHA1_HASH_SIZE];
+
+ /* Two stage SHA1 hash of the password. */
+ compute_two_stage_sha1_hash(password, strlen(password), hash_stage1,
+ hash_stage2);
+
+ /* create crypt string as sha1(message, hash_stage2) */;
+ compute_sha1_hash_multi((uint8 *)to, message, SCRAMBLE_LENGTH,
+ (const char *)hash_stage2, SHA1_HASH_SIZE);
+ my_crypt(to, (const uchar *)to, hash_stage1, SCRAMBLE_LENGTH);
+}
+
+/**
+ Check that scrambled message corresponds to the password.
+
+ The function is used by server to check that received reply is authentic.
+ This function does not check lengths of given strings: message must be
+ null-terminated, reply and hash_stage2 must be at least SHA1_HASH_SIZE
+ long (if not, something fishy is going on).
+
+ @param scramble_arg clients' reply, presumably produced by scramble()
+ @param message original random string, previously sent to client
+ (presumably second argument of scramble()), must be
+ exactly SCRAMBLE_LENGTH long and NULL-terminated.
+ @param hash_stage2 hex2octet-decoded database entry
+
+ @retval false password is correct
+ Wretval true password is invalid
+*/
+
+static bool check_scramble_sha1(const uchar *scramble_arg, const char *message,
+ const uint8 *hash_stage2) {
+ uint8 buf[SHA1_HASH_SIZE];
+ uint8 hash_stage2_reassured[SHA1_HASH_SIZE];
+
+ /* create key to encrypt scramble */
+ compute_sha1_hash_multi(buf, message, SCRAMBLE_LENGTH,
+ (const char *)hash_stage2, SHA1_HASH_SIZE);
+ /* encrypt scramble */
+ my_crypt((char *)buf, buf, scramble_arg, SCRAMBLE_LENGTH);
+
+ /* now buf supposedly contains hash_stage1: so we can get hash_stage2 */
+ compute_sha1_hash(hash_stage2_reassured, (const char *)buf, SHA1_HASH_SIZE);
+
+ return (memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE) != 0);
+}
+
+bool check_scramble(const uchar *scramble_arg, const char *message,
+ const uint8 *hash_stage2) {
+ return check_scramble_sha1(scramble_arg, message, hash_stage2);
+}
+
+/*
+ Convert scrambled password from asciiz hex string to binary form.
+
+ SYNOPSIS
+ get_salt_from_password()
+ res OUT buf to hold password. Must be at least SHA1_HASH_SIZE
+ bytes long.
+ password IN 4.1.1 version value of user.password
+*/
+
+void get_salt_from_password(uint8 *hash_stage2, const char *password) {
+ hex2octet(hash_stage2, password + 1 /* skip '*' */, SHA1_HASH_SIZE * 2);
+}
+
+/**
+ Convert scrambled password from binary form to asciiz hex string.
+
+ @param [out] to store resulting string here, 2*SHA1_HASH_SIZE+2 bytes
+ @param hash_stage2 password in salt format
+*/
+
+void make_password_from_salt(char *to, const uint8 *hash_stage2) {
+ *to++ = PVERSION41_CHAR;
+ octet2hex(to, (const char *)hash_stage2, SHA1_HASH_SIZE);
+}
diff --git a/contrib/libs/libmysql_r/sql/auth/sha2_password_common.cc b/contrib/libs/libmysql_r/sql/auth/sha2_password_common.cc
new file mode 100644
index 0000000000..7e02a54301
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/auth/sha2_password_common.cc
@@ -0,0 +1,450 @@
+/*
+ Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "sql/auth/sha2_password_common.h" /* validate_sha256_scramble */
+
+#include "my_config.h"
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+#include <openssl/evp.h>
+#include <string.h>
+#include <sys/types.h>
+#include <string>
+
+#ifdef _WIN32
+#include <malloc.h>
+#endif
+
+#include "my_compiler.h"
+#include "my_dbug.h" /* DBUG instrumentation */
+#include "my_inttypes.h" // IWYU pragma: keep
+#include "sql/auth/i_sha2_password_common.h"
+
+namespace sha2_password {
+/**
+ SHA256 digest generator constructor
+
+ Initializes digest context and sets
+ status of initialization.
+
+ If m_ok is set to false at the end,
+ it indicates a problem in initialization.
+*/
+
+SHA256_digest::SHA256_digest() : m_ok(false) { init(); }
+
+/**
+ Release acquired memory
+*/
+
+SHA256_digest::~SHA256_digest() { deinit(); }
+
+/**
+ Update digest with plaintext
+
+ @param [in] src Plaintext to be added
+ @param [in] length Length of the plaintext
+
+ @returns digest update status
+ @retval true Problem updating digest
+ @retval false Success
+*/
+
+bool SHA256_digest::update_digest(const void *src, unsigned int length) {
+ DBUG_ENTER("SHA256_digest::update_digest");
+ if (!m_ok || !src) {
+ DBUG_PRINT("info", ("Either digest context is not ok or "
+ "source is emptry string"));
+ DBUG_RETURN(true);
+ }
+ m_ok = EVP_DigestUpdate(md_context, src, length);
+ DBUG_RETURN(!m_ok);
+}
+
+/**
+ Retrive generated digest
+
+ @param [out] digest Digest text
+ @param [in] length Length of the digest buffer
+
+ Assumption : memory for digest has been allocated
+
+ @returns digest retrieval status
+ @retval true Error
+ @retval false Success
+*/
+
+bool SHA256_digest::retrieve_digest(unsigned char *digest,
+ unsigned int length) {
+ DBUG_ENTER("SHA256_digest::retrieve_digest");
+ if (!m_ok || !digest || length != CACHING_SHA2_DIGEST_LENGTH) {
+ DBUG_PRINT("info", ("Either digest context is not ok or "
+ "digest length is not as expected."));
+ DBUG_RETURN(true);
+ }
+ m_ok = EVP_DigestFinal_ex(md_context, m_digest, NULL);
+#if defined(HAVE_WOLFSSL) || OPENSSL_VERSION_NUMBER < 0x10100000L
+ EVP_MD_CTX_cleanup(md_context);
+#else /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+ EVP_MD_CTX_reset(md_context);
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+ memcpy(digest, m_digest, length);
+ DBUG_RETURN(!m_ok);
+}
+
+/**
+ Cleanup and reinit
+*/
+
+void SHA256_digest::scrub() {
+ deinit();
+ init();
+}
+
+/**
+ Initialize digest context
+
+ 1. Allocate memory for digest context
+ 2. Call initialization function(s)
+*/
+
+void SHA256_digest::init() {
+ DBUG_ENTER("SHA256_digest::init");
+ m_ok = false;
+ md_context = EVP_MD_CTX_create();
+ if (!md_context) {
+ DBUG_PRINT("info", ("Failed to create digest context"));
+ DBUG_VOID_RETURN;
+ }
+
+ m_ok = (bool)EVP_DigestInit_ex(md_context, EVP_sha256(), NULL);
+
+ if (!m_ok) {
+ EVP_MD_CTX_destroy(md_context);
+ md_context = NULL;
+ DBUG_PRINT("info", ("Failed to initialize digest context"));
+ }
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Release allocated memory for digest context
+*/
+
+void SHA256_digest::deinit() {
+ if (md_context) EVP_MD_CTX_destroy(md_context);
+ md_context = NULL;
+ m_ok = false;
+}
+
+/**
+ Generate_scramble constructor
+
+ @param [in] source Plaintext source
+ @param [in] rnd Salt
+ @param [in] digest_type Digest type
+*/
+Generate_scramble::Generate_scramble(
+ const std::string source, const std::string rnd,
+ Digest_info digest_type) /* = Digest_info::SHA256_DIGEST */
+ : m_src(source), m_rnd(rnd), m_digest_type(digest_type) {
+ switch (m_digest_type) {
+ case Digest_info::SHA256_DIGEST: {
+ m_digest_generator = new SHA256_digest();
+ m_digest_length = CACHING_SHA2_DIGEST_LENGTH;
+ break;
+ }
+ default:
+ DBUG_ASSERT(false);
+ };
+}
+
+/**
+ Generate_scramble destructor
+*/
+
+Generate_scramble::~Generate_scramble() {
+ if (m_digest_generator) delete m_digest_generator;
+ m_digest_generator = 0;
+}
+
+/**
+ Scramble generation
+
+ @param [out] scramble Output buffer for generated scramble
+ @param [in] scramble_length Size of scramble buffer
+
+ @note
+ SHA2(src) => digest_stage1
+ SHA2(digest_stage1) => digest_stage2
+ SHA2(digest_stage2, m_rnd) => scramble_stage1
+ XOR(digest_stage1, scramble_stage1) => scramble
+
+ @returns Status of scramble generation
+ @retval true Error generating scramble
+ @retval false Success
+*/
+
+bool Generate_scramble::scramble(unsigned char *scramble,
+ unsigned int scramble_length) {
+ DBUG_ENTER("Generate_scramble::scramble");
+ unsigned char *digest_stage1;
+ unsigned char *digest_stage2;
+ unsigned char *scramble_stage1;
+
+ if (!scramble || scramble_length != m_digest_length) {
+ DBUG_PRINT("info", ("Unexpected scrable length"
+ "Expected: %d, Actual: %d",
+ m_digest_length, !scramble ? 0 : scramble_length));
+ DBUG_RETURN(true);
+ }
+
+ switch (m_digest_type) {
+ case Digest_info::SHA256_DIGEST: {
+ digest_stage1 = (unsigned char *)alloca(m_digest_length);
+ digest_stage2 = (unsigned char *)alloca(m_digest_length);
+ scramble_stage1 = (unsigned char *)alloca(m_digest_length);
+ break;
+ }
+ default: {
+ DBUG_ASSERT(false);
+ DBUG_RETURN(true);
+ }
+ }
+
+ /* SHA2(src) => digest_stage1 */
+ if (m_digest_generator->update_digest(m_src.c_str(), m_src.length()) ||
+ m_digest_generator->retrieve_digest(digest_stage1, m_digest_length)) {
+ DBUG_PRINT("info", ("Failed to generate digest_stage1: SHA2(src)"));
+ DBUG_RETURN(true);
+ }
+
+ /* SHA2(digest_stage1) => digest_stage2 */
+ m_digest_generator->scrub();
+ if (m_digest_generator->update_digest(digest_stage1, m_digest_length) ||
+ m_digest_generator->retrieve_digest(digest_stage2, m_digest_length)) {
+ DBUG_PRINT("info",
+ ("Failed to generate digest_stage2: SHA2(digest_stage1)"));
+ DBUG_RETURN(true);
+ }
+
+ /* SHA2(digest_stage2, m_rnd) => scramble_stage1 */
+ m_digest_generator->scrub();
+ if (m_digest_generator->update_digest(digest_stage2, m_digest_length) ||
+ m_digest_generator->update_digest(m_rnd.c_str(), m_rnd.length()) ||
+ m_digest_generator->retrieve_digest(scramble_stage1, m_digest_length)) {
+ DBUG_PRINT("info", ("Failed to generate scrmable_stage1: "
+ "SHA2(digest_stage2, m_rnd)"));
+ DBUG_RETURN(true);
+ }
+
+ /* XOR(digest_stage1, scramble_stage1) => scramble */
+ for (uint i = 0; i < m_digest_length; ++i)
+ scramble[i] = (digest_stage1[i] ^ scramble_stage1[i]);
+
+ DBUG_RETURN(false);
+}
+
+/**
+ Validate scramble constructor
+ @param [in] scramble Scramble to be validated
+ @param [in] known Known digest against which scramble is to be verified
+ @param [in] rnd Salt
+ @param [in] rnd_length Length of the salt buffer
+ @param [in] digest_type Type od digest
+*/
+
+Validate_scramble::Validate_scramble(
+ const unsigned char *scramble, const unsigned char *known,
+ const unsigned char *rnd, unsigned int rnd_length,
+ Digest_info digest_type) /* = Digest_info::SHA256_DIGEST */
+ : m_scramble(scramble),
+ m_known(known),
+ m_rnd(rnd),
+ m_rnd_length(rnd_length),
+ m_digest_type(digest_type) {
+ switch (m_digest_type) {
+ case Digest_info::SHA256_DIGEST: {
+ m_digest_generator = new SHA256_digest();
+ m_digest_length = CACHING_SHA2_DIGEST_LENGTH;
+ break;
+ }
+ default:
+ DBUG_ASSERT(false);
+ break;
+ };
+}
+
+/** Validate_scramble destructor */
+
+Validate_scramble::~Validate_scramble() {
+ if (m_digest_generator) delete m_digest_generator;
+ m_digest_generator = 0;
+}
+
+/**
+ Validate the scramble
+
+ @note
+ SHA2(known, rnd) => scramble_stage1
+ XOR(scramble, scramble_stage1) => digest_stage1
+ SHA2(digest_stage1) => digest_stage2
+ m_known == digest_stage2
+
+ @returns Result of validation process
+ @retval false Successful validation
+ @retval true Error
+*/
+
+bool Validate_scramble::validate() {
+ DBUG_ENTER("Validate_scramble::validate");
+ unsigned char *digest_stage1 = 0;
+ unsigned char *digest_stage2 = 0;
+ unsigned char *scramble_stage1 = 0;
+
+ switch (m_digest_type) {
+ case Digest_info::SHA256_DIGEST: {
+ digest_stage1 = (unsigned char *)alloca(m_digest_length);
+ digest_stage2 = (unsigned char *)alloca(m_digest_length);
+ scramble_stage1 = (unsigned char *)alloca(m_digest_length);
+ break;
+ }
+ default: {
+ DBUG_ASSERT(false);
+ DBUG_RETURN(true);
+ }
+ }
+
+ /* SHA2(known, m_rnd) => scramble_stage1 */
+ if (m_digest_generator->update_digest(m_known, m_digest_length) ||
+ m_digest_generator->update_digest(m_rnd, m_rnd_length) ||
+ m_digest_generator->retrieve_digest(scramble_stage1, m_digest_length)) {
+ DBUG_PRINT("info",
+ ("Failed to generate scramble_stage1: SHA2(known, m_rnd)"));
+ DBUG_RETURN(true);
+ }
+
+ /* XOR(scramble, scramble_stage1) => digest_stage1 */
+ for (unsigned int i = 0; i < m_digest_length; ++i)
+ digest_stage1[i] = (m_scramble[i] ^ scramble_stage1[i]);
+
+ /* SHA2(digest_stage1) => digest_stage2 */
+ m_digest_generator->scrub();
+ if (m_digest_generator->update_digest(digest_stage1, m_digest_length) ||
+ m_digest_generator->retrieve_digest(digest_stage2, m_digest_length)) {
+ DBUG_PRINT("info",
+ ("Failed to generate digest_stage2: SHA2(digest_stage1)"));
+ DBUG_RETURN(true);
+ }
+
+ /* m_known == digest_stage2 */
+ if (memcmp(m_known, digest_stage2, m_digest_length) == 0) DBUG_RETURN(false);
+
+ DBUG_RETURN(true);
+}
+} // namespace sha2_password
+
+/*
+ Generate scramble from password and random number.
+
+ @param [out] scramble Buffer to put generated scramble
+ @param [in] scramble_size Size of the output buffer
+ @param [in] src Source text buffer
+ @param [in] src_size Source text buffer size
+ @param [in] rnd Random text buffer
+ @param [in] rnd_size Random text buffer size
+
+ @note
+ SHA2(src) => X
+ SHA2(X) => Y
+ SHA2(XOR(rnd, Y) => Z
+ XOR(X, Z) => scramble
+
+ @returns Status of scramble generation
+ @retval true Error
+ @retval false Generation successful
+
+*/
+
+bool generate_sha256_scramble(unsigned char *scramble, size_t scramble_size,
+ const char *src, size_t src_size, const char *rnd,
+ size_t rnd_size) {
+ DBUG_ENTER("generate_scramble");
+ std::string source(src, src_size);
+ std::string random(rnd, rnd_size);
+
+ sha2_password::Generate_scramble scramble_generator(source, random);
+ if (scramble_generator.scramble(scramble, scramble_size)) {
+ DBUG_PRINT("info", ("Failed to generate SHA256 based scramble"));
+ DBUG_RETURN(true);
+ }
+
+ DBUG_RETURN(false);
+}
+
+/*
+ Validate scramble against known text
+
+ @param [in] scramble Buffer with scramble to be checked
+ @param [in] scramble_size Size of scramble buffer
+ @param [in] known Buffer with known text to compare against
+ @param [in] known_size Size of know text buffer
+ @param [in] rnd Buffer with random text
+ @param [in] rnd_size Size of random text buffer
+
+ @note
+ XOR(SHA2(secret), SHA2(XOR(rnd, SHA2(SHA2(secret))))) => scramble
+ SHA2(SHA2(secret)) => known
+
+ Validation:
+ scramble is: XOR(SHA2(secret1), SHA2(XOR(rnd, SHA2(SHA2(secret1)))))
+ known is: SHA2(SHA2(secret2))
+ Our aim is to check secret1 == secret2
+ - From known and rnd we generate: SHA2(XOR(rnd, scramble))
+ Let's call it X
+ - We then do : XOR(X, scramble) => Let's call this Y
+ If secret1 == secret2, this should give us SHA2(secret1)
+ - We then do SHA2(Y).
+ If secret1 == secret2, this should give us SHA2(SHA2(secret1))
+ - If SHA(Y) == known, then we have established that
+ secret1 == secret2
+
+ @returns status of validation
+ @retval true scramble does not match known text
+ @retval false scramble matches known text
+
+*/
+
+bool validate_sha256_scramble(const unsigned char *scramble,
+ size_t scramble_size MY_ATTRIBUTE((unused)),
+ const unsigned char *known,
+ size_t known_size MY_ATTRIBUTE((unused)),
+ const unsigned char *rnd, size_t rnd_size) {
+ DBUG_ENTER("validate_scramble");
+
+ sha2_password::Validate_scramble scramble_validator(scramble, known, rnd,
+ rnd_size);
+ DBUG_RETURN(scramble_validator.validate());
+}
diff --git a/contrib/libs/libmysql_r/sql/auth/sha2_password_common.h b/contrib/libs/libmysql_r/sql/auth/sha2_password_common.h
new file mode 100644
index 0000000000..4cb28a79e5
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/auth/sha2_password_common.h
@@ -0,0 +1,34 @@
+/*
+ Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SHA2_PASSWORD_INCLUDED
+#define SHA2_PASSWORD_INCLUDED
+
+#include <stddef.h>
+
+bool validate_sha256_scramble(const unsigned char *scramble,
+ size_t scramble_size, const unsigned char *known,
+ size_t known_size, const unsigned char *rnd,
+ size_t rnd_size);
+
+#endif // SHA2_PASSWORD_INCLUDED
diff --git a/contrib/libs/libmysql_r/sql/dd/object_id.h b/contrib/libs/libmysql_r/sql/dd/object_id.h
new file mode 100644
index 0000000000..c681420b19
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/dd/object_id.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef DD__OBJECT_ID_INCLUDED
+#define DD__OBJECT_ID_INCLUDED
+
+namespace dd {
+
+///////////////////////////////////////////////////////////////////////////
+
+typedef unsigned long long Object_id;
+
+/**
+ The default object ID which represents that the DD object is
+ new and not persistent in dictionary tables yet.
+*/
+const Object_id INVALID_OBJECT_ID = (Object_id)-1;
+
+///////////////////////////////////////////////////////////////////////////
+
+} // namespace dd
+
+#endif // DD__OBJECT_ID_INCLUDED
diff --git a/contrib/libs/libmysql_r/sql/dd/string_type.h b/contrib/libs/libmysql_r/sql/dd/string_type.h
new file mode 100644
index 0000000000..f8930941ef
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/dd/string_type.h
@@ -0,0 +1,95 @@
+/* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef DD__STRING_TYPE
+#define DD__STRING_TYPE
+
+#include <stddef.h>
+#include <sstream>
+#include <string>
+#include <system_error>
+
+#include "sql/stateless_allocator.h" // Stateless_allocator
+
+namespace dd {
+/**
+ Functor class which allocates memory for String_type. Implementation
+ uses my_malloc with key_memory_DD_String_type.
+*/
+struct String_type_alloc {
+ void *operator()(size_t s) const;
+};
+
+typedef Stateless_allocator<char, String_type_alloc> String_type_allocator;
+
+/**
+ Template alias for char-based std::basic_string.
+*/
+template <class A>
+using Char_string_template = std::basic_string<char, std::char_traits<char>, A>;
+
+typedef Char_string_template<String_type_allocator> String_type;
+
+/**
+ Template alias for char-based std::basic_stringstream.
+ */
+template <class A>
+using Char_stringstream_template =
+ std::basic_stringstream<char, std::char_traits<char>, A>;
+
+/**
+ Instantiation of std::basic_stringstream with the same allocator as
+ String_type. This is needed since a std::basic_stringstream::str()
+ returns a basic_string allocated with its own allocator. Note that
+ this also means that it is diffcult to use a different PSI key for
+ the stream memory as that would mean the return type of
+ Stringstream_type::str() would be different and incompatible with
+ String_type.
+
+ To work around this would require the creation of a temporary
+ String_type from the string returned from stringstream::str().
+*/
+typedef Char_stringstream_template<String_type_allocator> Stringstream_type;
+
+template <typename LEX_STRING_TYPE>
+String_type make_string_type(const LEX_STRING_TYPE &lst) {
+ return {lst.str, lst.length};
+}
+} // namespace dd
+
+namespace std {
+
+/**
+ Specialize std::hash for dd::String_type so that it can be
+ used with unordered_ containers. Uses our standard murmur3_32
+ implementation, and the same suitability restrictions apply.
+ @see murmur3_32
+*/
+template <>
+struct hash<dd::String_type> {
+ typedef dd::String_type argument_type;
+ typedef size_t result_type;
+
+ size_t operator()(const dd::String_type &s) const;
+};
+} // namespace std
+#endif /* DD__STRING_TYPE */
diff --git a/contrib/libs/libmysql_r/sql/dd/types/object_table.h b/contrib/libs/libmysql_r/sql/dd/types/object_table.h
new file mode 100644
index 0000000000..53cd7a6e93
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/dd/types/object_table.h
@@ -0,0 +1,186 @@
+/* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef DD__OBJECT_TABLE_INCLUDED
+#define DD__OBJECT_TABLE_INCLUDED
+
+#include "my_inttypes.h"
+#include "sql/dd/string_type.h" // dd::String_type
+
+class THD;
+
+namespace dd {
+
+///////////////////////////////////////////////////////////////////////////
+
+class Object_table_definition;
+class Properties;
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ This class represents all data dictionary table like mysql.tables,
+ mysql.columns and more. This is the base class of all the classes
+ defined in sql/dd/impl/tables/ headers. This class is also the base
+ class of tables requested by the DDSE and by plugins.
+
+ The server code should contain a Object_table subclass for each DD table
+ which is a target table for at least one of the supported DD versions (i.e.,
+ the DD versions from which this server can upgrade). So even if a previous
+ DD version stops using a DD table, the later servers which can upgrade need
+ to keep the Object_table subclass for that table. The motivation for that
+ is to be able to recognize the table, and to be able to remove it.
+
+ Instances of this class will contain one or two table definitions, depending
+ on the context:
+
+ - The actual table definition reflects the persistently stored DD table,
+ i.e., what is reflected in the persistently stored mete data.
+ - The target table definition reflects the DD table which the server
+ is using during normal operation.
+
+ If the actual DD version is different from the target DD version, upgrade
+ is required. The actual table definition is used only in situations where
+ we have an upgrade or downgrade.
+
+ @note This class may be inherited along different paths
+ for some subclasses due to the diamond shaped
+ inheritance hierarchy; thus, direct subclasses
+ must inherit this class virtually.
+*/
+
+class Object_table {
+ public:
+ /**
+ Allocate a new Object_table instance on the heap.
+
+ The new instance has the predefined options that all DD tables share:
+
+ ENGINE=INNODB
+ DEFAULT CHARSET=utf8
+ COLLATE=utf8_bin
+ ROW_FORMAT=DYNAMIC
+ STATS_PERSISTENT=0
+ TABLESPACE=mysql
+
+ @note The object is owned by the caller.
+
+ @returns pointer to new Object_table instance.
+ */
+ static Object_table *create_object_table();
+
+ /**
+ Get the table name used by the target definition for the dictionary table.
+
+ @return table name.
+ */
+ virtual const String_type &name() const = 0;
+
+ /**
+ Get the target definition for the dictionary table.
+
+ @note There are const and non-const variants.
+
+ @return Pointer to the definition of the table.
+ */
+ virtual Object_table_definition *target_table_definition() = 0;
+
+ virtual const Object_table_definition *target_table_definition() const = 0;
+
+ /**
+ Mark the target definition for the dictionary table as abandoned.
+
+ @param last_dd_version Last version where this object table was used.
+ */
+ virtual void set_abandoned(uint last_dd_version) const = 0;
+
+ /**
+ Check if the dictionary table is abandoned.
+
+ @return true if the table is abandoned.
+ */
+ virtual bool is_abandoned() const = 0;
+
+ /**
+ Get the actual definition for the dictionary table.
+
+ The actual definition is the definition which is used by a DD table
+ which is stored persistently. Normally, for an ordinary running server,
+ the actual table definitions are equal to the target table definitions.
+ In an upgrade context, they may differ.
+
+ @return Pointer to the definition of the table.
+ */
+ virtual const Object_table_definition *actual_table_definition() const = 0;
+
+ /**
+ Set the actual definition for the dictionary table.
+
+ @param table_def_properties Actual table definition represented as
+ a set of properties.
+
+ @return false if no error.
+ */
+ virtual bool set_actual_table_definition(
+ const Properties &table_def_properties) const = 0;
+
+ /**
+ Get the field ordinal position in the object table.
+
+ @return Integer ordinal position.
+ */
+ virtual int field_number(const String_type &field_label) const = 0;
+
+ /**
+ Execute low level code for populating the table.
+
+ @return Boolean operation outcome, false if success.
+ */
+ virtual bool populate(THD *thd) const = 0;
+
+ /**
+ Check if the table should be hidden.
+
+ Most of Object tables (alias DD tables) are hidden from users,
+ but some of them are expected to be visible (not hidden) to user and be
+ able to update them, e.g., innodb_index_stats/innodb_table_stats.
+
+ @returns true if the table should be hidden.
+ */
+ virtual bool is_hidden() const = 0;
+
+ /**
+ Mark the dictionary table as hidden or visible.
+
+ @param hidden Set to 'true' if the table should be hidden.
+ */
+ virtual void set_hidden(bool hidden) = 0;
+
+ public:
+ virtual ~Object_table() {}
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+} // namespace dd
+
+#endif // DD__OBJECT_TABLE_INCLUDED
diff --git a/contrib/libs/libmysql_r/sql/discrete_interval.h b/contrib/libs/libmysql_r/sql/discrete_interval.h
new file mode 100644
index 0000000000..338298557b
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/discrete_interval.h
@@ -0,0 +1,208 @@
+#ifndef DISCRETE_INTERVAL_INCLUDED
+#define DISCRETE_INTERVAL_INCLUDED
+
+/* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <limits.h>
+
+#include "my_dbug.h"
+#include "my_inttypes.h"
+
+/*
+ Such interval is "discrete": it is the set of
+ { auto_inc_interval_min + k * increment,
+ 0 <= k <= (auto_inc_interval_values-1) }
+ Where "increment" is maintained separately by the user of this class (and is
+ currently only thd->variables.auto_increment_increment).
+ It mustn't be allocated on a MEM_ROOT, because SET INSERT_ID needs to
+ allocate memory which must stay allocated for use by the next statement.
+*/
+class Discrete_interval {
+ private:
+ ulonglong interval_min;
+ ulonglong interval_values;
+ ulonglong interval_max; // excluded bound. Redundant.
+ public:
+ Discrete_interval *next; // used when linked into Discrete_intervals_list
+
+ /// Determine if the given value is within the interval
+ bool in_range(const ulonglong value) const {
+ return ((value >= interval_min) && (value < interval_max));
+ }
+
+ void replace(ulonglong start, ulonglong val, ulonglong incr) {
+ interval_min = start;
+ interval_values = val;
+ interval_max = (val == ULLONG_MAX) ? val : start + val * incr;
+ }
+ Discrete_interval(ulonglong start, ulonglong val, ulonglong incr)
+ : next(NULL) {
+ replace(start, val, incr);
+ }
+ Discrete_interval() : next(NULL) { replace(0, 0, 0); }
+ ulonglong minimum() const { return interval_min; }
+ ulonglong values() const { return interval_values; }
+ ulonglong maximum() const { return interval_max; }
+ /*
+ If appending [3,5] to [1,2], we merge both in [1,5] (they should have the
+ same increment for that, user of the class has to ensure that). That is
+ just a space optimization. Returns 0 if merge succeeded.
+ */
+ bool merge_if_contiguous(ulonglong start, ulonglong val, ulonglong incr) {
+ if (interval_max == start) {
+ if (val == ULLONG_MAX) {
+ interval_values = interval_max = val;
+ } else {
+ interval_values += val;
+ interval_max = start + val * incr;
+ }
+ return 0;
+ }
+ return 1;
+ }
+};
+
+/// List of Discrete_interval objects
+class Discrete_intervals_list {
+/**
+ Discrete_intervals_list objects are used to remember the
+ intervals of autoincrement values that have been used by the
+ current INSERT statement, so that the values can be written to the
+ binary log. However, the binary log can currently only store the
+ beginning of the first interval (because WL#3404 is not yet
+ implemented). Hence, it is currently not necessary to store
+ anything else than the first interval, in the list. When WL#3404 is
+ implemented, we should change the '# define' below.
+*/
+#define DISCRETE_INTERVAL_LIST_HAS_MAX_ONE_ELEMENT 1
+
+ private:
+ /**
+ To avoid heap allocation in the common case when there is only one
+ interval in the list, we store the first interval here.
+ */
+ Discrete_interval first_interval;
+ Discrete_interval *head;
+ Discrete_interval *tail;
+ /**
+ When many intervals are provided at the beginning of the execution of a
+ statement (in a replication slave or SET INSERT_ID), "current" points to
+ the interval being consumed by the thread now (so "current" goes from
+ "head" to "tail" then to NULL).
+ */
+ Discrete_interval *current;
+ uint elements; ///< number of elements
+ void operator=(Discrete_intervals_list &); // prevent use of this
+ bool append(Discrete_interval *new_interval) {
+ if (unlikely(new_interval == NULL)) return true;
+ DBUG_PRINT("info", ("adding new auto_increment interval"));
+ if (head == NULL)
+ head = current = new_interval;
+ else
+ tail->next = new_interval;
+ tail = new_interval;
+ elements++;
+ return false;
+ }
+ void copy_shallow(const Discrete_intervals_list *other) {
+ const Discrete_interval *o_first_interval = &other->first_interval;
+ first_interval = other->first_interval;
+ head = other->head == o_first_interval ? &first_interval : other->head;
+ tail = other->tail == o_first_interval ? &first_interval : other->tail;
+ current =
+ other->current == o_first_interval ? &first_interval : other->current;
+ elements = other->elements;
+ }
+ Discrete_intervals_list(const Discrete_intervals_list &other) {
+ copy_shallow(&other);
+ }
+
+ public:
+ Discrete_intervals_list()
+ : head(NULL), tail(NULL), current(NULL), elements(0) {}
+ void empty() {
+ if (head) {
+ // first element, not on heap, should not be delete-d; start with next:
+ for (Discrete_interval *i = head->next; i;) {
+#ifdef DISCRETE_INTERVAL_LIST_HAS_MAX_ONE_ELEMENT
+ DBUG_ASSERT(0);
+#endif
+ Discrete_interval *next = i->next;
+ delete i;
+ i = next;
+ }
+ }
+ head = tail = current = NULL;
+ elements = 0;
+ }
+ void swap(Discrete_intervals_list *other) {
+ const Discrete_intervals_list tmp(*other);
+ other->copy_shallow(this);
+ copy_shallow(&tmp);
+ }
+ const Discrete_interval *get_next() {
+ const Discrete_interval *tmp = current;
+ if (current != NULL) current = current->next;
+ return tmp;
+ }
+ ~Discrete_intervals_list() { empty(); }
+ /**
+ Appends an interval to the list.
+
+ @param start start of interval
+ @param val how many values it contains
+ @param incr what increment between each value
+ @retval true error
+ @retval false success
+ */
+ bool append(ulonglong start, ulonglong val, ulonglong incr) {
+ // If there are no intervals, add one.
+ if (head == NULL) {
+ first_interval.replace(start, val, incr);
+ return append(&first_interval);
+ }
+ // If this interval can be merged with previous, do that.
+ if (tail->merge_if_contiguous(start, val, incr) == 0) return false;
+ // If this interval cannot be merged, append it.
+#ifdef DISCRETE_INTERVAL_LIST_HAS_MAX_ONE_ELEMENT
+ /*
+ We cannot create yet another interval as we already contain one. This
+ situation can happen. Assume innodb_autoinc_lock_mode>=1 and
+ CREATE TABLE T(A INT AUTO_INCREMENT PRIMARY KEY) ENGINE=INNODB;
+ INSERT INTO T VALUES (NULL),(NULL),(1025),(NULL);
+ Then InnoDB will reserve [1,4] (because of 4 rows) then
+ [1026,1026]. Only the first interval is important for
+ statement-based binary logging as it tells the starting point. So we
+ ignore the second interval:
+ */
+ return false;
+#else
+ return append(new Discrete_interval(start, val, incr));
+#endif
+ }
+ ulonglong minimum() const { return (head ? head->minimum() : 0); }
+ ulonglong maximum() const { return (head ? tail->maximum() : 0); }
+ uint nb_elements() const { return elements; }
+};
+
+#endif /* DISCRETE_INTERVAL_INCLUDED */
diff --git a/contrib/libs/libmysql_r/sql/handler.h b/contrib/libs/libmysql_r/sql/handler.h
new file mode 100644
index 0000000000..7d7ebe46fc
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/handler.h
@@ -0,0 +1,6919 @@
+#ifndef HANDLER_INCLUDED
+#define HANDLER_INCLUDED
+
+/*
+ Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/* Definitions for parameters to do with handler-routines */
+
+#include <fcntl.h>
+#include <float.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <algorithm>
+#include <bitset>
+#include <functional>
+#include <map>
+#include <random> // std::mt19937
+#include <set>
+#include <string>
+
+#include <mysql/components/services/page_track_service.h>
+#include "ft_global.h" // ft_hints
+#include "lex_string.h"
+#include "m_ctype.h"
+#include "map_helpers.h"
+#include "my_alloc.h"
+#include "my_base.h"
+#include "my_bitmap.h"
+#include "my_compiler.h"
+#include "my_dbug.h"
+#include "my_double2ulonglong.h"
+#include "my_inttypes.h"
+#include "my_io.h"
+#include "my_sys.h"
+#include "my_thread_local.h" // my_errno
+#include "mysql/components/services/psi_table_bits.h"
+#include "sql/dd/object_id.h" // dd::Object_id
+#include "sql/dd/string_type.h"
+#include "sql/dd/types/object_table.h" // dd::Object_table
+#include "sql/discrete_interval.h" // Discrete_interval
+#include "sql/key.h"
+#include "sql/sql_const.h" // SHOW_COMP_OPTION
+#include "sql/sql_list.h" // SQL_I_List
+#include "sql/sql_plugin_ref.h" // plugin_ref
+#include "thr_lock.h" // thr_lock_type
+#include "typelib.h"
+
+class Alter_info;
+class Candidate_table_order;
+class Create_field;
+class Field;
+class Item;
+class JOIN;
+class Json_dom;
+class Partition_handler;
+class Plugin_table;
+class Plugin_tablespace;
+class Record_buffer;
+class SE_cost_constants; // see opt_costconstants.h
+class String;
+class THD;
+class handler;
+class partition_info;
+struct System_status_var;
+
+namespace dd {
+class Properties;
+} // namespace dd
+struct FOREIGN_KEY_INFO;
+struct KEY_CACHE;
+struct LEX;
+struct MY_BITMAP;
+struct SAVEPOINT;
+struct TABLE;
+struct TABLE_LIST;
+struct TABLE_SHARE;
+struct Tablespace_options;
+struct handlerton;
+
+typedef struct xid_t XID;
+typedef struct st_xarecover_txn XA_recover_txn;
+struct MDL_key;
+
+namespace dd {
+enum class enum_column_types;
+class Table;
+class Tablespace;
+} // namespace dd
+
+/** Id for identifying Table SDIs */
+constexpr const uint32 SDI_TYPE_TABLE = 1;
+
+/** Id for identifying Tablespace SDIs */
+constexpr const uint32 SDI_TYPE_TABLESPACE = 2;
+
+/** Key to identify a dictionary object */
+struct sdi_key_t {
+ /** Type of Object, For ex: column, index, etc */
+ uint32 type;
+
+ /** Object id which should be unique in tablespsace */
+ uint64 id;
+};
+
+using sdi_container = std::vector<sdi_key_t>;
+struct sdi_vector_t {
+ sdi_container m_vec;
+};
+
+typedef bool (*qc_engine_callback)(THD *thd, const char *table_key,
+ uint key_length, ulonglong *engine_data);
+
+typedef bool(stat_print_fn)(THD *thd, const char *type, size_t type_len,
+ const char *file, size_t file_len,
+ const char *status, size_t status_len);
+
+class ha_statistics;
+class ha_tablespace_statistics;
+
+namespace AQP {
+class Join_plan;
+}
+class Unique_on_insert;
+
+extern ulong savepoint_alloc_size;
+
+/// Maps from slot to plugin. May return NULL if plugin has been unloaded.
+st_plugin_int *hton2plugin(uint slot);
+/// Returns the size of the array holding pointers to plugins.
+size_t num_hton2plugins();
+
+/**
+ For unit testing.
+ Insert plugin into arbitrary slot in array.
+ Remove plugin from arbitrary slot in array.
+*/
+st_plugin_int *insert_hton2plugin(uint slot, st_plugin_int *plugin);
+st_plugin_int *remove_hton2plugin(uint slot);
+
+extern const char *ha_row_type[];
+extern const char *tx_isolation_names[];
+extern const char *binlog_format_names[];
+extern TYPELIB tx_isolation_typelib;
+extern ulong total_ha_2pc;
+
+// the following is for checking tables
+
+#define HA_ADMIN_ALREADY_DONE 1
+#define HA_ADMIN_OK 0
+#define HA_ADMIN_NOT_IMPLEMENTED -1
+#define HA_ADMIN_FAILED -2
+#define HA_ADMIN_CORRUPT -3
+#define HA_ADMIN_INTERNAL_ERROR -4
+#define HA_ADMIN_INVALID -5
+#define HA_ADMIN_REJECT -6
+#define HA_ADMIN_TRY_ALTER -7
+#define HA_ADMIN_WRONG_CHECKSUM -8
+#define HA_ADMIN_NOT_BASE_TABLE -9
+#define HA_ADMIN_NEEDS_UPGRADE -10
+#define HA_ADMIN_NEEDS_ALTER -11
+#define HA_ADMIN_NEEDS_CHECK -12
+#define HA_ADMIN_STATS_UPD_ERR -13
+/** User needs to dump and re-create table to fix pre 5.0 decimal types */
+#define HA_ADMIN_NEEDS_DUMP_UPGRADE -14
+
+/**
+ Return values for check_if_supported_inplace_alter().
+
+ @see check_if_supported_inplace_alter() for description of
+ the individual values.
+*/
+enum enum_alter_inplace_result {
+ HA_ALTER_ERROR,
+ HA_ALTER_INPLACE_NOT_SUPPORTED,
+ HA_ALTER_INPLACE_EXCLUSIVE_LOCK,
+ HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE,
+ HA_ALTER_INPLACE_SHARED_LOCK,
+ HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE,
+ HA_ALTER_INPLACE_NO_LOCK,
+ HA_ALTER_INPLACE_INSTANT
+};
+
+/* Bits in table_flags() to show what database can do */
+
+#define HA_NO_TRANSACTIONS (1 << 0) /* Doesn't support transactions */
+#define HA_PARTIAL_COLUMN_READ (1 << 1) /* read may not return all columns */
+/*
+ Used to avoid scanning full tables on an index. If this flag is set then
+ the handler always has a primary key (hidden if not defined) and this
+ index is used for scanning rather than a full table scan in all
+ situations. No separate data/index file.
+*/
+#define HA_TABLE_SCAN_ON_INDEX (1 << 2)
+
+/// Not in use.
+#define HA_UNUSED3 (1 << 3)
+
+/*
+ Can the storage engine handle spatial data.
+ Used to check that no spatial attributes are declared unless
+ the storage engine is capable of handling it.
+*/
+#define HA_CAN_GEOMETRY (1 << 4)
+/*
+ Reading keys in random order is as fast as reading keys in sort order
+ (Used in records.cc to decide if we should use a record cache and by
+ filesort to decide if we should sort key + data or key + pointer-to-row.
+ For further explanation see intro to init_read_record.
+*/
+#define HA_FAST_KEY_READ (1 << 5)
+/*
+ Set the following flag if we on delete should force all key to be read
+ and on update read all keys that changes
+*/
+#define HA_REQUIRES_KEY_COLUMNS_FOR_DELETE (1 << 6)
+/*
+ Is NULL values allowed in indexes.
+ If this is not allowed then it is not possible to use an index on a
+ NULLable field.
+*/
+#define HA_NULL_IN_KEY (1 << 7)
+/*
+ Tells that we can the position for the conflicting duplicate key
+ record is stored in table->file->dupp_ref. (insert uses rnd_pos() on
+ this to find the duplicated row)
+*/
+#define HA_DUPLICATE_POS (1 << 8)
+#define HA_NO_BLOBS (1 << 9) /* Doesn't support blobs */
+/*
+ Is the storage engine capable of defining an index of a prefix on
+ a BLOB attribute.
+*/
+#define HA_CAN_INDEX_BLOBS (1 << 10)
+/*
+ Auto increment fields can be part of a multi-part key. For second part
+ auto-increment keys, the auto_incrementing is done in handler.cc
+*/
+#define HA_AUTO_PART_KEY (1 << 11)
+/*
+ Can't define a table without primary key (and cannot handle a table
+ with hidden primary key)
+*/
+#define HA_REQUIRE_PRIMARY_KEY (1 << 12)
+/*
+ Does the counter of records after the info call specify an exact
+ value or not. If it does this flag is set.
+*/
+#define HA_STATS_RECORDS_IS_EXACT (1 << 13)
+/// Not in use.
+#define HA_UNUSED14 (1 << 14)
+/*
+ This parameter is set when the handler will also return the primary key
+ when doing read-only-key on another index, i.e., if we get the primary
+ key columns for free when we do an index read (usually, it also implies
+ that HA_PRIMARY_KEY_REQUIRED_FOR_POSITION flag is set).
+*/
+#define HA_PRIMARY_KEY_IN_READ_INDEX (1 << 15)
+/*
+ If HA_PRIMARY_KEY_REQUIRED_FOR_POSITION is set, it means that to position()
+ uses a primary key given by the record argument.
+ Without primary key, we can't call position().
+ If not set, the position is returned as the current rows position
+ regardless of what argument is given.
+*/
+#define HA_PRIMARY_KEY_REQUIRED_FOR_POSITION (1 << 16)
+#define HA_CAN_RTREEKEYS (1 << 17)
+/*
+ Seems to be an old MyISAM feature that is no longer used. No handler
+ has it defined but it is checked in init_read_record. Further investigation
+ needed.
+*/
+#define HA_NOT_DELETE_WITH_CACHE (1 << 18)
+/*
+ The following is we need to a primary key to delete (and update) a row.
+ If there is no primary key, all columns needs to be read on update and delete
+*/
+#define HA_PRIMARY_KEY_REQUIRED_FOR_DELETE (1 << 19)
+/*
+ Indexes on prefixes of character fields are not allowed.
+*/
+#define HA_NO_PREFIX_CHAR_KEYS (1 << 20)
+/*
+ Does the storage engine support fulltext indexes.
+*/
+#define HA_CAN_FULLTEXT (1 << 21)
+/*
+ Can the HANDLER interface in the MySQL API be used towards this
+ storage engine.
+*/
+#define HA_CAN_SQL_HANDLER (1 << 22)
+/*
+ Set if the storage engine does not support auto increment fields.
+*/
+#define HA_NO_AUTO_INCREMENT (1 << 23)
+/*
+ Supports CHECKSUM option in CREATE TABLE (MyISAM feature).
+*/
+#define HA_HAS_CHECKSUM (1 << 24)
+/*
+ Table data are stored in separate files (for lower_case_table_names).
+ Should file names always be in lower case (used by engines that map
+ table names to file names.
+*/
+#define HA_FILE_BASED (1 << 26)
+#define HA_NO_VARCHAR (1 << 27)
+/*
+ Is the storage engine capable of handling bit fields.
+*/
+#define HA_CAN_BIT_FIELD (1 << 28)
+#define HA_ANY_INDEX_MAY_BE_UNIQUE (1 << 30)
+#define HA_NO_COPY_ON_ALTER (1LL << 31)
+#define HA_COUNT_ROWS_INSTANT (1LL << 32) /* records() gives exact count*/
+/* Has it's own method of binlog logging */
+#define HA_HAS_OWN_BINLOGGING (1LL << 33)
+/*
+ Engine is capable of row-format and statement-format logging,
+ respectively
+*/
+#define HA_BINLOG_ROW_CAPABLE (1LL << 34)
+#define HA_BINLOG_STMT_CAPABLE (1LL << 35)
+/*
+ When a multiple key conflict happens in a REPLACE command mysql
+ expects the conflicts to be reported in the ascending order of
+ key names.
+
+ For e.g.
+
+ CREATE TABLE t1 (a INT, UNIQUE (a), b INT NOT NULL, UNIQUE (b), c INT NOT
+ NULL, INDEX(c));
+
+ REPLACE INTO t1 VALUES (1,1,1),(2,2,2),(2,1,3);
+
+ MySQL expects the conflict with 'a' to be reported before the conflict with
+ 'b'.
+
+ If the underlying storage engine does not report the conflicting keys in
+ ascending order, it causes unexpected errors when the REPLACE command is
+ executed.
+
+ This flag helps the underlying SE to inform the server that the keys are not
+ ordered.
+*/
+#define HA_DUPLICATE_KEY_NOT_IN_ORDER (1LL << 36)
+/*
+ Engine supports REPAIR TABLE. Used by CHECK TABLE FOR UPGRADE if an
+ incompatible table is detected. If this flag is set, CHECK TABLE FOR UPGRADE
+ will report ER_TABLE_NEEDS_UPGRADE, otherwise ER_TABLE_NEED_REBUILD.
+*/
+#define HA_CAN_REPAIR (1LL << 37)
+
+/*
+ Set of all binlog flags. Currently only contain the capabilities
+ flags.
+ */
+#define HA_BINLOG_FLAGS (HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE)
+
+/**
+ The handler supports read before write removal optimization
+
+ Read before write removal may be used for storage engines which support
+ write without previous read of the row to be updated. Handler returning
+ this flag must implement start_read_removal() and end_read_removal().
+ The handler may return "fake" rows constructed from the key of the row
+ asked for. This is used to optimize UPDATE and DELETE by reducing the
+ number of round-trips between handler and storage engine.
+
+ Example:
+ UPDATE a=1 WHERE pk IN (@<keys@>)
+
+ @verbatim
+ mysql_update()
+ {
+ if (<conditions for starting read removal>)
+ start_read_removal()
+ -> handler returns true if read removal supported for this table/query
+
+ while(read_record("pk=<key>"))
+ -> handler returns fake row with column "pk" set to <key>
+
+ ha_update_row()
+ -> handler sends write "a=1" for row with "pk=<key>"
+
+ end_read_removal()
+ -> handler returns the number of rows actually written
+ }
+ @endverbatim
+
+ @note This optimization in combination with batching may be used to
+ remove even more round-trips.
+*/
+#define HA_READ_BEFORE_WRITE_REMOVAL (1LL << 38)
+
+/*
+ Engine supports extended fulltext API
+ */
+#define HA_CAN_FULLTEXT_EXT (1LL << 39)
+
+/*
+ Storage engine doesn't synchronize result set with expected table contents.
+ Used by replication slave to check if it is possible to retrieve rows from
+ the table when deciding whether to do a full table scan, index scan or hash
+ scan while applying a row event.
+ */
+#define HA_READ_OUT_OF_SYNC (1LL << 40)
+
+/*
+ Storage engine supports table export using the
+ FLUSH TABLE <table_list> FOR EXPORT statement.
+ */
+#define HA_CAN_EXPORT (1LL << 41)
+
+/*
+ The handler don't want accesses to this table to
+ be const-table optimized
+*/
+#define HA_BLOCK_CONST_TABLE (1LL << 42)
+
+/*
+ Handler supports FULLTEXT hints
+*/
+#define HA_CAN_FULLTEXT_HINTS (1LL << 43)
+
+/**
+ Storage engine doesn't support LOCK TABLE ... READ LOCAL locks
+ but doesn't want to use handler::store_lock() API for upgrading
+ them to LOCK TABLE ... READ locks, for example, because it doesn't
+ use THR_LOCK locks at all.
+*/
+#define HA_NO_READ_LOCAL_LOCK (1LL << 44)
+
+/**
+ A storage engine is compatible with the attachable transaction requirements
+ means that
+
+ - either SE detects the fact that THD::ha_data was reset and starts a new
+ attachable transaction, closes attachable transaction on close_connection
+ and resumes regular (outer) transaction when THD::ha_data is restored;
+
+ - or SE completely ignores THD::ha_data and close_connection like MyISAM
+ does.
+*/
+#define HA_ATTACHABLE_TRX_COMPATIBLE (1LL << 45)
+
+/**
+ Handler supports Generated Columns
+*/
+#define HA_GENERATED_COLUMNS (1LL << 46)
+
+/**
+ Supports index on virtual generated column
+*/
+#define HA_CAN_INDEX_VIRTUAL_GENERATED_COLUMN (1LL << 47)
+
+/**
+ Supports descending indexes
+*/
+#define HA_DESCENDING_INDEX (1LL << 48)
+
+/**
+ Supports partial update of BLOB columns.
+*/
+#define HA_BLOB_PARTIAL_UPDATE (1LL << 49)
+
+/**
+ If this isn't defined, only columns/indexes with Cartesian coordinate systems
+ (projected SRS or SRID 0) is supported. Columns/indexes without SRID
+ restriction is also supported if this isn't defined.
+*/
+#define HA_SUPPORTS_GEOGRAPHIC_GEOMETRY_COLUMN (1LL << 50)
+
+/**
+ Handler supports expressions as DEFAULT for a column.
+*/
+#define HA_SUPPORTS_DEFAULT_EXPRESSION (1LL << 51)
+
+/**
+ Handlers with this flag set do not support UPDATE operations.
+*/
+#define HA_UPDATE_NOT_SUPPORTED (1LL << 52)
+
+/**
+ Handlers with this flag set do not support DELETE operations.
+*/
+#define HA_DELETE_NOT_SUPPORTED (1LL << 53)
+
+/**
+ The storage engine does not support using indexes for access. Indexes can only
+ be used for estimating cost.
+*/
+#define HA_NO_INDEX_ACCESS (1LL << 54)
+
+/**
+ Supports multi-valued index
+*/
+#define HA_MULTI_VALUED_KEY_SUPPORT (1LL << 55)
+
+/*
+ Bits in index_flags(index_number) for what you can do with index.
+ If you do not implement indexes, just return zero here.
+*/
+/*
+ Does the index support read next, this is assumed in the server
+ code and never checked so all indexes must support this.
+ Note that the handler can be used even if it doesn't have any index.
+*/
+#define HA_READ_NEXT 1 /* TODO really use this flag */
+/*
+ Can the index be used to scan backwards (supports ::index_prev).
+*/
+#define HA_READ_PREV 2
+/*
+ Can the index deliver its record in index order. Typically true for
+ all ordered indexes and not true for hash indexes. Used to set keymap
+ part_of_sortkey.
+ This keymap is only used to find indexes usable for resolving an ORDER BY
+ in the query. Thus in most cases index_read will work just fine without
+ order in result production. When this flag is set it is however safe to
+ order all output started by index_read since most engines do this. With
+ read_multi_range calls there is a specific flag setting order or not
+ order so in those cases ordering of index output can be avoided.
+*/
+#define HA_READ_ORDER 4
+/*
+ Specify whether index can handle ranges, typically true for all
+ ordered indexes and not true for hash indexes.
+ Used by optimiser to check if ranges (as key >= 5) can be optimised
+ by index.
+*/
+#define HA_READ_RANGE 8
+/*
+ Can't use part key searches. This is typically true for hash indexes
+ and typically not true for ordered indexes.
+*/
+#define HA_ONLY_WHOLE_INDEX 16
+/*
+ Does the storage engine support index-only scans on this index.
+ Enables use of HA_EXTRA_KEYREAD and HA_EXTRA_NO_KEYREAD
+ Used to set Key_map keys_for_keyread and to check in optimiser for
+ index-only scans. When doing a read under HA_EXTRA_KEYREAD the handler
+ only have to fill in the columns the key covers. If
+ HA_PRIMARY_KEY_IN_READ_INDEX is set then also the PRIMARY KEY columns
+ must be updated in the row.
+*/
+#define HA_KEYREAD_ONLY 64
+/*
+ Index scan will not return records in rowid order. Not guaranteed to be
+ set for unordered (e.g. HASH) indexes.
+*/
+#define HA_KEY_SCAN_NOT_ROR 128
+#define HA_DO_INDEX_COND_PUSHDOWN 256 /* Supports Index Condition Pushdown */
+
+/* operations for disable/enable indexes */
+#define HA_KEY_SWITCH_NONUNIQ 0
+#define HA_KEY_SWITCH_ALL 1
+#define HA_KEY_SWITCH_NONUNIQ_SAVE 2
+#define HA_KEY_SWITCH_ALL_SAVE 3
+
+/*
+ Use this instead of 0 as the initial value for the slot number of
+ handlerton, so that we can distinguish uninitialized slot number
+ from slot 0.
+*/
+#define HA_SLOT_UNDEF ((uint)-1)
+
+/*
+ Parameters for open() (in register form->filestat)
+ HA_GET_INFO does an implicit HA_ABORT_IF_LOCKED
+*/
+
+#define HA_OPEN_KEYFILE 1
+#define HA_OPEN_RNDFILE 2
+#define HA_GET_INDEX 4
+#define HA_GET_INFO 8 /* do a handler::info() after open */
+#define HA_READ_ONLY 16 /* File opened as readonly */
+/* Try readonly if can't open with read and write */
+#define HA_TRY_READ_ONLY 32
+#define HA_WAIT_IF_LOCKED 64 /* Wait if locked on open */
+#define HA_ABORT_IF_LOCKED 128 /* skip if locked on open.*/
+#define HA_BLOCK_LOCK 256 /* unlock when reading some records */
+#define HA_OPEN_TEMPORARY 512
+
+/* Some key definitions */
+#define HA_KEY_NULL_LENGTH 1
+#define HA_KEY_BLOB_LENGTH 2
+
+#define HA_LEX_CREATE_TMP_TABLE 1
+#define HA_LEX_CREATE_IF_NOT_EXISTS 2
+#define HA_LEX_CREATE_TABLE_LIKE 4
+#define HA_LEX_CREATE_INTERNAL_TMP_TABLE 8
+#define HA_MAX_REC_LENGTH 65535U
+
+/**
+ Options for the START TRANSACTION statement.
+
+ Note that READ ONLY and READ WRITE are logically mutually exclusive.
+ This is enforced by the parser and depended upon by trans_begin().
+
+ We need two flags instead of one in order to differentiate between
+ situation when no READ WRITE/ONLY clause were given and thus transaction
+ is implicitly READ WRITE and the case when READ WRITE clause was used
+ explicitly.
+*/
+
+// WITH CONSISTENT SNAPSHOT option
+static const uint MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT = 1;
+// READ ONLY option
+static const uint MYSQL_START_TRANS_OPT_READ_ONLY = 2;
+// READ WRITE option
+static const uint MYSQL_START_TRANS_OPT_READ_WRITE = 4;
+// HIGH PRIORITY option
+static const uint MYSQL_START_TRANS_OPT_HIGH_PRIORITY = 8;
+
+enum legacy_db_type {
+ DB_TYPE_UNKNOWN = 0,
+ DB_TYPE_DIAB_ISAM = 1,
+ DB_TYPE_HASH,
+ DB_TYPE_MISAM,
+ DB_TYPE_PISAM,
+ DB_TYPE_RMS_ISAM,
+ DB_TYPE_HEAP,
+ DB_TYPE_ISAM,
+ DB_TYPE_MRG_ISAM,
+ DB_TYPE_MYISAM,
+ DB_TYPE_MRG_MYISAM,
+ DB_TYPE_BERKELEY_DB,
+ DB_TYPE_INNODB,
+ DB_TYPE_GEMINI,
+ DB_TYPE_NDBCLUSTER,
+ DB_TYPE_EXAMPLE_DB,
+ DB_TYPE_ARCHIVE_DB,
+ DB_TYPE_CSV_DB,
+ DB_TYPE_FEDERATED_DB,
+ DB_TYPE_BLACKHOLE_DB,
+ DB_TYPE_PARTITION_DB, // No longer used.
+ DB_TYPE_BINLOG,
+ DB_TYPE_SOLID,
+ DB_TYPE_PBXT,
+ DB_TYPE_TABLE_FUNCTION,
+ DB_TYPE_MEMCACHE,
+ DB_TYPE_FALCON,
+ DB_TYPE_MARIA,
+ /** Performance schema engine. */
+ DB_TYPE_PERFORMANCE_SCHEMA,
+ DB_TYPE_TEMPTABLE,
+ DB_TYPE_FIRST_DYNAMIC = 42,
+ DB_TYPE_DEFAULT = 127 // Must be last
+};
+
+enum row_type : int {
+ ROW_TYPE_NOT_USED = -1,
+ ROW_TYPE_DEFAULT,
+ ROW_TYPE_FIXED,
+ ROW_TYPE_DYNAMIC,
+ ROW_TYPE_COMPRESSED,
+ ROW_TYPE_REDUNDANT,
+ ROW_TYPE_COMPACT,
+ /** Unused. Reserved for future versions. */
+ ROW_TYPE_PAGED
+};
+
+enum enum_binlog_func {
+ BFN_RESET_LOGS = 1,
+ BFN_RESET_SLAVE = 2,
+ BFN_BINLOG_WAIT = 3,
+ BFN_BINLOG_END = 4,
+ BFN_BINLOG_PURGE_FILE = 5
+};
+
+enum enum_binlog_command {
+ LOGCOM_CREATE_TABLE,
+ LOGCOM_ALTER_TABLE,
+ LOGCOM_RENAME_TABLE,
+ LOGCOM_DROP_TABLE,
+ LOGCOM_CREATE_DB,
+ LOGCOM_ALTER_DB,
+ LOGCOM_DROP_DB,
+ LOGCOM_ACL_NOTIFY
+};
+
+enum class enum_sampling_method { SYSTEM };
+
+/* Bits in used_fields */
+#define HA_CREATE_USED_AUTO (1L << 0)
+#define HA_CREATE_USED_RAID (1L << 1) // RAID is no longer availble
+#define HA_CREATE_USED_UNION (1L << 2)
+#define HA_CREATE_USED_INSERT_METHOD (1L << 3)
+#define HA_CREATE_USED_MIN_ROWS (1L << 4)
+#define HA_CREATE_USED_MAX_ROWS (1L << 5)
+#define HA_CREATE_USED_AVG_ROW_LENGTH (1L << 6)
+#define HA_CREATE_USED_PACK_KEYS (1L << 7)
+#define HA_CREATE_USED_CHARSET (1L << 8)
+#define HA_CREATE_USED_DEFAULT_CHARSET (1L << 9)
+#define HA_CREATE_USED_DATADIR (1L << 10)
+#define HA_CREATE_USED_INDEXDIR (1L << 11)
+#define HA_CREATE_USED_ENGINE (1L << 12)
+#define HA_CREATE_USED_CHECKSUM (1L << 13)
+#define HA_CREATE_USED_DELAY_KEY_WRITE (1L << 14)
+#define HA_CREATE_USED_ROW_FORMAT (1L << 15)
+#define HA_CREATE_USED_COMMENT (1L << 16)
+#define HA_CREATE_USED_PASSWORD (1L << 17)
+#define HA_CREATE_USED_CONNECTION (1L << 18)
+#define HA_CREATE_USED_KEY_BLOCK_SIZE (1L << 19)
+/** Unused. Reserved for future versions. */
+#define HA_CREATE_USED_TRANSACTIONAL (1L << 20)
+/** Unused. Reserved for future versions. */
+#define HA_CREATE_USED_PAGE_CHECKSUM (1L << 21)
+/** This is set whenever STATS_PERSISTENT=0|1|default has been
+specified in CREATE/ALTER TABLE. See also HA_OPTION_STATS_PERSISTENT in
+include/my_base.h. It is possible to distinguish whether
+STATS_PERSISTENT=default has been specified or no STATS_PERSISTENT= is
+given at all. */
+#define HA_CREATE_USED_STATS_PERSISTENT (1L << 22)
+/**
+ This is set whenever STATS_AUTO_RECALC=0|1|default has been
+ specified in CREATE/ALTER TABLE. See enum_stats_auto_recalc.
+ It is possible to distinguish whether STATS_AUTO_RECALC=default
+ has been specified or no STATS_AUTO_RECALC= is given at all.
+*/
+#define HA_CREATE_USED_STATS_AUTO_RECALC (1L << 23)
+/**
+ This is set whenever STATS_SAMPLE_PAGES=N|default has been
+ specified in CREATE/ALTER TABLE. It is possible to distinguish whether
+ STATS_SAMPLE_PAGES=default has been specified or no STATS_SAMPLE_PAGES= is
+ given at all.
+*/
+#define HA_CREATE_USED_STATS_SAMPLE_PAGES (1L << 24)
+
+/**
+ This is set whenever a 'TABLESPACE=...' phrase is used on CREATE TABLE
+*/
+#define HA_CREATE_USED_TABLESPACE (1L << 25)
+
+/** COMPRESSION="zlib|lz4|none" used during table create. */
+#define HA_CREATE_USED_COMPRESS (1L << 26)
+
+/** ENCRYPTION="Y" used during table create. */
+#define HA_CREATE_USED_ENCRYPT (1L << 27)
+
+/**
+ CREATE|ALTER SCHEMA|DATABASE|TABLE has an explicit COLLATE clause.
+
+ Implies HA_CREATE_USED_DEFAULT_CHARSET.
+*/
+#define HA_CREATE_USED_DEFAULT_COLLATE (1L << 28)
+
+/** SECONDARY_ENGINE used during table create. */
+#define HA_CREATE_USED_SECONDARY_ENGINE (1L << 29)
+
+/**
+ CREATE|ALTER SCHEMA|DATABASE has an explicit ENCRYPTION clause.
+
+ Implies HA_CREATE_USED_DEFAULT_ENCRYPTION.
+*/
+#define HA_CREATE_USED_DEFAULT_ENCRYPTION (1L << 30)
+/*
+ End of bits used in used_fields
+*/
+
+/*
+ Structure to hold list of database_name.table_name.
+ This is used at both mysqld and storage engine layer.
+*/
+struct st_handler_tablename {
+ const char *db;
+ const char *tablename;
+};
+
+#define MAXGTRIDSIZE 64
+#define MAXBQUALSIZE 64
+
+#define COMPATIBLE_DATA_YES 0
+#define COMPATIBLE_DATA_NO 1
+
+/*
+ These structures are used to pass information from a set of SQL commands
+ on add/drop/change tablespace definitions to the proper hton.
+*/
+#define UNDEF_NODEGROUP 65535
+
+// FUTURE: Combine these two enums into one enum class
+enum ts_command_type {
+ TS_CMD_NOT_DEFINED = -1,
+ CREATE_TABLESPACE = 0,
+ ALTER_TABLESPACE = 1,
+ CREATE_LOGFILE_GROUP = 2,
+ ALTER_LOGFILE_GROUP = 3,
+ DROP_TABLESPACE = 4,
+ DROP_LOGFILE_GROUP = 5,
+ CHANGE_FILE_TABLESPACE = 6,
+ ALTER_ACCESS_MODE_TABLESPACE = 7,
+ CREATE_UNDO_TABLESPACE = 8,
+ ALTER_UNDO_TABLESPACE = 9,
+ DROP_UNDO_TABLESPACE = 10
+};
+
+enum ts_alter_tablespace_type {
+ TS_ALTER_TABLESPACE_TYPE_NOT_DEFINED = -1,
+ ALTER_TABLESPACE_ADD_FILE = 1,
+ ALTER_TABLESPACE_DROP_FILE = 2,
+ ALTER_TABLESPACE_RENAME = 3,
+ ALTER_TABLESPACE_OPTIONS = 4,
+ ALTER_UNDO_TABLESPACE_SET_ACTIVE = 5,
+ ALTER_UNDO_TABLESPACE_SET_INACTIVE = 6
+};
+
+/**
+ Legacy struct for passing tablespace information to SEs.
+
+ FUTURE: Pass all info through dd objects
+ */
+class st_alter_tablespace {
+ public:
+ const char *tablespace_name = nullptr;
+ const char *logfile_group_name = nullptr;
+ ts_command_type ts_cmd_type = TS_CMD_NOT_DEFINED;
+ enum ts_alter_tablespace_type ts_alter_tablespace_type =
+ TS_ALTER_TABLESPACE_TYPE_NOT_DEFINED;
+ const char *data_file_name = nullptr;
+ const char *undo_file_name = nullptr;
+ ulonglong extent_size = 1024 * 1024; // Default 1 MByte
+ ulonglong undo_buffer_size = 8 * 1024 * 1024; // Default 8 MByte
+ ulonglong redo_buffer_size = 8 * 1024 * 1024; // Default 8 MByte
+ ulonglong initial_size = 128 * 1024 * 1024; // Default 128 MByte
+ ulonglong autoextend_size = 0; // No autoextension as default
+ ulonglong max_size = 0; // Max size == initial size => no extension
+ ulonglong file_block_size = 0; // 0=default or must be a valid Page Size
+ uint nodegroup_id = UNDEF_NODEGROUP;
+ bool wait_until_completed = true;
+ const char *ts_comment = nullptr;
+
+ bool is_tablespace_command() {
+ return ts_cmd_type == CREATE_TABLESPACE ||
+ ts_cmd_type == ALTER_TABLESPACE || ts_cmd_type == DROP_TABLESPACE ||
+ ts_cmd_type == CHANGE_FILE_TABLESPACE ||
+ ts_cmd_type == ALTER_ACCESS_MODE_TABLESPACE;
+ }
+
+ /**
+ Proper constructor even for all-public class simplifies initialization and
+ allows members to be const.
+
+ FUTURE: With constructor all members can be made const, and do not need
+ default initializers.
+
+ @param tablespace name of tabelspace (nullptr for logfile group statements)
+ @param logfile_group name of logfile group or nullptr
+ @param cmd main statement type
+ @param alter_tablespace_cmd subcommand type for ALTER TABLESPACE
+ @param datafile tablespace file for CREATE and ALTER ... ADD ...
+ @param undofile only applies to logfile group statements. nullptr otherwise.
+ @param opts options provided by parser
+ */
+ st_alter_tablespace(const char *tablespace, const char *logfile_group,
+ ts_command_type cmd,
+ enum ts_alter_tablespace_type alter_tablespace_cmd,
+ const char *datafile, const char *undofile,
+ const Tablespace_options &opts);
+};
+
+/*
+ Make sure that the order of schema_tables and enum_schema_tables are the same.
+*/
+enum enum_schema_tables : int {
+ SCH_FIRST = 0,
+ SCH_COLUMN_PRIVILEGES = SCH_FIRST,
+ SCH_ENGINES,
+ SCH_OPEN_TABLES,
+ SCH_OPTIMIZER_TRACE,
+ SCH_PLUGINS,
+ SCH_PROCESSLIST,
+ SCH_PROFILES,
+ SCH_SCHEMA_PRIVILEGES,
+ SCH_TABLESPACES,
+ SCH_TABLE_PRIVILEGES,
+ SCH_USER_PRIVILEGES,
+ SCH_TMP_TABLE_COLUMNS,
+ SCH_TMP_TABLE_KEYS,
+ SCH_LAST = SCH_TMP_TABLE_KEYS
+};
+
+enum ha_stat_type { HA_ENGINE_STATUS, HA_ENGINE_LOGS, HA_ENGINE_MUTEX };
+enum ha_notification_type : int { HA_NOTIFY_PRE_EVENT, HA_NOTIFY_POST_EVENT };
+
+/** Clone start operation mode */
+enum Ha_clone_mode {
+ /** Start a new clone operation */
+ HA_CLONE_MODE_START,
+
+ /** Re-start a clone operation after failure */
+ HA_CLONE_MODE_RESTART,
+
+ /** Add a new task to a running clone operation */
+ HA_CLONE_MODE_ADD_TASK,
+
+ /** Get version for transfer data format */
+ HA_CLONE_MODE_VERSION,
+
+ /** Max value for clone mode */
+ HA_CLONE_MODE_MAX
+};
+
+/** Clone operation types. */
+enum Ha_clone_type : size_t {
+ /** Caller must block all write operation to the SE. */
+ HA_CLONE_BLOCKING,
+
+ /** For transactional SE, archive redo to support concurrent dml */
+ HA_CLONE_REDO,
+
+ /** For transactional SE, track page changes to support concurrent dml */
+ HA_CLONE_PAGE,
+
+ /** For transactional SE, use both page tracking and redo to optimize
+ clone with concurrent dml. Currently supported by Innodb. */
+ HA_CLONE_HYBRID,
+
+ /** SE supports multiple threads for clone */
+ HA_CLONE_MULTI_TASK,
+
+ /** SE supports restarting clone after network failure */
+ HA_CLONE_RESTART,
+
+ /** Maximum value of clone type */
+ HA_CLONE_TYPE_MAX
+};
+
+using Ha_clone_flagset = std::bitset<HA_CLONE_TYPE_MAX>;
+
+/** File reference for clone */
+struct Ha_clone_file {
+ /** File reference type */
+ enum {
+ /** File handle */
+ FILE_HANDLE,
+
+ /** File descriptor */
+ FILE_DESC
+
+ } type;
+
+ /** File reference */
+ union {
+ /** File descriptor */
+ int file_desc;
+
+ /** File handle for windows */
+ void *file_handle;
+ };
+};
+
+/* Abstract callback interface to stream data back to the caller. */
+class Ha_clone_cbk {
+ protected:
+ /** Constructor to initialize members. */
+ Ha_clone_cbk()
+ : m_hton(),
+ m_loc_idx(),
+ m_client_buff_size(),
+ m_data_desc(),
+ m_desc_len(),
+ m_src_name(),
+ m_dest_name(),
+ m_state_estimate(),
+ m_flag() {}
+
+ public:
+ /** Callback providing data from current position of a
+ file descriptor of specific length.
+ @param[in] from_file source file to read from
+ @param[in] len data length
+ @return error code */
+ virtual int file_cbk(Ha_clone_file from_file, uint len) = 0;
+
+ /** Callback providing data in buffer of specific length.
+ @param[in] from_buffer source buffer to read from
+ @param[in] len data length
+ @return error code */
+ virtual int buffer_cbk(uchar *from_buffer, uint len) = 0;
+
+ /** Callback providing a file descriptor to write data starting
+ from current position.
+ @param[in] to_file destination file to write data
+ @return error code */
+ virtual int apply_file_cbk(Ha_clone_file to_file) = 0;
+
+ /** Callback to get data in buffer.
+ @param[out] to_buffer data buffer
+ @param[out] len data length
+ @return error code */
+ virtual int apply_buffer_cbk(uchar *&to_buffer, uint &len) = 0;
+
+ /** virtual destructor. */
+ virtual ~Ha_clone_cbk() {}
+
+ /** Set current storage engine handlerton.
+ @param[in] hton SE handlerton */
+ void set_hton(handlerton *hton) { m_hton = hton; }
+
+ /** Get current storage engine handlerton.
+ @return SE handlerton */
+ handlerton *get_hton() { return (m_hton); }
+
+ /** Set caller's transfer buffer size. SE can adjust the data chunk size
+ based on this parameter.
+ @param[in] size buffer size in bytes */
+ void set_client_buffer_size(uint size) { m_client_buff_size = size; }
+
+ /** Get caller's transfer buffer size.
+ @return buffer size in bytes */
+ uint get_client_buffer_size() { return (m_client_buff_size); }
+
+ /** Set current SE index.
+ @param[in] idx SE index in locator array */
+ void set_loc_index(uint idx) { m_loc_idx = idx; }
+
+ /** Get current SE index.
+ @return SE index in locator array */
+ uint get_loc_index() { return (m_loc_idx); }
+
+ /** Set data descriptor. SE specific descriptor for the
+ data transferred by the callbacks.
+ @param[in] desc serialized data descriptor
+ @param[in] len length of the descriptor byte stream */
+ void set_data_desc(const uchar *desc, uint len) {
+ m_data_desc = desc;
+ m_desc_len = len;
+ }
+
+ /** Get data descriptor. SE specific descriptor for the
+ data transferred by the callbacks.
+ @param[out] lenp length of the descriptor byte stream
+ @return pointer to the serialized data descriptor */
+ const uchar *get_data_desc(uint *lenp) {
+ if (lenp != nullptr) {
+ *lenp = m_desc_len;
+ }
+
+ return (m_data_desc);
+ }
+
+ /** Get SE source file name. Used for debug printing and error message.
+ @return null terminated string for source file name */
+ const char *get_source_name() { return (m_src_name); }
+
+ /** Set SE source file name.
+ @param[in] name null terminated string for source file name */
+ void set_source_name(const char *name) { m_src_name = name; }
+
+ /** Get SE destination file name. Used for debug printing and error message.
+ @return null terminated string for destination file name */
+ const char *get_dest_name() { return (m_dest_name); }
+
+ /** Set SE destination file name.
+ @param[in] name null terminated string for destination file name */
+ void set_dest_name(const char *name) { m_dest_name = name; }
+
+ /** Clear all flags set by SE */
+ void clear_flags() { m_flag = 0; }
+
+ /** Mark that ACK is needed for the data transfer before returning
+ from callback. Set by SE. */
+ void set_ack() { m_flag |= HA_CLONE_ACK; }
+
+ /** Check if ACK is needed for the data transfer
+ @return true if ACK is needed */
+ bool is_ack_needed() const { return (m_flag & HA_CLONE_ACK); }
+
+ /** Mark that the file descriptor is opened for read/write
+ with OS buffer cache. For O_DIRECT, the flag is not set. */
+ void set_os_buffer_cache() { m_flag |= HA_CLONE_FILE_CACHE; }
+
+ /** Check if the file descriptor is opened for read/write with OS
+ buffer cache. Currently clone avoids using zero copy (sendfile on linux),
+ if SE is using O_DIRECT. This improves data copy performance.
+ @return true if O_DIRECT is not used */
+ bool is_os_buffer_cache() const { return (m_flag & HA_CLONE_FILE_CACHE); }
+
+ /** Mark that the file can be transferred with zero copy. */
+ void set_zero_copy() { m_flag |= HA_CLONE_ZERO_COPY; }
+
+ /** Check if zero copy optimization is suggested. */
+ bool is_zero_copy() const { return (m_flag & HA_CLONE_ZERO_COPY); }
+
+ /** Mark that data needs secure transfer. */
+ void set_secure() { m_flag |= HA_CLONE_SECURE; }
+
+ /** Check if data needs secure transfer. */
+ bool is_secure() const { return (m_flag & HA_CLONE_SECURE); }
+
+ /** Set state information and notify state change.
+ @param[in] estimate estimated bytes for current state. */
+ void mark_state_change(uint64_t estimate) {
+ m_flag |= HA_CLONE_STATE_CHANGE;
+ m_state_estimate = estimate;
+ }
+
+ /** Check if SE notified state change. */
+ bool is_state_change(uint64_t &estimate) {
+ estimate = m_state_estimate;
+ return (m_flag & HA_CLONE_STATE_CHANGE);
+ }
+
+ private:
+ /** Handlerton for the SE */
+ handlerton *m_hton;
+
+ /** SE index in caller's locator array */
+ uint m_loc_idx;
+
+ /** Caller's transfer buffer size. */
+ uint m_client_buff_size;
+
+ /** SE's Serialized data descriptor */
+ const uchar *m_data_desc;
+
+ /** SE's Serialized descriptor length. */
+ uint m_desc_len;
+
+ /** Current source file name */
+ const char *m_src_name;
+
+ /** Current destination file name */
+ const char *m_dest_name;
+
+ /** Estimated bytes to be transferred. */
+ uint64_t m_state_estimate;
+
+ /** Flag storing data related options */
+ int m_flag;
+
+ /** Acknowledgement is needed for the data transfer. */
+ const int HA_CLONE_ACK = 0x01;
+
+ /** Data file is opened for read/write with OS buffer cache. */
+ const int HA_CLONE_FILE_CACHE = 0x02;
+
+ /** Data file can be transferred with zero copy. */
+ const int HA_CLONE_ZERO_COPY = 0x04;
+
+ /** Data needs to be transferred securely over SSL connection. */
+ const int HA_CLONE_SECURE = 0x08;
+
+ /** State change notification by SE. */
+ const int HA_CLONE_STATE_CHANGE = 0x10;
+};
+
+/**
+ Column type description for foreign key columns compatibility check.
+
+ Contains subset of information from dd::Column class. It is inconvenient
+ to use dd::Column class directly for such checks because it requires valid
+ dd::Table object and in some cases we want to produce Ha_fk_column_type
+ right from column description in Create_field format.
+*/
+struct Ha_fk_column_type {
+ dd::enum_column_types type;
+ /*
+ Note that both dd::Column::char_length() and length here are really
+ in bytes.
+ */
+ size_t char_length;
+ const CHARSET_INFO *field_charset;
+ size_t elements_count;
+ uint numeric_scale;
+ bool is_unsigned;
+};
+
+/* handlerton methods */
+
+/**
+ close_connection is only called if
+ thd->ha_data[xxx_hton.slot] is non-zero, so even if you don't need
+ this storage area - set it to something, so that MySQL would know
+ this storage engine was accessed in this connection
+*/
+typedef int (*close_connection_t)(handlerton *hton, THD *thd);
+
+/** Terminate connection/statement notification. */
+typedef void (*kill_connection_t)(handlerton *hton, THD *thd);
+
+/**
+ Shut down all storage engine background tasks that might access
+ the data dictionary, before the main shutdown.
+*/
+typedef void (*pre_dd_shutdown_t)(handlerton *hton);
+
+/**
+ sv points to a storage area, that was earlier passed
+ to the savepoint_set call
+*/
+typedef int (*savepoint_rollback_t)(handlerton *hton, THD *thd, void *sv);
+
+/**
+ sv points to an uninitialized storage area of requested size
+ (see savepoint_offset description)
+*/
+typedef int (*savepoint_set_t)(handlerton *hton, THD *thd, void *sv);
+
+/**
+ Check if storage engine allows to release metadata locks which were
+ acquired after the savepoint if rollback to savepoint is done.
+ @return true - If it is safe to release MDL locks.
+ false - If it is not.
+*/
+typedef bool (*savepoint_rollback_can_release_mdl_t)(handlerton *hton,
+ THD *thd);
+
+typedef int (*savepoint_release_t)(handlerton *hton, THD *thd, void *sv);
+
+/**
+ 'all' is true if it's a real commit, that makes persistent changes
+ 'all' is false if it's not in fact a commit but an end of the
+ statement that is part of the transaction.
+ NOTE 'all' is also false in auto-commit mode where 'end of statement'
+ and 'real commit' mean the same event.
+*/
+typedef int (*commit_t)(handlerton *hton, THD *thd, bool all);
+
+typedef int (*rollback_t)(handlerton *hton, THD *thd, bool all);
+
+typedef int (*prepare_t)(handlerton *hton, THD *thd, bool all);
+
+typedef int (*recover_t)(handlerton *hton, XA_recover_txn *xid_list, uint len,
+ MEM_ROOT *mem_root);
+
+/** X/Open XA distributed transaction status codes */
+enum xa_status_code {
+ /**
+ normal execution
+ */
+ XA_OK = 0,
+
+ /**
+ asynchronous operation already outstanding
+ */
+ XAER_ASYNC = -2,
+
+ /**
+ a resource manager error occurred in the transaction branch
+ */
+ XAER_RMERR = -3,
+
+ /**
+ the XID is not valid
+ */
+ XAER_NOTA = -4,
+
+ /**
+ invalid arguments were given
+ */
+ XAER_INVAL = -5,
+
+ /**
+ routine invoked in an improper context
+ */
+ XAER_PROTO = -6,
+
+ /**
+ resource manager unavailable
+ */
+ XAER_RMFAIL = -7,
+
+ /**
+ the XID already exists
+ */
+ XAER_DUPID = -8,
+
+ /**
+ resource manager doing work outside transaction
+ */
+ XAER_OUTSIDE = -9
+};
+
+typedef xa_status_code (*commit_by_xid_t)(handlerton *hton, XID *xid);
+
+typedef xa_status_code (*rollback_by_xid_t)(handlerton *hton, XID *xid);
+
+/**
+ Create handler object for the table in the storage engine.
+
+ @param hton Handlerton object for the storage engine.
+ @param table TABLE_SHARE for the table, can be NULL if caller
+ didn't perform full-blown open of table definition.
+ @param partitioned Indicates whether table is partitioned.
+ @param mem_root Memory root to be used for allocating handler
+ object.
+*/
+typedef handler *(*create_t)(handlerton *hton, TABLE_SHARE *table,
+ bool partitioned, MEM_ROOT *mem_root);
+
+typedef void (*drop_database_t)(handlerton *hton, char *path);
+
+typedef int (*panic_t)(handlerton *hton, enum ha_panic_function flag);
+
+typedef int (*start_consistent_snapshot_t)(handlerton *hton, THD *thd);
+
+/**
+ Flush the log(s) of storage engine(s).
+
+ @param hton Handlerton of storage engine.
+ @param binlog_group_flush true if we got invoked by binlog group
+ commit during flush stage, false in other cases.
+ @retval false Succeed
+ @retval true Error
+*/
+typedef bool (*flush_logs_t)(handlerton *hton, bool binlog_group_flush);
+
+typedef bool (*show_status_t)(handlerton *hton, THD *thd, stat_print_fn *print,
+ enum ha_stat_type stat);
+
+/**
+ The flag values are defined in sql_partition.h.
+ If this function is set, then it implies that the handler supports
+ partitioned tables.
+ If this function exists, then handler::get_partition_handler must also be
+ implemented.
+*/
+typedef uint (*partition_flags_t)();
+
+/**
+ SE specific validation of the tablespace name.
+
+ This function will ask the relevant SE whether the submitted tablespace
+ name is valid.
+
+ @param ts_cmd Purpose of usage - is this tablespace DDL?
+ @param tablespace_name Name of the tablespace.
+
+ @return Tablespace name validity.
+ @retval == false: The tablespace name is invalid.
+ @retval == true: The tablespace name is valid.
+*/
+typedef bool (*is_valid_tablespace_name_t)(ts_command_type ts_cmd,
+ const char *tablespace_name);
+
+/**
+ Get the tablespace name from the SE for the given schema and table.
+
+ @param thd Thread context.
+ @param db_name Name of the relevant schema.
+ @param table_name Name of the relevant table.
+ @param [out] tablespace_name Name of the tablespace containing the table.
+
+ @return Operation status.
+ @retval == 0 Success.
+ @retval != 0 Error (handler error code returned).
+*/
+typedef int (*get_tablespace_t)(THD *thd, LEX_CSTRING db_name,
+ LEX_CSTRING table_name,
+ LEX_CSTRING *tablespace_name);
+
+/**
+ Create/drop or alter tablespace in the storage engine.
+
+ @param hton Hadlerton of the SE.
+ @param thd Thread context.
+ @param ts_info Description of tablespace and specific
+ operation on it.
+ @param old_ts_def dd::Tablespace object describing old version
+ of tablespace.
+ @param [in,out] new_ts_def dd::Tablespace object describing new version
+ of tablespace. Engines which support atomic DDL
+ can adjust this object. The updated information
+ will be saved to the data-dictionary.
+
+ @return Operation status.
+ @retval == 0 Success.
+ @retval != 0 Error (handler error code returned).
+*/
+typedef int (*alter_tablespace_t)(handlerton *hton, THD *thd,
+ st_alter_tablespace *ts_info,
+ const dd::Tablespace *old_ts_def,
+ dd::Tablespace *new_ts_def);
+
+/**
+ SE interface for getting tablespace extension.
+ @return Extension of tablespace datafile name.
+*/
+typedef const char *(*get_tablespace_filename_ext_t)();
+
+/**
+ Get the tablespace data from SE and insert it into Data dictionary
+
+ @param thd Thread context
+
+ @return Operation status.
+ @retval == 0 Success.
+ @retval != 0 Error (handler error code returned)
+*/
+typedef int (*upgrade_tablespace_t)(THD *thd);
+
+/**
+ Get the tablespace data from SE and insert it into Data dictionary
+
+ @param[in] tablespace tablespace object
+
+ @return Operation status.
+ @retval == 0 Success.
+ @retval != 0 Error (handler error code returned)
+*/
+typedef bool (*upgrade_space_version_t)(dd::Tablespace *tablespace);
+
+/**
+ Finish upgrade process inside storage engines.
+ This includes resetting flags to indicate upgrade process
+ and cleanup after upgrade.
+
+ @param thd Thread context
+
+ @return Operation status.
+ @retval == 0 Success.
+ @retval != 0 Error (handler error code returned)
+*/
+typedef int (*finish_upgrade_t)(THD *thd, bool failed_upgrade);
+
+/**
+ Upgrade logs after the checkpoint from where upgrade
+ process can only roll forward.
+
+ @param thd Thread context
+
+ @return Operation status.
+ @retval == 0 Success.
+ @retval != 0 Error (handler error code returned)
+*/
+typedef int (*upgrade_logs_t)(THD *thd);
+
+enum class Tablespace_type {
+ SPACE_TYPE_DICTIONARY,
+ SPACE_TYPE_SYSTEM,
+ SPACE_TYPE_UNDO,
+ SPACE_TYPE_TEMPORARY,
+ SPACE_TYPE_SHARED,
+ SPACE_TYPE_IMPLICIT
+};
+
+/**
+ Get the tablespace type from the SE.
+
+ @param[in] space tablespace object
+ @param[out] space_type type of space
+
+ @return Operation status.
+ @retval false on success and true for failure.
+*/
+typedef bool (*get_tablespace_type_t)(const dd::Tablespace &space,
+ Tablespace_type *space_type);
+
+/**
+ Get the tablespace type given the name, from the SE.
+
+ @param[in] tablespace_name tablespace name
+ @param[out] space_type type of space
+
+ @return Operation status.
+ @retval false on success and true for failure.
+*/
+typedef bool (*get_tablespace_type_by_name_t)(const char *tablespace_name,
+ Tablespace_type *space_type);
+
+typedef int (*fill_is_table_t)(handlerton *hton, THD *thd, TABLE_LIST *tables,
+ class Item *cond, enum enum_schema_tables);
+
+typedef int (*binlog_func_t)(handlerton *hton, THD *thd, enum_binlog_func fn,
+ void *arg);
+
+typedef void (*binlog_log_query_t)(handlerton *hton, THD *thd,
+ enum_binlog_command binlog_command,
+ const char *query, uint query_length,
+ const char *db, const char *table_name);
+
+typedef int (*discover_t)(handlerton *hton, THD *thd, const char *db,
+ const char *name, uchar **frmblob, size_t *frmlen);
+
+typedef int (*find_files_t)(handlerton *hton, THD *thd, const char *db,
+ const char *path, const char *wild, bool dir,
+ List<LEX_STRING> *files);
+
+typedef int (*table_exists_in_engine_t)(handlerton *hton, THD *thd,
+ const char *db, const char *name);
+
+typedef int (*make_pushed_join_t)(handlerton *hton, THD *thd,
+ const AQP::Join_plan *plan);
+
+/**
+ Check if the given db.tablename is a system table for this SE.
+
+ @param db Database name to check.
+ @param table_name table name to check.
+ @param is_sql_layer_system_table if the supplied db.table_name is a SQL
+ layer system table.
+
+ @see example_is_supported_system_table in ha_example.cc
+
+ is_sql_layer_system_table is supplied to make more efficient
+ checks possible for SEs that support all SQL layer tables.
+
+ This interface is optional, so every SE need not implement it.
+*/
+typedef bool (*is_supported_system_table_t)(const char *db,
+ const char *table_name,
+ bool is_sql_layer_system_table);
+
+/**
+ Create SDI in a tablespace. This API should be used when upgrading
+ a tablespace with no SDI or after invoking sdi_drop().
+ @param[in] tablespace tablespace object
+ @retval false success
+ @retval true failure
+*/
+typedef bool (*sdi_create_t)(dd::Tablespace *tablespace);
+
+/**
+ Drop SDI in a tablespace. This API should be used only when
+ SDI is corrupted.
+ @param[in] tablespace tablespace object
+ @retval false success
+ @retval true failure
+*/
+typedef bool (*sdi_drop_t)(dd::Tablespace *tablespace);
+
+/**
+ Get the SDI keys in a tablespace into vector.
+ @param[in] tablespace tablespace object
+ @param[in,out] vector vector of SDI Keys
+ @retval false success
+ @retval true failure
+*/
+typedef bool (*sdi_get_keys_t)(const dd::Tablespace &tablespace,
+ sdi_vector_t &vector);
+
+/**
+ Retrieve SDI for a given SDI key.
+
+ Since the caller of this api will not know the SDI length, SDI retrieval
+ should be done in the following way.
+
+ i. Allocate initial memory of some size (Lets say 64KB)
+ ii. Pass the allocated memory to the below api.
+ iii. If passed buffer is sufficient, sdi_get_by_id() copies the sdi
+ to the buffer passed and returns success, else sdi_len is modified
+ with the actual length of the SDI (and returns false on failure).
+ For genuine errors, sdi_len is returned as UINT64_MAX
+ iv. If sdi_len != UINT64_MAX, retry the call after allocating the memory
+ of sdi_len
+ v. Free the memory after using SDI (responsibility of caller)
+
+ @param[in] tablespace tablespace object
+ @param[in] sdi_key SDI key to uniquely identify SDI obj
+ @param[in,out] sdi SDI retrieved from tablespace
+ A non-null pointer must be passed in
+ @param[in,out] sdi_len in: length of the memory allocated
+ out: actual length of SDI
+ @retval false success
+ @retval true failure
+*/
+typedef bool (*sdi_get_t)(const dd::Tablespace &tablespace,
+ const sdi_key_t *sdi_key, void *sdi, uint64 *sdi_len);
+
+/**
+ Insert/Update SDI for a given SDI key.
+ @param[in] hton handlerton object
+ @param[in] tablespace tablespace object
+ @param[in] sdi_key SDI key to uniquely identify SDI obj
+ @param[in] sdi SDI to write into the tablespace
+ @param[in] sdi_len length of SDI BLOB returned
+ @retval false success
+ @retval true failure, my_error() should be called
+ by SE
+*/
+typedef bool (*sdi_set_t)(handlerton *hton, const dd::Tablespace &tablespace,
+ const dd::Table *table, const sdi_key_t *sdi_key,
+ const void *sdi, uint64 sdi_len);
+
+/**
+ Delete SDI for a given SDI key.
+ @param[in] tablespace tablespace object
+ @param[in] sdi_key SDI key to uniquely identify SDI obj
+ @retval false success
+ @retval true failure, my_error() should be called
+ by SE
+*/
+typedef bool (*sdi_delete_t)(const dd::Tablespace &tablespace,
+ const dd::Table *table, const sdi_key_t *sdi_key);
+
+/**
+ Check if the DDSE is started in a way that leaves thd DD being read only.
+
+ @retval true The data dictionary can only be read.
+ @retval false The data dictionary can be read and written.
+ */
+typedef bool (*is_dict_readonly_t)();
+
+/**
+ Drop all temporary tables which have been left from previous server
+ run belonging to this SE. Used on server start-up.
+
+ @param[in] hton Handlerton for storage engine.
+ @param[in] thd Thread context.
+ @param[in,out] files List of files in directories for temporary files
+ which match tmp_file_prefix and thus can belong to
+ temporary tables (but not necessarily in this SE).
+ It is recommended to remove file from the list if
+ SE recognizes it as belonging to temporary table
+ in this SE and deletes it.
+*/
+typedef bool (*rm_tmp_tables_t)(handlerton *hton, THD *thd,
+ List<LEX_STRING> *files);
+
+/**
+ Retrieve cost constants to be used for this storage engine.
+
+ A storage engine that wants to provide its own cost constants to
+ be used in the optimizer cost model, should implement this function.
+ The server will call this function to get a cost constant object
+ that will be used for tables stored in this storage engine instead
+ of using the default cost constants.
+
+ Life cycle for the cost constant object: The storage engine must
+ allocate the cost constant object on the heap. After the function
+ returns, the server takes over the ownership of this object.
+ The server will eventually delete the object by calling delete.
+
+ @note In the initial version the storage_category parameter will
+ not be used. The only valid value this will have is DEFAULT_STORAGE_CLASS
+ (see declaration in opt_costconstants.h).
+
+ @param storage_category the storage type that the cost constants will
+ be used for
+
+ @return a pointer to the cost constant object, if NULL is returned
+ the default cost constants will be used
+*/
+typedef SE_cost_constants *(*get_cost_constants_t)(uint storage_category);
+
+/**
+ @param[in,out] thd pointer to THD
+ @param[in] new_trx_arg pointer to replacement transaction
+ @param[out] ptr_trx_arg double pointer to being replaced transaction
+
+ Associated with THD engine's native transaction is replaced
+ with @c new_trx_arg. The old value is returned through a buffer if non-null
+ pointer is provided with @c ptr_trx_arg.
+ The method is adapted by XA start and XA prepare handlers to
+ handle XA transaction that is logged as two parts by slave applier.
+
+ This interface concerns engines that are aware of XA transaction.
+*/
+typedef void (*replace_native_transaction_in_thd_t)(THD *thd, void *new_trx_arg,
+ void **ptr_trx_arg);
+
+/** Mode for initializing the data dictionary. */
+enum dict_init_mode_t {
+ DICT_INIT_CREATE_FILES, ///< Create all required SE files
+ DICT_INIT_CHECK_FILES, ///< Verify existence of expected files
+ DICT_INIT_UPGRADE_57_FILES, ///< Used for upgrade from mysql-5.7
+ DICT_INIT_IGNORE_FILES ///< Don't care about files at all
+};
+
+/**
+ Initialize the SE for being used to store the DD tables. Create
+ the required files according to the dict_init_mode. Create strings
+ representing the required DDSE tables, i.e., tables that the DDSE
+ expects to exist in the DD, and add them to the appropriate out
+ parameter.
+
+ @note There are two variants of this function type, one is to be
+ used by the DDSE, and has a different type of output parameters
+ because the SQL layer needs more information about the DDSE tables
+ in order to support upgrade.
+
+ @param dict_init_mode How to initialize files
+ @param version Target DD version if a new
+ server is being installed.
+ 0 if restarting an existing
+ server.
+ @param [out] DDSE_tables List of SQL DDL statements
+ for creating DD tables that
+ are needed by the DDSE.
+ @param [out] DDSE_tablespaces List of meta data for predefined
+ tablespaces created by the DDSE.
+
+ @retval true An error occurred.
+ @retval false Success - no errors.
+ */
+
+typedef bool (*dict_init_t)(dict_init_mode_t dict_init_mode, uint version,
+ List<const Plugin_table> *DDSE_tables,
+ List<const Plugin_tablespace> *DDSE_tablespaces);
+
+typedef bool (*ddse_dict_init_t)(
+ dict_init_mode_t dict_init_mode, uint version,
+ List<const dd::Object_table> *DDSE_tables,
+ List<const Plugin_tablespace> *DDSE_tablespaces);
+
+/**
+ Initialize the set of hard coded DD table ids.
+*/
+typedef void (*dict_register_dd_table_id_t)(dd::Object_id hard_coded_tables);
+
+/**
+ Invalidate an entry in the local dictionary cache.
+
+ Needed during bootstrap to make sure the contents in the DDSE
+ dictionary cache is in sync with the global DD.
+
+ @param schema_name Schema name.
+ @param table_name Table name.
+ */
+
+typedef void (*dict_cache_reset_t)(const char *schema_name,
+ const char *table_name);
+
+/**
+ Invalidate all table and tablespace entries in the local dictionary cache.
+
+ Needed for recovery during server restart.
+ */
+
+typedef void (*dict_cache_reset_tables_and_tablespaces_t)();
+
+/** Mode for data dictionary recovery. */
+enum dict_recovery_mode_t {
+ DICT_RECOVERY_INITIALIZE_SERVER, ///< First start of a new server
+ DICT_RECOVERY_INITIALIZE_TABLESPACES, ///< First start, create tablespaces
+ DICT_RECOVERY_RESTART_SERVER ///< Restart of an existing server
+};
+
+/**
+ Do recovery in the DDSE as part of initializing the data dictionary.
+ The dict_recovery_mode indicates what kind of recovery should be
+ done.
+
+ @param dict_recovery_mode How to do recovery
+ @param version Target DD version if a new
+ server is being installed.
+ Actual DD version if restarting
+ an existing server.
+
+ @retval true An error occurred.
+ @retval false Success - no errors.
+ */
+
+typedef bool (*dict_recover_t)(dict_recovery_mode_t dict_recovery_mode,
+ uint version);
+
+/**
+ Get the server version id stored in the header of the
+ dictionary tablespace.
+
+ @param [out] version Version number from the DD
+ tablespace header.
+
+ @retval Operation outcome, false if no error, otherwise true.
+*/
+typedef bool (*dict_get_server_version_t)(uint *version);
+
+/**
+ Store the current server version number into the
+ header of the dictionary tablespace.
+
+ @retval Operation outcome, false if no error, otherwise true.
+*/
+typedef bool (*dict_set_server_version_t)();
+
+/**
+ Notify/get permission from storage engine before acquisition or after
+ release of exclusive metadata lock on object represented by key.
+
+ @param thd Thread context.
+ @param mdl_key MDL key identifying object on which exclusive
+ lock is to be acquired/was released.
+ @param notification_type Indicates whether this is pre-acquire or
+ post-release notification.
+ @param victimized 'true' if locking failed as we were selected
+ as a victim in order to avoid possible deadlocks.
+
+ @note Notification is done only for objects from TABLESPACE, SCHEMA,
+ TABLE, FUNCTION, PROCEDURE, TRIGGER and EVENT namespaces.
+
+ @note Problems during notification are to be reported as warnings, MDL
+ subsystem will report generic error if pre-acquire notification
+ fails/SE refuses lock acquisition.
+ @note Return value is ignored/error is not reported in case of
+ post-release notification.
+
+ @note In some cases post-release notification might happen even if
+ there were no prior pre-acquire notification. For example,
+ when SE was loaded after exclusive lock acquisition, or when
+ we need notify SEs which permitted lock acquisition that it
+ didn't happen because one of SEs didn't allow it (in such case
+ we will do post-release notification for all SEs for simplicity).
+
+ @return False - if notification was successful/lock can be acquired,
+ True - if it has failed/lock should not be acquired.
+*/
+typedef bool (*notify_exclusive_mdl_t)(THD *thd, const MDL_key *mdl_key,
+ ha_notification_type notification_type,
+ bool *victimized);
+
+/**
+ Notify/get permission from storage engine before or after execution of
+ ALTER TABLE operation on the table identified by the MDL key.
+
+ @param thd Thread context.
+ @param mdl_key MDL key identifying table which is going to be
+ or was ALTERed.
+ @param notification_type Indicates whether this is pre-ALTER TABLE or
+ post-ALTER TABLE notification.
+
+ @note This hook is necessary because for ALTER TABLE upgrade to X
+ metadata lock happens fairly late during the execution process,
+ so it can be expensive to abort ALTER TABLE operation at this
+ stage by returning failure from notify_exclusive_mdl() hook.
+
+ @note This hook follows the same error reporting convention as
+ @see notify_exclusive_mdl().
+
+ @note Similarly to notify_exclusive_mdl() in some cases post-ALTER
+ notification might happen even if there were no prior pre-ALTER
+ notification.
+
+ @note Post-ALTER notification can happen before post-release notification
+ for exclusive metadata lock acquired by this ALTER TABLE.
+
+ @return False - if notification was successful/ALTER TABLE can proceed.
+ True - if it has failed/ALTER TABLE should be aborted.
+*/
+typedef bool (*notify_alter_table_t)(THD *thd, const MDL_key *mdl_key,
+ ha_notification_type notification_type);
+
+/**
+ @brief
+ Initiate master key rotation
+
+ @returns false on success,
+ true on failure
+*/
+typedef bool (*rotate_encryption_master_key_t)(void);
+
+/**
+ @brief
+ Retrieve ha_statistics from SE.
+
+ @param db_name Name of schema
+ @param table_name Name of table
+ @param se_private_id SE private id of the table.
+ @param ts_se_private_data Tablespace SE private data.
+ @param tbl_se_private_data Table SE private data.
+ @param flags Type of statistics to retrieve.
+ @param[out] stats Contains statistics read from SE.
+
+ @note Handlers that implement this callback/API should adhere
+ to servers expectation that, the implementation would invoke
+ my_error() before returning 'true'/failure from this function.
+
+ @returns false on success,
+ true on failure
+*/
+typedef bool (*get_table_statistics_t)(
+ const char *db_name, const char *table_name, dd::Object_id se_private_id,
+ const dd::Properties &ts_se_private_data,
+ const dd::Properties &tbl_se_private_data, uint flags,
+ ha_statistics *stats);
+
+/**
+ @brief
+ Retrieve index column cardinality from SE.
+
+ @param db_name Name of schema
+ @param table_name Name of table
+ @param index_name Name of index
+ @param index_ordinal_position Position of index.
+ @param column_ordinal_position Position of column in index.
+ @param se_private_id SE private id of the table.
+ @param[out] cardinality cardinality being returned by SE.
+
+ @note Handlers that implement this callback/API should adhere
+ to servers expectation that, the implementation would invoke
+ my_error() before returning 'true'/failure from this function.
+
+ @returns false on success,
+ true on failure
+*/
+typedef bool (*get_index_column_cardinality_t)(
+ const char *db_name, const char *table_name, const char *index_name,
+ uint index_ordinal_position, uint column_ordinal_position,
+ dd::Object_id se_private_id, ulonglong *cardinality);
+
+/**
+ Retrieve ha_tablespace_statistics from SE.
+
+ @param tablespace_name Tablespace_name
+ @param ts_se_private_data Tablespace SE private data.
+ @param tbl_se_private_data Table SE private data.
+ @param[out] stats Contains tablespace
+ statistics read from SE.
+
+ @note Handlers that implement this callback/API should adhere
+ to servers expectation that, the implementation would invoke
+ my_error() before returning 'true'/failure from this function.
+
+ @returns false on success, true on failure
+*/
+typedef bool (*get_tablespace_statistics_t)(
+ const char *tablespace_name, const char *file_name,
+ const dd::Properties &ts_se_private_data, ha_tablespace_statistics *stats);
+
+/* Database physical clone interfaces */
+
+/** Get capability flags for clone operation
+@param[out] flags capability flag */
+using Clone_capability_t = void (*)(Ha_clone_flagset &flags);
+
+/** Begin copy from source database
+@param[in] hton handlerton for SE
+@param[in] thd server thread handle
+@param[in,out] loc locator
+@param[in,out] loc_len locator length
+@param[out] task_id task identifier
+@param[in] type clone type
+@param[in] mode mode for starting clone
+@return error code */
+using Clone_begin_t = int (*)(handlerton *hton, THD *thd, const uchar *&loc,
+ uint &loc_len, uint &task_id, Ha_clone_type type,
+ Ha_clone_mode mode);
+
+/** Copy data from source database in chunks via callback
+@param[in] hton handlerton for SE
+@param[in] thd server thread handle
+@param[in] loc locator
+@param[in] loc_len locator length in bytes
+@param[in] task_id task identifier
+@param[in] cbk callback interface for sending data
+@return error code */
+using Clone_copy_t = int (*)(handlerton *hton, THD *thd, const uchar *loc,
+ uint loc_len, uint task_id, Ha_clone_cbk *cbk);
+
+/** Acknowledge data transfer to source database
+@param[in] hton handlerton for SE
+@param[in] thd server thread handle
+@param[in] loc locator
+@param[in] loc_len locator length in bytes
+@param[in] task_id task identifier
+@param[in] in_err inform any error occurred
+@param[in] cbk callback interface
+@return error code */
+using Clone_ack_t = int (*)(handlerton *hton, THD *thd, const uchar *loc,
+ uint loc_len, uint task_id, int in_err,
+ Ha_clone_cbk *cbk);
+
+/** End copy from source database
+@param[in] hton handlerton for SE
+@param[in] thd server thread handle
+@param[in] loc locator
+@param[in] loc_len locator length in bytes
+@param[in] task_id task identifier
+@param[in] in_err error code when ending after error
+@return error code */
+using Clone_end_t = int (*)(handlerton *hton, THD *thd, const uchar *loc,
+ uint loc_len, uint task_id, int in_err);
+
+/** Begin apply to destination database
+@param[in] hton handlerton for SE
+@param[in] thd server thread handle
+@param[in,out] loc locator
+@param[in,out] loc_len locator length
+@param[in] task_id task identifier
+@param[in] mode mode for starting clone
+@param[in] data_dir target data directory
+@return error code */
+using Clone_apply_begin_t = int (*)(handlerton *hton, THD *thd,
+ const uchar *&loc, uint &loc_len,
+ uint &task_id, Ha_clone_mode mode,
+ const char *data_dir);
+
+/** Apply data to destination database in chunks via callback
+@param[in] hton handlerton for SE
+@param[in] thd server thread handle
+@param[in] loc locator
+@param[in] loc_len locator length in bytes
+@param[in] task_id task identifier
+@param[in] in_err inform any error occurred
+@param[in] cbk callback interface for receiving data
+@return error code */
+using Clone_apply_t = int (*)(handlerton *hton, THD *thd, const uchar *loc,
+ uint loc_len, uint task_id, int in_err,
+ Ha_clone_cbk *cbk);
+
+/** End apply to destination database
+@param[in] hton handlerton for SE
+@param[in] thd server thread handle
+@param[in] loc locator
+@param[in] loc_len locator length in bytes
+@param[in] task_id task identifier
+@param[in] in_err error code when ending after error
+@return error code */
+using Clone_apply_end_t = int (*)(handlerton *hton, THD *thd, const uchar *loc,
+ uint loc_len, uint task_id, int in_err);
+
+struct Clone_interface_t {
+ /* Get clone capabilities of an SE */
+ Clone_capability_t clone_capability;
+
+ /* Interfaces to copy data. */
+ Clone_begin_t clone_begin;
+ Clone_copy_t clone_copy;
+ Clone_ack_t clone_ack;
+ Clone_end_t clone_end;
+
+ /* Interfaces to apply data. */
+ Clone_apply_begin_t clone_apply_begin;
+ Clone_apply_t clone_apply;
+ Clone_apply_end_t clone_apply_end;
+};
+
+/**
+ Perform post-commit/rollback cleanup after DDL statement (e.g. in
+ case of DROP TABLES really remove table files from disk).
+
+ @note This hook will be invoked after DDL commit or rollback only
+ for storage engines supporting atomic DDL.
+
+ @note Problems during execution of this method should be reported to
+ error log and as warnings/notes to user. Since this method is
+ called after successful commit of the statement we can't fail
+ statement with error.
+*/
+typedef void (*post_ddl_t)(THD *thd);
+
+/**
+ Perform SE-specific cleanup after recovery of transactions.
+
+ @note Particularly SEs supporting atomic DDL can use this call
+ to perform post-DDL actions for DDL statements which were
+ committed or rolled back during recovery stage.
+*/
+typedef void (*post_recover_t)(void);
+
+/**
+ Lock a handlerton (resource) log to collect log information.
+*/
+
+typedef bool (*lock_hton_log_t)(handlerton *hton);
+
+/**
+ Unlock a handlerton (resource) log after collecting log information.
+*/
+
+typedef bool (*unlock_hton_log_t)(handlerton *hton);
+
+/**
+ Collect a handlerton (resource) log information.
+*/
+
+typedef bool (*collect_hton_log_info_t)(handlerton *hton, Json_dom *json);
+
+/**
+ Check SE considers types of child and parent columns in foreign key
+ to be compatible.
+
+ @param child_column_type Child column type description.
+ @param parent_column_type Parent column type description.
+ @param check_charsets Indicates whether we need to check
+ that charsets of string columns
+ match. Which is true in most cases.
+
+ @returns True if types are compatible, False if not.
+*/
+
+typedef bool (*check_fk_column_compat_t)(
+ const Ha_fk_column_type *child_column_type,
+ const Ha_fk_column_type *parent_column_type, bool check_charsets);
+
+typedef bool (*is_reserved_db_name_t)(handlerton *hton, const char *name);
+
+/**
+ Prepare the secondary engine for executing a statement. This function is
+ called right after the secondary engine TABLE objects have been opened by
+ open_secondary_engine_tables(), before the statement is optimized and
+ executed. Secondary engines will typically create a context object in this
+ function, which they can use to store state that is needed during the
+ optimization and execution phases.
+
+ @param thd thread context
+ @param lex the statement to execute
+ @return true on error, false on success
+*/
+using prepare_secondary_engine_t = bool (*)(THD *thd, LEX *lex);
+
+/**
+ Optimize a statement for execution on a secondary storage engine. This
+ function is called when the optimization of a statement has completed, just
+ before the statement is executed. Secondary engines can use this function to
+ apply engine-specific optimizations to the execution plan. They can also
+ reject executing the query by raising an error, in which case the query will
+ be reprepared and executed by the primary storage engine.
+
+ @param thd thread context
+ @param lex the statement being optimized
+ @return true on error, false on success
+*/
+using optimize_secondary_engine_t = bool (*)(THD *thd, LEX *lex);
+
+/**
+ Compares the cost of two join plans in the secondary storage engine. The cost
+ of the current candidate is compared with the cost of the best plan seen so
+ far.
+
+ @param thd thread context
+ @param join the JOIN to evaluate
+ @param table_order the ordering of the tables in the candidate plan
+ @param optimizer_cost the cost estimate calculated by the optimizer
+ @param[out] cheaper true if the candidate is the best plan seen so far for
+ this JOIN (must be true if it is the first plan seen),
+ false otherwise
+ @param[out] secondary_engine_cost the cost estimated by the secondary engine
+
+ @return false on success, or true if an error has been raised
+*/
+using compare_secondary_engine_cost_t = bool (*)(
+ THD *thd, const JOIN &join, const Candidate_table_order &table_order,
+ double optimizer_cost, bool *cheaper, double *secondary_engine_cost);
+
+// FIXME: Temporary workaround to enable storage engine plugins to use the
+// before_commit hook. Remove after WL#11320 has been completed.
+typedef void (*se_before_commit_t)(void *arg);
+
+// FIXME: Temporary workaround to enable storage engine plugins to use the
+// after_commit hook. Remove after WL#11320 has been completed.
+typedef void (*se_after_commit_t)(void *arg);
+
+// FIXME: Temporary workaround to enable storage engine plugins to use the
+// before_rollback hook. Remove after WL#11320 has been completed.
+typedef void (*se_before_rollback_t)(void *arg);
+
+/*
+ Page Tracking : interfaces to handlerton functions which starts/stops page
+ tracking, and purges/fetches page tracking information.
+*/
+
+/**
+ Start page tracking.
+
+ @param[out] start_id SE specific sequence number [LSN for InnoDB]
+ indicating when the tracking was started
+
+ @return Operation status.
+ @retval 0 Success
+ @retval other ER_* mysql error. Get error details from THD.
+*/
+using page_track_start_t = int (*)(uint64_t *start_id);
+
+/**
+ Stop page tracking.
+
+ @param[out] stop_id SE specific sequence number [LSN for InnoDB]
+ indicating when the tracking was stopped
+
+ @return Operation status.
+ @retval 0 Success
+ @retval other ER_* mysql error. Get error details from THD.
+*/
+using page_track_stop_t = int (*)(uint64_t *stop_id);
+
+/**
+ Purge page tracking data.
+
+ @param[in,out] purge_id SE specific sequence number [LSN for InnoDB]
+ initially indicating till where the data needs to be purged and finally
+ updated to until where it was actually purged
+
+ @return Operation status.
+ @retval 0 Success
+ @retval other ER_* mysql error. Get error details from THD.
+*/
+using page_track_purge_t = int (*)(uint64_t *purge_id);
+
+/**
+ Fetch tracked pages.
+
+ @param[in] cbk_func callback function return page IDs
+ @param[in] cbk_ctx caller's context for callback
+ @param[in,out] start_id SE specific sequence number [LSN for InnoDB] from
+ where the pages tracked would be returned.
+ @note The range might get expanded and the actual start_id used for the
+ querying will be updated.
+ @param[in,out] stop_id SE specific sequence number [LSN for InnoDB]
+ until where the pages tracked would be returned.
+ @note The range might get expanded and the actual stop_id used for the
+ querying will be updated.
+ @param[out] buffer allocated buffer to copy page IDs
+ @param[in] buffer_len length of buffer in bytes
+
+ @return Operation status.
+ @retval 0 Success
+ @retval other ER_* mysql error. Get error details from THD.
+*/
+using page_track_get_page_ids_t = int (*)(Page_Track_Callback cbk_func,
+ void *cbk_ctx, uint64_t *start_id,
+ uint64_t *stop_id,
+ unsigned char *buffer,
+ size_t buffer_len);
+
+/**
+ Fetch approximate number of tracked pages in the given range.
+
+ @param[in,out] start_id SE specific sequence number [LSN for InnoDB] from
+ where the pages tracked would be returned.
+ @note the range might get expanded and the actual start_id used for the
+ querying will be updated.
+ @param[in,out] stop_id SE specific sequence number [LSN for InnoDB]
+ until where the pages tracked would be returned.
+ @note the range might get expanded and the actual stop_id used for the
+ querying will be updated.
+ @param[out] num_pages number of pages tracked
+
+ @return Operation status.
+ @retval 0 Success
+ @retval other ER_* mysql error. Get error details from THD.
+*/
+using page_track_get_num_page_ids_t = int (*)(uint64_t *start_id,
+ uint64_t *stop_id,
+ uint64_t *num_pages);
+
+/** Fetch the status of the page tracking system.
+@param[out] status vector of a pair of (ID, bool) where ID is the
+start/stop point and bool is true if the ID is a start point else false */
+using page_track_get_status_t =
+ void (*)(std::vector<std::pair<uint64_t, bool>> &status);
+
+/** Page track interface */
+struct Page_track_t {
+ page_track_start_t start;
+ page_track_stop_t stop;
+ page_track_purge_t purge;
+ page_track_get_page_ids_t get_page_ids;
+ page_track_get_num_page_ids_t get_num_page_ids;
+ page_track_get_status_t get_status;
+};
+
+/**
+ handlerton is a singleton structure - one instance per storage engine -
+ to provide access to storage engine functionality that works on the
+ "global" level (unlike handler class that works on a per-table basis).
+
+ usually handlerton instance is defined statically in ha_xxx.cc as
+
+ static handlerton { ... } xxx_hton;
+
+ savepoint_*, prepare, recover, and *_by_xid pointers can be 0.
+*/
+struct handlerton {
+ /**
+ Historical marker for if the engine is available or not.
+ */
+ SHOW_COMP_OPTION state;
+
+ /**
+ Historical number used for frm file to determine the correct storage engine.
+ This is going away and new engines will just use "name" for this.
+ */
+ enum legacy_db_type db_type;
+ /**
+ Each storage engine has it's own memory area (actually a pointer)
+ in the thd, for storing per-connection information.
+ It is accessed as
+
+ thd->ha_data[xxx_hton.slot]
+
+ slot number is initialized by MySQL after xxx_init() is called.
+ */
+ uint slot;
+ /**
+ To store per-savepoint data storage engine is provided with an area
+ of a requested size (0 is ok here).
+ savepoint_offset must be initialized statically to the size of
+ the needed memory to store per-savepoint information.
+ After xxx_init it is changed to be an offset to savepoint storage
+ area and need not be used by storage engine.
+ see binlog_hton and binlog_savepoint_set/rollback for an example.
+ */
+ uint savepoint_offset;
+
+ /* handlerton methods */
+
+ close_connection_t close_connection;
+ kill_connection_t kill_connection;
+ pre_dd_shutdown_t pre_dd_shutdown;
+ savepoint_set_t savepoint_set;
+ savepoint_rollback_t savepoint_rollback;
+ savepoint_rollback_can_release_mdl_t savepoint_rollback_can_release_mdl;
+ savepoint_release_t savepoint_release;
+ commit_t commit;
+ rollback_t rollback;
+ prepare_t prepare;
+ recover_t recover;
+ commit_by_xid_t commit_by_xid;
+ rollback_by_xid_t rollback_by_xid;
+ create_t create;
+ drop_database_t drop_database;
+ panic_t panic;
+ start_consistent_snapshot_t start_consistent_snapshot;
+ flush_logs_t flush_logs;
+ show_status_t show_status;
+ partition_flags_t partition_flags;
+ is_valid_tablespace_name_t is_valid_tablespace_name;
+ get_tablespace_t get_tablespace;
+ alter_tablespace_t alter_tablespace;
+ get_tablespace_filename_ext_t get_tablespace_filename_ext;
+ upgrade_tablespace_t upgrade_tablespace;
+ upgrade_space_version_t upgrade_space_version;
+ get_tablespace_type_t get_tablespace_type;
+ get_tablespace_type_by_name_t get_tablespace_type_by_name;
+ upgrade_logs_t upgrade_logs;
+ finish_upgrade_t finish_upgrade;
+ fill_is_table_t fill_is_table;
+ dict_init_t dict_init;
+ ddse_dict_init_t ddse_dict_init;
+ dict_register_dd_table_id_t dict_register_dd_table_id;
+ dict_cache_reset_t dict_cache_reset;
+ dict_cache_reset_tables_and_tablespaces_t
+ dict_cache_reset_tables_and_tablespaces;
+ dict_recover_t dict_recover;
+ dict_get_server_version_t dict_get_server_version;
+ dict_set_server_version_t dict_set_server_version;
+ is_reserved_db_name_t is_reserved_db_name;
+
+ /** Global handler flags. */
+ uint32 flags;
+
+ /*
+ Those handlerton functions below are properly initialized at handler
+ init.
+ */
+
+ binlog_func_t binlog_func;
+ binlog_log_query_t binlog_log_query;
+ discover_t discover;
+ find_files_t find_files;
+ table_exists_in_engine_t table_exists_in_engine;
+ make_pushed_join_t make_pushed_join;
+ is_supported_system_table_t is_supported_system_table;
+
+ /*
+ APIs for retrieving Serialized Dictionary Information by tablespace id
+ */
+
+ sdi_create_t sdi_create;
+ sdi_drop_t sdi_drop;
+ sdi_get_keys_t sdi_get_keys;
+ sdi_get_t sdi_get;
+ sdi_set_t sdi_set;
+ sdi_delete_t sdi_delete;
+
+ /**
+ Null-ended array of file extentions that exist for the storage engine.
+ Used by frm_error() and the default handler::rename_table and delete_table
+ methods in handler.cc.
+
+ For engines that have two file name extentions (separate meta/index file
+ and data file), the order of elements is relevant. First element of engine
+ file name extentions array should be meta/index file extention. Second
+ element - data file extention. This order is assumed by
+ prepare_for_repair() when REPAIR TABLE ... USE_FRM is issued.
+
+ For engines that don't have files, file_extensions is NULL.
+
+ Currently, the following alternatives are used:
+ - file_extensions == NULL;
+ - file_extensions[0] != NULL, file_extensions[1] == NULL;
+ - file_extensions[0] != NULL, file_extensions[1] != NULL,
+ file_extensions[2] == NULL;
+ */
+ const char **file_extensions;
+
+ is_dict_readonly_t is_dict_readonly;
+ rm_tmp_tables_t rm_tmp_tables;
+ get_cost_constants_t get_cost_constants;
+ replace_native_transaction_in_thd_t replace_native_transaction_in_thd;
+ notify_exclusive_mdl_t notify_exclusive_mdl;
+ notify_alter_table_t notify_alter_table;
+ rotate_encryption_master_key_t rotate_encryption_master_key;
+
+ get_table_statistics_t get_table_statistics;
+ get_index_column_cardinality_t get_index_column_cardinality;
+ get_tablespace_statistics_t get_tablespace_statistics;
+
+ post_ddl_t post_ddl;
+ post_recover_t post_recover;
+
+ /** Clone data transfer interfaces */
+ Clone_interface_t clone_interface;
+
+ /** Flag for Engine License. */
+ uint32 license;
+ /** Location for engines to keep personal structures. */
+ void *data;
+
+ /*
+ Log_resource functions that must be supported by storage engines
+ with relevant log information to be collected.
+ */
+ lock_hton_log_t lock_hton_log;
+ unlock_hton_log_t unlock_hton_log;
+ collect_hton_log_info_t collect_hton_log_info;
+
+ /** Flags describing details of foreign key support by storage engine. */
+ uint32 foreign_keys_flags;
+
+ check_fk_column_compat_t check_fk_column_compat;
+
+ /**
+ Pointer to a function that prepares a secondary engine for executing a
+ statement.
+
+ @see prepare_secondary_engine_t for function signature.
+ */
+ prepare_secondary_engine_t prepare_secondary_engine;
+
+ /**
+ Pointer to a function that optimizes the current statement for
+ execution on the secondary storage engine represented by this
+ handlerton.
+
+ @see optimize_secondary_engine_t for function signature.
+ */
+ optimize_secondary_engine_t optimize_secondary_engine;
+
+ /**
+ Pointer to a function that estimates the cost of executing a join in a
+ secondary storage engine.
+
+ @see compare_secondary_engine_cost_t for function signature.
+ */
+ compare_secondary_engine_cost_t compare_secondary_engine_cost;
+
+ se_before_commit_t se_before_commit;
+ se_after_commit_t se_after_commit;
+ se_before_rollback_t se_before_rollback;
+
+ /** Page tracking interface */
+ Page_track_t page_track;
+};
+
+/* Possible flags of a handlerton (there can be 32 of them) */
+#define HTON_NO_FLAGS 0
+#define HTON_CLOSE_CURSORS_AT_COMMIT (1 << 0)
+#define HTON_ALTER_NOT_SUPPORTED (1 << 1) // Engine does not support alter
+#define HTON_CAN_RECREATE (1 << 2) // Delete all is used fro truncate
+#define HTON_HIDDEN (1 << 3) // Engine does not appear in lists
+/*
+ Bit 4 was occupied by BDB-specific HTON_FLUSH_AFTER_RENAME flag and is no
+ longer used.
+*/
+#define HTON_NOT_USER_SELECTABLE (1 << 5)
+#define HTON_TEMPORARY_NOT_SUPPORTED \
+ (1 << 6) // Having temporary tables not supported
+#define HTON_SUPPORT_LOG_TABLES (1 << 7) // Engine supports log tables
+#define HTON_NO_PARTITION (1 << 8) // You can not partition these tables
+
+/*
+ This flag should be set when deciding that the engine does not allow row based
+ binary logging (RBL) optimizations.
+
+ Currently, setting this flag, means that table's read/write_set will be left
+ untouched when logging changes to tables in this engine. In practice this
+ means that the server will not mess around with table->write_set and/or
+ table->read_set when using RBL and deciding whether to log full or minimal
+ rows.
+
+ It's valuable for instance for virtual tables, eg: Performance Schema which
+ have no meaning for replication.
+*/
+#define HTON_NO_BINLOG_ROW_OPT (1 << 9)
+
+/**
+ Engine supports extended keys. The flag allows to
+ use 'extended key' feature if the engine is able to
+ do it (has primary key values in the secondary key).
+ Note that handler flag HA_PRIMARY_KEY_IN_READ_INDEX is
+ actually partial case of HTON_SUPPORTS_EXTENDED_KEYS.
+*/
+
+#define HTON_SUPPORTS_EXTENDED_KEYS (1 << 10)
+
+// Engine support foreign key constraint.
+
+#define HTON_SUPPORTS_FOREIGN_KEYS (1 << 11)
+
+/**
+ Engine supports atomic DDL. That is rollback of transaction for DDL
+ statement will also rollback all changes in SE, commit of transaction
+ of DDL statement will make it durable.
+*/
+
+#define HTON_SUPPORTS_ATOMIC_DDL (1 << 12)
+
+/* Engine supports packed keys. */
+#define HTON_SUPPORTS_PACKED_KEYS (1 << 13)
+
+/** Engine is a secondary storage engine. */
+#define HTON_IS_SECONDARY_ENGINE (1 << 14)
+
+/** Engine supports secondary storage engines. */
+#define HTON_SUPPORTS_SECONDARY_ENGINE (1 << 15)
+
+/** Engine supports table or tablespace encryption . */
+#define HTON_SUPPORTS_TABLE_ENCRYPTION (1 << 16)
+
+inline bool ddl_is_atomic(const handlerton *hton) {
+ return (hton->flags & HTON_SUPPORTS_ATOMIC_DDL) != 0;
+}
+
+/* Bits for handlerton::foreign_keys_flags bitmap. */
+
+/**
+ Engine supports both unique and non-unique parent keys for
+ foreign keys which contain full foreign key as its prefix.
+
+ Storage engines which support foreign keys but do not have
+ this flag set are assumed to support only parent keys which
+ are primary/unique and contain exactly the same columns as
+ the foreign key, possibly, in different order.
+*/
+
+static const uint32 HTON_FKS_WITH_PREFIX_PARENT_KEYS = (1 << 0);
+
+/**
+ Storage engine supports hash keys as supporting keys for foreign
+ keys. Hash key should contain all foreign key columns and only
+ them (altough in any order).
+
+ Storage engines which support foreign keys but do not have this
+ flag set are assumed to not allow hash keys as supporting keys.
+*/
+
+static const uint32 HTON_FKS_WITH_SUPPORTING_HASH_KEYS = (1 << 1);
+
+/**
+ Storage engine supports non-hash keys which have common prefix
+ with the foreign key as supporting keys for it. If there are
+ several such keys, one which shares biggest prefix with FK is
+ chosen.
+
+ Storage engines which support foreign keys but do not have this
+ flag set are assumed to require that supporting key contains full
+ foreign key as its prefix.
+*/
+
+static const uint32 HTON_FKS_WITH_ANY_PREFIX_SUPPORTING_KEYS = (1 << 2);
+
+/**
+ Storage engine does not support using the same key for both parent
+ and supporting key, but requires the two to be different.
+*/
+
+static const uint32 HTON_FKS_NEED_DIFFERENT_PARENT_AND_SUPPORTING_KEYS =
+ (1 << 3);
+
+/**
+ Engine takes into account hidden part of key (coming from primary key)
+ when determines if it can serve as parent key for a foreign key.
+
+ Implies HTON_FKS_WITH_PREFIX_PARENT_KEYS and is related to
+ HTON_SUPPORTS_EXTENDED_KEYS.
+*/
+
+static const uint32 HTON_FKS_WITH_EXTENDED_PARENT_KEYS = (1 << 4);
+
+enum enum_tx_isolation : int {
+ ISO_READ_UNCOMMITTED,
+ ISO_READ_COMMITTED,
+ ISO_REPEATABLE_READ,
+ ISO_SERIALIZABLE
+};
+
+enum enum_stats_auto_recalc : int {
+ HA_STATS_AUTO_RECALC_DEFAULT = 0,
+ HA_STATS_AUTO_RECALC_ON,
+ HA_STATS_AUTO_RECALC_OFF
+};
+
+/* struct to hold information about the table that should be created */
+struct HA_CREATE_INFO {
+ const CHARSET_INFO *table_charset{nullptr};
+ const CHARSET_INFO *default_table_charset{nullptr};
+ LEX_STRING connect_string{nullptr, 0};
+ const char *password{nullptr};
+ const char *tablespace{nullptr};
+ LEX_STRING comment{nullptr, 0};
+
+ /**
+ Algorithm (and possible options) to be used for InnoDB's transparent
+ page compression. If this attribute is set then it is hint to the
+ storage engine to try and compress the data using the specified algorithm
+ where possible. Note: this value is interpreted by the storage engine only.
+ and ignored by the Server layer. */
+
+ LEX_STRING compress{nullptr, 0};
+
+ /**
+ This attibute is used for InnoDB's transparent page encryption.
+ If this attribute is set then it is hint to the storage engine to encrypt
+ the data. Note: this value is interpreted by the storage engine only.
+ and ignored by the Server layer. */
+
+ LEX_STRING encrypt_type{nullptr, 0};
+
+ /**
+ * Secondary engine of the table.
+ * Is nullptr if no secondary engine defined.
+ */
+ LEX_CSTRING secondary_engine{nullptr, 0};
+
+ const char *data_file_name{nullptr};
+ const char *index_file_name{nullptr};
+ const char *alias{nullptr};
+ ulonglong max_rows{0};
+ ulonglong min_rows{0};
+ ulonglong auto_increment_value{0};
+ ulong table_options{0};
+ ulong avg_row_length{0};
+ ulong used_fields{0};
+ ulong key_block_size{0};
+ uint stats_sample_pages{0}; /* number of pages to sample during
+ stats estimation, if used, otherwise 0. */
+ enum_stats_auto_recalc stats_auto_recalc{HA_STATS_AUTO_RECALC_DEFAULT};
+ SQL_I_List<TABLE_LIST> merge_list;
+ handlerton *db_type{nullptr};
+ /**
+ Row type of the table definition.
+
+ Defaults to ROW_TYPE_DEFAULT for all non-ALTER statements.
+ For ALTER TABLE defaults to ROW_TYPE_NOT_USED (means "keep the current").
+
+ Can be changed either explicitly by the parser.
+ If nothing specified inherits the value of the original table (if present).
+ */
+ enum row_type row_type = ROW_TYPE_DEFAULT;
+ uint null_bits{0}; /* NULL bits at start of record */
+ uint options{0}; /* OR of HA_CREATE_ options */
+ uint merge_insert_method{0};
+ ha_storage_media storage_media{HA_SM_DEFAULT}; /* DEFAULT, DISK or MEMORY */
+
+ /*
+ A flag to indicate if this table should be marked as a hidden table in
+ the data dictionary. One use case is to mark the temporary tables
+ created by ALTER to be marked as hidden.
+ */
+ bool m_hidden{false};
+
+ /**
+ Fill HA_CREATE_INFO to be used by ALTER as well as upgrade code.
+ This function separates code from mysql_prepare_alter_table() to be
+ used by upgrade code as well to reduce code duplication.
+ For ALTER code path, this lets new create options override the old
+ ones.
+
+ @param[in] share TABLE_SHARE object
+ @param[in] used_fields If a given create option is not flagged, old
+ value be copied from the TABLE_SHARE.
+ */
+
+ void init_create_options_from_share(const TABLE_SHARE *share,
+ uint used_fields);
+};
+
+/**
+ Structure describing changes to an index to be caused by ALTER TABLE.
+*/
+
+struct KEY_PAIR {
+ /**
+ Pointer to KEY object describing old version of index in
+ TABLE::key_info array for TABLE instance representing old
+ version of table.
+ */
+ KEY *old_key;
+ /**
+ Pointer to KEY object describing new version of index in
+ Alter_inplace_info::key_info_buffer array.
+ */
+ KEY *new_key;
+};
+
+/**
+ In-place alter handler context.
+
+ This is a superclass intended to be subclassed by individual handlers
+ in order to store handler unique context between in-place alter API calls.
+
+ The handler is responsible for creating the object. This can be done
+ as early as during check_if_supported_inplace_alter().
+
+ The SQL layer is responsible for destroying the object.
+
+ @see Alter_inplace_info
+*/
+
+class inplace_alter_handler_ctx {
+ public:
+ inplace_alter_handler_ctx() {}
+
+ virtual void set_shared_data(
+ const inplace_alter_handler_ctx *ctx MY_ATTRIBUTE((unused))) {}
+ virtual ~inplace_alter_handler_ctx() {}
+};
+
+/**
+ Class describing changes to be done by ALTER TABLE.
+ Instance of this class is passed to storage engine in order
+ to determine if this ALTER TABLE can be done using in-place
+ algorithm. It is also used for executing the ALTER TABLE
+ using in-place algorithm.
+*/
+
+class Alter_inplace_info {
+ public:
+ /**
+ Bits to show in detail what operations the storage engine is
+ to execute.
+
+ All these operations are supported as in-place operations by the
+ SQL layer. This means that operations that by their nature must
+ be performed by copying the table to a temporary table, will not
+ have their own flags here (e.g. ALTER TABLE FORCE, ALTER TABLE
+ ENGINE).
+
+ We generally try to specify handler flags only if there are real
+ changes. But in cases when it is cumbersome to determine if some
+ attribute has really changed we might choose to set flag
+ pessimistically, for example, relying on parser output only.
+ */
+ typedef ulonglong HA_ALTER_FLAGS;
+
+ // Add non-unique, non-primary index
+ static const HA_ALTER_FLAGS ADD_INDEX = 1ULL << 0;
+
+ // Drop non-unique, non-primary index
+ static const HA_ALTER_FLAGS DROP_INDEX = 1ULL << 1;
+
+ // Add unique, non-primary index
+ static const HA_ALTER_FLAGS ADD_UNIQUE_INDEX = 1ULL << 2;
+
+ // Drop unique, non-primary index
+ static const HA_ALTER_FLAGS DROP_UNIQUE_INDEX = 1ULL << 3;
+
+ // Add primary index
+ static const HA_ALTER_FLAGS ADD_PK_INDEX = 1ULL << 4;
+
+ // Drop primary index
+ static const HA_ALTER_FLAGS DROP_PK_INDEX = 1ULL << 5;
+
+ // Add column
+
+ // Virtual generated column
+ static const HA_ALTER_FLAGS ADD_VIRTUAL_COLUMN = 1ULL << 6;
+ // Stored base (non-generated) column
+ static const HA_ALTER_FLAGS ADD_STORED_BASE_COLUMN = 1ULL << 7;
+ // Stored generated column
+ static const HA_ALTER_FLAGS ADD_STORED_GENERATED_COLUMN = 1ULL << 8;
+ // Add generic column (convience constant).
+ static const HA_ALTER_FLAGS ADD_COLUMN =
+ ADD_VIRTUAL_COLUMN | ADD_STORED_BASE_COLUMN | ADD_STORED_GENERATED_COLUMN;
+
+ // Drop column
+ static const HA_ALTER_FLAGS DROP_VIRTUAL_COLUMN = 1ULL << 9;
+ static const HA_ALTER_FLAGS DROP_STORED_COLUMN = 1ULL << 10;
+ static const HA_ALTER_FLAGS DROP_COLUMN =
+ DROP_VIRTUAL_COLUMN | DROP_STORED_COLUMN;
+
+ // Rename column
+ static const HA_ALTER_FLAGS ALTER_COLUMN_NAME = 1ULL << 11;
+
+ // Change column datatype
+ static const HA_ALTER_FLAGS ALTER_VIRTUAL_COLUMN_TYPE = 1ULL << 12;
+ static const HA_ALTER_FLAGS ALTER_STORED_COLUMN_TYPE = 1ULL << 13;
+
+ /**
+ Change column datatype in such way that new type has compatible
+ packed representation with old type, so it is theoretically
+ possible to perform change by only updating data dictionary
+ without changing table rows.
+ */
+ static const HA_ALTER_FLAGS ALTER_COLUMN_EQUAL_PACK_LENGTH = 1ULL << 14;
+
+ /// A virtual column has changed its position
+ static const HA_ALTER_FLAGS ALTER_VIRTUAL_COLUMN_ORDER = 1ULL << 15;
+
+ /// A stored column has changed its position (disregarding virtual columns)
+ static const HA_ALTER_FLAGS ALTER_STORED_COLUMN_ORDER = 1ULL << 16;
+
+ // Change column from NOT NULL to NULL
+ static const HA_ALTER_FLAGS ALTER_COLUMN_NULLABLE = 1ULL << 17;
+
+ // Change column from NULL to NOT NULL
+ static const HA_ALTER_FLAGS ALTER_COLUMN_NOT_NULLABLE = 1ULL << 18;
+
+ // Set or remove default column value
+ static const HA_ALTER_FLAGS ALTER_COLUMN_DEFAULT = 1ULL << 19;
+
+ // Change column generation expression
+ static const HA_ALTER_FLAGS ALTER_VIRTUAL_GCOL_EXPR = 1ULL << 20;
+ static const HA_ALTER_FLAGS ALTER_STORED_GCOL_EXPR = 1ULL << 21;
+
+ // Add foreign key
+ static const HA_ALTER_FLAGS ADD_FOREIGN_KEY = 1ULL << 22;
+
+ // Drop foreign key
+ static const HA_ALTER_FLAGS DROP_FOREIGN_KEY = 1ULL << 23;
+
+ // table_options changed, see HA_CREATE_INFO::used_fields for details.
+ static const HA_ALTER_FLAGS CHANGE_CREATE_OPTION = 1ULL << 24;
+
+ // Table is renamed
+ static const HA_ALTER_FLAGS ALTER_RENAME = 1ULL << 25;
+
+ // Change the storage type of column
+ static const HA_ALTER_FLAGS ALTER_COLUMN_STORAGE_TYPE = 1ULL << 26;
+
+ // Change the column format of column
+ static const HA_ALTER_FLAGS ALTER_COLUMN_COLUMN_FORMAT = 1ULL << 27;
+
+ // Add partition
+ static const HA_ALTER_FLAGS ADD_PARTITION = 1ULL << 28;
+
+ // Drop partition
+ static const HA_ALTER_FLAGS DROP_PARTITION = 1ULL << 29;
+
+ // Changing partition options
+ static const HA_ALTER_FLAGS ALTER_PARTITION = 1ULL << 30;
+
+ // Coalesce partition
+ static const HA_ALTER_FLAGS COALESCE_PARTITION = 1ULL << 31;
+
+ // Reorganize partition ... into
+ static const HA_ALTER_FLAGS REORGANIZE_PARTITION = 1ULL << 32;
+
+ // Reorganize partition
+ static const HA_ALTER_FLAGS ALTER_TABLE_REORG = 1ULL << 33;
+
+ // Remove partitioning
+ static const HA_ALTER_FLAGS ALTER_REMOVE_PARTITIONING = 1ULL << 34;
+
+ // Partition operation with ALL keyword
+ static const HA_ALTER_FLAGS ALTER_ALL_PARTITION = 1ULL << 35;
+
+ /**
+ Rename index. Note that we set this flag only if there are no other
+ changes to the index being renamed. Also for simplicity we don't
+ detect renaming of indexes which is done by dropping index and then
+ re-creating index with identical definition under different name.
+ */
+ static const HA_ALTER_FLAGS RENAME_INDEX = 1ULL << 36;
+
+ /**
+ Recreate the table for ALTER TABLE FORCE, ALTER TABLE ENGINE
+ and OPTIMIZE TABLE operations.
+ */
+ static const HA_ALTER_FLAGS RECREATE_TABLE = 1ULL << 37;
+
+ // Add spatial index
+ static const HA_ALTER_FLAGS ADD_SPATIAL_INDEX = 1ULL << 38;
+
+ // Alter index comment
+ static const HA_ALTER_FLAGS ALTER_INDEX_COMMENT = 1ULL << 39;
+
+ // New/changed virtual generated column require validation
+ static const HA_ALTER_FLAGS VALIDATE_VIRTUAL_COLUMN = 1ULL << 40;
+
+ /**
+ Change index option in a way which is likely not to require index
+ recreation. For example, change COMMENT or KEY::is_algorithm_explicit
+ flag (without change of index algorithm itself).
+ */
+ static const HA_ALTER_FLAGS CHANGE_INDEX_OPTION = 1LL << 41;
+
+ // Rebuild partition
+ static const HA_ALTER_FLAGS ALTER_REBUILD_PARTITION = 1ULL << 42;
+
+ /**
+ Change in index length such that it does not require index rebuild.
+ For example, change in index length due to column expansion like
+ varchar(X) changed to varchar(X + N).
+ */
+ static const HA_ALTER_FLAGS ALTER_COLUMN_INDEX_LENGTH = 1ULL << 43;
+
+ /**
+ Change to one of columns on which virtual generated column depends,
+ so its values require re-evaluation.
+ */
+ static const HA_ALTER_FLAGS VIRTUAL_GCOL_REEVAL = 1ULL << 44;
+
+ /**
+ Change to one of columns on which stored generated column depends,
+ so its values require re-evaluation.
+ */
+ static const HA_ALTER_FLAGS STORED_GCOL_REEVAL = 1ULL << 45;
+
+ // Add check constraint.
+ static const HA_ALTER_FLAGS ADD_CHECK_CONSTRAINT = 1ULL << 46;
+
+ // Drop check constraint.
+ static const HA_ALTER_FLAGS DROP_CHECK_CONSTRAINT = 1ULL << 47;
+
+ // Suspend check constraint.
+ static const HA_ALTER_FLAGS SUSPEND_CHECK_CONSTRAINT = 1ULL << 48;
+
+ /**
+ Create options (like MAX_ROWS) for the new version of table.
+
+ @note The referenced instance of HA_CREATE_INFO object was already
+ used to create new .FRM file for table being altered. So it
+ has been processed by mysql_prepare_create_table() already.
+ For example, this means that it has HA_OPTION_PACK_RECORD
+ flag in HA_CREATE_INFO::table_options member correctly set.
+ */
+ HA_CREATE_INFO *create_info;
+
+ /**
+ Alter options, fields and keys for the new version of table.
+
+ @note The referenced instance of Alter_info object was already
+ used to create new .FRM file for table being altered. So it
+ has been processed by mysql_prepare_create_table() already.
+ In particular, this means that in Create_field objects for
+ fields which were present in some form in the old version
+ of table, Create_field::field member points to corresponding
+ Field instance for old version of table.
+ */
+ Alter_info *alter_info;
+
+ /**
+ Indicates whether operation should fail if table is non-empty.
+ Storage engines should not suggest/allow execution of such operations
+ using INSTANT algorithm since check whether table is empty done from
+ SQL-layer is not "instant". Also SEs might choose different algorithm for
+ ALTER TABLE execution knowing that it will be allowed to proceed only if
+ table is empty.
+
+ Unlike for Alter_table_ctx::error_if_not_empty, we use bool for this flag
+ and not bitmap, since SEs are really interested in the fact that ALTER
+ will fail if table is not empty and not in exact reason behind this fact,
+ and because we want to avoid extra dependency between Alter_table_ctx and
+ Alter_inplace_info.
+ */
+ bool error_if_not_empty;
+
+ /**
+ Array of KEYs for new version of table - including KEYs to be added.
+
+ @note Currently this array is produced as result of
+ mysql_prepare_create_table() call.
+ This means that it follows different convention for
+ KEY_PART_INFO::fieldnr values than objects in TABLE::key_info
+ array.
+
+ @todo This is mainly due to the fact that we need to keep compatibility
+ with removed handler::add_index() call. We plan to switch to
+ TABLE::key_info numbering later.
+
+ KEYs are sorted - see sort_keys().
+ */
+ KEY *key_info_buffer;
+
+ /** Size of key_info_buffer array. */
+ uint key_count;
+
+ /** Size of index_drop_buffer array. */
+ uint index_drop_count;
+
+ /**
+ Array of pointers to KEYs to be dropped belonging to the TABLE instance
+ for the old version of the table.
+ */
+ KEY **index_drop_buffer;
+
+ /** Size of index_add_buffer array. */
+ uint index_add_count;
+
+ /**
+ Array of indexes into key_info_buffer for KEYs to be added,
+ sorted in increasing order.
+ */
+ uint *index_add_buffer;
+
+ /** Size of index_rename_buffer array. */
+ uint index_rename_count;
+
+ /** Size of index_rename_buffer array. */
+ uint index_altered_visibility_count;
+
+ /**
+ Array of KEY_PAIR objects describing indexes being renamed.
+ For each index renamed it contains object with KEY_PAIR::old_key
+ pointing to KEY object belonging to the TABLE instance for old
+ version of table representing old version of index and with
+ KEY_PAIR::new_key pointing to KEY object for new version of
+ index in key_info_buffer member.
+ */
+ KEY_PAIR *index_rename_buffer;
+ KEY_PAIR *index_altered_visibility_buffer;
+
+ /** Number of virtual columns to be added. */
+ uint virtual_column_add_count;
+
+ /** number of virtual columns to be dropped. */
+ uint virtual_column_drop_count;
+
+ /**
+ Context information to allow handlers to keep context between in-place
+ alter API calls.
+
+ @see inplace_alter_handler_ctx for information about object lifecycle.
+ */
+ inplace_alter_handler_ctx *handler_ctx;
+
+ /**
+ If the table uses several handlers, like ha_partition uses one handler
+ per partition, this contains a Null terminated array of ctx pointers
+ that should all be committed together.
+ Or NULL if only handler_ctx should be committed.
+ Set to NULL if the low level handler::commit_inplace_alter_table uses it,
+ to signal to the main handler that everything was committed as atomically.
+
+ @see inplace_alter_handler_ctx for information about object lifecycle.
+ */
+ inplace_alter_handler_ctx **group_commit_ctx;
+
+ /**
+ Flags describing in detail which operations the storage engine is to
+ execute.
+ */
+ HA_ALTER_FLAGS handler_flags;
+
+ /**
+ Partition_info taking into account the partition changes to be performed.
+ Contains all partitions which are present in the old version of the table
+ with partitions to be dropped or changed marked as such + all partitions
+ to be added in the new version of table marked as such.
+ */
+ partition_info *modified_part_info;
+
+ /** true for online operation (LOCK=NONE) */
+ bool online;
+
+ /**
+ Can be set by handler along with handler_ctx. The difference is that
+ this flag can be used to store SE-specific in-place ALTER context in cases
+ when constructing full-blown inplace_alter_handler_ctx descendant is
+ inconvenient.
+ */
+ uint handler_trivial_ctx;
+
+ /**
+ Can be set by handler to describe why a given operation cannot be done
+ in-place (HA_ALTER_INPLACE_NOT_SUPPORTED) or why it cannot be done
+ online (HA_ALTER_INPLACE_NO_LOCK or HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE)
+ If set, it will be used with ER_ALTER_OPERATION_NOT_SUPPORTED_REASON if
+ results from handler::check_if_supported_inplace_alter() doesn't match
+ requirements set by user. If not set, the more generic
+ ER_ALTER_OPERATION_NOT_SUPPORTED will be used.
+
+ Please set to a properly localized string, for example using
+ my_get_err_msg(), so that the error message as a whole is localized.
+ */
+ const char *unsupported_reason;
+
+ Alter_inplace_info(HA_CREATE_INFO *create_info_arg,
+ Alter_info *alter_info_arg, bool error_if_not_empty_arg,
+ KEY *key_info_arg, uint key_count_arg,
+ partition_info *modified_part_info_arg)
+ : create_info(create_info_arg),
+ alter_info(alter_info_arg),
+ error_if_not_empty(error_if_not_empty_arg),
+ key_info_buffer(key_info_arg),
+ key_count(key_count_arg),
+ index_drop_count(0),
+ index_drop_buffer(NULL),
+ index_add_count(0),
+ index_add_buffer(NULL),
+ index_rename_count(0),
+ index_altered_visibility_count(0),
+ index_rename_buffer(NULL),
+ virtual_column_add_count(0),
+ virtual_column_drop_count(0),
+ handler_ctx(NULL),
+ group_commit_ctx(NULL),
+ handler_flags(0),
+ modified_part_info(modified_part_info_arg),
+ online(false),
+ handler_trivial_ctx(0),
+ unsupported_reason(NULL) {}
+
+ ~Alter_inplace_info() { destroy(handler_ctx); }
+
+ /**
+ Used after check_if_supported_inplace_alter() to report
+ error if the result does not match the LOCK/ALGORITHM
+ requirements set by the user.
+
+ @param not_supported Part of statement that was not supported.
+ @param try_instead Suggestion as to what the user should
+ replace not_supported with.
+ */
+ void report_unsupported_error(const char *not_supported,
+ const char *try_instead);
+
+ /** Add old and new version of key to array of indexes to be renamed. */
+ void add_renamed_key(KEY *old_key, KEY *new_key) {
+ KEY_PAIR *key_pair = index_rename_buffer + index_rename_count++;
+ key_pair->old_key = old_key;
+ key_pair->new_key = new_key;
+ DBUG_PRINT("info",
+ ("index renamed: '%s' to '%s'", old_key->name, new_key->name));
+ }
+
+ void add_altered_index_visibility(KEY *old_key, KEY *new_key) {
+ KEY_PAIR *key_pair =
+ index_altered_visibility_buffer + index_altered_visibility_count++;
+ key_pair->old_key = old_key;
+ key_pair->new_key = new_key;
+ DBUG_PRINT("info", ("index had visibility altered: %i to %i",
+ old_key->is_visible, new_key->is_visible));
+ }
+
+ /**
+ Add old and new version of modified key to arrays of indexes to
+ be dropped and added (correspondingly).
+ */
+ void add_modified_key(KEY *old_key, KEY *new_key) {
+ index_drop_buffer[index_drop_count++] = old_key;
+ index_add_buffer[index_add_count++] = (uint)(new_key - key_info_buffer);
+ DBUG_PRINT("info", ("index changed: '%s'", old_key->name));
+ }
+
+ /** Drop key to array of indexes to be dropped. */
+ void add_dropped_key(KEY *old_key) {
+ index_drop_buffer[index_drop_count++] = old_key;
+ DBUG_PRINT("info", ("index dropped: '%s'", old_key->name));
+ }
+
+ /** Add key to array of indexes to be added. */
+ void add_added_key(KEY *new_key) {
+ index_add_buffer[index_add_count++] = (uint)(new_key - key_info_buffer);
+ DBUG_PRINT("info", ("index added: '%s'", new_key->name));
+ }
+};
+
+struct HA_CHECK_OPT {
+ HA_CHECK_OPT() {} /* Remove gcc warning */
+ uint flags; /* isam layer flags (e.g. for myisamchk) */
+ uint sql_flags; /* sql layer flags - for something myisamchk cannot do */
+ KEY_CACHE *key_cache; /* new key cache when changing key cache */
+ void init();
+};
+
+/*
+ This is a buffer area that the handler can use to store rows.
+ 'end_of_used_area' should be kept updated after calls to
+ read-functions so that other parts of the code can use the
+ remaining area (until next read calls is issued).
+*/
+
+struct HANDLER_BUFFER {
+ uchar *buffer; /* Buffer one can start using */
+ uchar *buffer_end; /* End of buffer */
+ uchar *end_of_used_area; /* End of area that was used by handler */
+};
+
+typedef void *range_seq_t;
+
+struct RANGE_SEQ_IF {
+ /*
+ Initialize the traversal of range sequence
+
+ SYNOPSIS
+ init()
+ init_params The seq_init_param parameter
+ n_ranges The number of ranges obtained
+ flags A combination of HA_MRR_SINGLE_POINT, HA_MRR_FIXED_KEY
+
+ RETURN
+ An opaque value to be used as RANGE_SEQ_IF::next() parameter
+ */
+ range_seq_t (*init)(void *init_params, uint n_ranges, uint flags);
+
+ /*
+ Get the next range in the range sequence
+
+ SYNOPSIS
+ next()
+ seq The value returned by RANGE_SEQ_IF::init()
+ range OUT Information about the next range
+
+ RETURN
+ 0 - Ok, the range structure filled with info about the next range
+ 1 - No more ranges
+ */
+ uint (*next)(range_seq_t seq, KEY_MULTI_RANGE *range);
+
+ /*
+ Check whether range_info orders to skip the next record
+
+ SYNOPSIS
+ skip_record()
+ seq The value returned by RANGE_SEQ_IF::init()
+ range_info Information about the next range
+ (Ignored if MRR_NO_ASSOCIATION is set)
+ rowid Rowid of the record to be checked (ignored if set to 0)
+
+ RETURN
+ 1 - Record with this range_info and/or this rowid shall be filtered
+ out from the stream of records returned by ha_multi_range_read_next()
+ 0 - The record shall be left in the stream
+ */
+ bool (*skip_record)(range_seq_t seq, char *range_info, uchar *rowid);
+
+ /*
+ Check if the record combination matches the index condition
+ SYNOPSIS
+ skip_index_tuple()
+ seq The value returned by RANGE_SEQ_IF::init()
+ range_info Information about the next range
+
+ RETURN
+ 0 - The record combination satisfies the index condition
+ 1 - Otherwise
+ */
+ bool (*skip_index_tuple)(range_seq_t seq, char *range_info);
+};
+
+/**
+ Used to store optimizer cost estimates.
+
+ The class consists of PODs only: default operator=, copy constructor
+ and destructor are used.
+ */
+class Cost_estimate {
+ private:
+ double io_cost; ///< cost of I/O operations
+ double cpu_cost; ///< cost of CPU operations
+ double import_cost; ///< cost of remote operations
+ double mem_cost; ///< memory used (bytes)
+
+ public:
+ Cost_estimate() : io_cost(0), cpu_cost(0), import_cost(0), mem_cost(0) {}
+
+ /// Returns sum of time-consuming costs, i.e., not counting memory cost
+ double total_cost() const { return io_cost + cpu_cost + import_cost; }
+ double get_io_cost() const { return io_cost; }
+ double get_cpu_cost() const { return cpu_cost; }
+ double get_import_cost() const { return import_cost; }
+ double get_mem_cost() const { return mem_cost; }
+
+ /**
+ Whether or not all costs in the object are zero
+
+ @return true if all costs are zero, false otherwise
+ */
+ bool is_zero() const {
+ return !(io_cost || cpu_cost || import_cost || mem_cost);
+ }
+ /**
+ Whether or not the total cost is the maximal double
+
+ @return true if total cost is the maximal double, false otherwise
+ */
+ bool is_max_cost() const { return io_cost == DBL_MAX; }
+ /// Reset all costs to zero
+ void reset() { io_cost = cpu_cost = import_cost = mem_cost = 0; }
+ /// Set current cost to the maximal double
+ void set_max_cost() {
+ reset();
+ io_cost = DBL_MAX;
+ }
+
+ /// Multiply io, cpu and import costs by parameter
+ void multiply(double m) {
+ DBUG_ASSERT(!is_max_cost());
+
+ io_cost *= m;
+ cpu_cost *= m;
+ import_cost *= m;
+ /* Don't multiply mem_cost */
+ }
+
+ Cost_estimate &operator+=(const Cost_estimate &other) {
+ DBUG_ASSERT(!is_max_cost() && !other.is_max_cost());
+
+ io_cost += other.io_cost;
+ cpu_cost += other.cpu_cost;
+ import_cost += other.import_cost;
+ mem_cost += other.mem_cost;
+
+ return *this;
+ }
+
+ Cost_estimate operator+(const Cost_estimate &other) {
+ Cost_estimate result = *this;
+ result += other;
+
+ return result;
+ }
+
+ Cost_estimate operator-(const Cost_estimate &other) {
+ Cost_estimate result;
+
+ DBUG_ASSERT(!other.is_max_cost());
+
+ result.io_cost = io_cost - other.io_cost;
+ result.cpu_cost = cpu_cost - other.cpu_cost;
+ result.import_cost = import_cost - other.import_cost;
+ result.mem_cost = mem_cost - other.mem_cost;
+ return result;
+ }
+
+ bool operator>(const Cost_estimate &other) const {
+ return total_cost() > other.total_cost() ? true : false;
+ }
+
+ bool operator<(const Cost_estimate &other) const {
+ return other > *this ? true : false;
+ }
+
+ /// Add to IO cost
+ void add_io(double add_io_cost) {
+ DBUG_ASSERT(!is_max_cost());
+ io_cost += add_io_cost;
+ }
+
+ /// Add to CPU cost
+ void add_cpu(double add_cpu_cost) {
+ DBUG_ASSERT(!is_max_cost());
+ cpu_cost += add_cpu_cost;
+ }
+
+ /// Add to import cost
+ void add_import(double add_import_cost) {
+ DBUG_ASSERT(!is_max_cost());
+ import_cost += add_import_cost;
+ }
+
+ /// Add to memory cost
+ void add_mem(double add_mem_cost) {
+ DBUG_ASSERT(!is_max_cost());
+ mem_cost += add_mem_cost;
+ }
+};
+
+void get_sweep_read_cost(TABLE *table, ha_rows nrows, bool interrupted,
+ Cost_estimate *cost);
+
+/*
+ The below two are not used (and not handled) in this milestone of this WL
+ entry because there seems to be no use for them at this stage of
+ implementation.
+*/
+#define HA_MRR_SINGLE_POINT 1
+#define HA_MRR_FIXED_KEY 2
+
+/*
+ Indicates that RANGE_SEQ_IF::next(&range) doesn't need to fill in the
+ 'range' parameter.
+*/
+#define HA_MRR_NO_ASSOCIATION 4
+
+/*
+ The MRR user will provide ranges in key order, and MRR implementation
+ must return rows in key order.
+ Passing this flag to multi_read_range_init() may cause the
+ default MRR handler to be used even if HA_MRR_USE_DEFAULT_IMPL
+ was not specified.
+ (If the native MRR impl. can not provide SORTED result)
+*/
+#define HA_MRR_SORTED 8
+
+/* MRR implementation doesn't have to retrieve full records */
+#define HA_MRR_INDEX_ONLY 16
+
+/*
+ The passed memory buffer is of maximum possible size, the caller can't
+ assume larger buffer.
+*/
+#define HA_MRR_LIMITS 32
+
+/*
+ Flag set <=> default MRR implementation is used
+ (The choice is made by **_info[_const]() function which may set this
+ flag. SQL layer remembers the flag value and then passes it to
+ multi_read_range_init().
+*/
+#define HA_MRR_USE_DEFAULT_IMPL 64
+
+/*
+ Used only as parameter to multi_range_read_info():
+ Flag set <=> the caller guarantees that the bounds of the scanned ranges
+ will not have NULL values.
+*/
+#define HA_MRR_NO_NULL_ENDPOINTS 128
+
+/*
+ Set by the MRR implementation to signal that it will natively
+ produced sorted result if multi_range_read_init() is called with
+ the HA_MRR_SORTED flag - Else multi_range_read_init(HA_MRR_SORTED)
+ will revert to use the default MRR implementation.
+*/
+#define HA_MRR_SUPPORT_SORTED 256
+
+class ha_statistics {
+ public:
+ ulonglong data_file_length; /* Length off data file */
+ ulonglong max_data_file_length; /* Length off data file */
+ ulonglong index_file_length;
+ ulonglong max_index_file_length;
+ ulonglong delete_length; /* Free bytes */
+ ulonglong auto_increment_value;
+ /*
+ The number of records in the table.
+ 0 - means the table has exactly 0 rows
+ other - if (table_flags() & HA_STATS_RECORDS_IS_EXACT)
+ the value is the exact number of records in the table
+ else
+ it is an estimate
+ */
+ ha_rows records;
+ ha_rows deleted; /* Deleted records */
+ ulong mean_rec_length; /* physical reclength */
+ /* TODO: create_time should be retrieved from the new DD. Remove this. */
+ time_t create_time; /* When table was created */
+ ulong check_time;
+ ulong update_time;
+ uint block_size; /* index block size */
+
+ /*
+ number of buffer bytes that native mrr implementation needs,
+ */
+ uint mrr_length_per_rec;
+
+ /**
+ Estimate for how much of the table that is availabe in a memory
+ buffer. Valid range is [0..1]. If it has the special value
+ IN_MEMORY_ESTIMATE_UNKNOWN (defined in structs.h), it means that
+ the storage engine has not supplied any value for it.
+ */
+ double table_in_mem_estimate;
+
+ ha_statistics()
+ : data_file_length(0),
+ max_data_file_length(0),
+ index_file_length(0),
+ delete_length(0),
+ auto_increment_value(0),
+ records(0),
+ deleted(0),
+ mean_rec_length(0),
+ create_time(0),
+ check_time(0),
+ update_time(0),
+ block_size(0),
+ table_in_mem_estimate(IN_MEMORY_ESTIMATE_UNKNOWN) {}
+};
+
+/**
+ Calculates length of key.
+
+ Given a key index and a map of key parts return length of buffer used by key
+ parts.
+
+ @param table Table containing the key
+ @param key Key index
+ @param keypart_map which key parts that is used
+
+ @return Length of used key parts.
+*/
+uint calculate_key_len(TABLE *table, uint key, key_part_map keypart_map);
+/*
+ bitmap with first N+1 bits set
+ (keypart_map for a key prefix of [0..N] keyparts)
+*/
+#define make_keypart_map(N) (((key_part_map)2 << (N)) - 1)
+/*
+ bitmap with first N bits set
+ (keypart_map for a key prefix of [0..N-1] keyparts)
+*/
+#define make_prev_keypart_map(N) (((key_part_map)1 << (N)) - 1)
+
+/** Base class to be used by handlers different shares */
+class Handler_share {
+ public:
+ Handler_share() {}
+ virtual ~Handler_share() {}
+};
+
+/**
+ Wrapper for struct ft_hints.
+*/
+
+class Ft_hints {
+ private:
+ struct ft_hints hints;
+
+ public:
+ Ft_hints(uint ft_flags) {
+ hints.flags = ft_flags;
+ hints.op_type = FT_OP_UNDEFINED;
+ hints.op_value = 0.0;
+ hints.limit = HA_POS_ERROR;
+ }
+
+ /**
+ Set comparison operation type and and value for master MATCH function.
+
+ @param type comparison operation type
+ @param value comparison operation value
+ */
+ void set_hint_op(enum ft_operation type, double value) {
+ hints.op_type = type;
+ hints.op_value = value;
+ }
+
+ /**
+ Set Ft_hints flag.
+
+ @param ft_flag Ft_hints flag
+ */
+ void set_hint_flag(uint ft_flag) { hints.flags |= ft_flag; }
+
+ /**
+ Set Ft_hints limit.
+
+ @param ft_limit limit
+ */
+ void set_hint_limit(ha_rows ft_limit) { hints.limit = ft_limit; }
+
+ /**
+ Get Ft_hints limit.
+
+ @return Ft_hints limit
+ */
+ ha_rows get_limit() { return hints.limit; }
+
+ /**
+ Get Ft_hints operation value.
+
+ @return operation value
+ */
+ double get_op_value() { return hints.op_value; }
+
+ /**
+ Get Ft_hints operation type.
+
+ @return operation type
+ */
+ enum ft_operation get_op_type() { return hints.op_type; }
+
+ /**
+ Get Ft_hints flags.
+
+ @return Ft_hints flags
+ */
+ uint get_flags() { return hints.flags; }
+
+ /**
+ Get ft_hints struct.
+
+ @return pointer to ft_hints struct
+ */
+ struct ft_hints *get_hints() {
+ return &hints;
+ }
+};
+
+/**
+ The handler class is the interface for dynamically loadable
+ storage engines. Do not add ifdefs and take care when adding or
+ changing virtual functions to avoid vtable confusion
+
+ Functions in this class accept and return table columns data. Two data
+ representation formats are used:
+ 1. TableRecordFormat - Used to pass [partial] table records to/from
+ storage engine
+
+ 2. KeyTupleFormat - used to pass index search tuples (aka "keys") to
+ storage engine. See opt_range.cc for description of this format.
+
+ TableRecordFormat
+ =================
+ [Warning: this description is work in progress and may be incomplete]
+ The table record is stored in a fixed-size buffer:
+
+ record: null_bytes, column1_data, column2_data, ...
+
+ The offsets of the parts of the buffer are also fixed: every column has
+ an offset to its column{i}_data, and if it is nullable it also has its own
+ bit in null_bytes.
+
+ The record buffer only includes data about columns that are marked in the
+ relevant column set (table->read_set and/or table->write_set, depending on
+ the situation).
+ <not-sure>It could be that it is required that null bits of non-present
+ columns are set to 1</not-sure>
+
+ VARIOUS EXCEPTIONS AND SPECIAL CASES
+
+ If the table has no nullable columns, then null_bytes is still
+ present, its length is one byte <not-sure> which must be set to 0xFF
+ at all times. </not-sure>
+
+ If the table has columns of type BIT, then certain bits from those columns
+ may be stored in null_bytes as well. Grep around for Field_bit for
+ details.
+
+ For blob columns (see Field_blob), the record buffer stores length of the
+ data, following by memory pointer to the blob data. The pointer is owned
+ by the storage engine and is valid until the next operation.
+
+ If a blob column has NULL value, then its length and blob data pointer
+ must be set to 0.
+
+
+ Overview of main modules of the handler API
+ ===========================================
+ The overview below was copied from the storage/partition/ha_partition.h when
+ support for non-native partitioning was removed.
+
+ -------------------------------------------------------------------------
+ MODULE create/delete handler object
+ -------------------------------------------------------------------------
+ Object create/delete method. Normally called when a table object
+ exists.
+
+ -------------------------------------------------------------------------
+ MODULE meta data changes
+ -------------------------------------------------------------------------
+ Meta data routines to CREATE, DROP, RENAME table are often used at
+ ALTER TABLE (update_create_info used from ALTER TABLE and SHOW ..).
+
+ Methods:
+ delete_table()
+ rename_table()
+ create()
+ update_create_info()
+
+ -------------------------------------------------------------------------
+ MODULE open/close object
+ -------------------------------------------------------------------------
+ Open and close handler object to ensure all underlying files and
+ objects allocated and deallocated for query handling is handled
+ properly.
+
+ A handler object is opened as part of its initialisation and before
+ being used for normal queries (not before meta-data changes always.
+ If the object was opened it will also be closed before being deleted.
+
+ Methods:
+ open()
+ close()
+
+ -------------------------------------------------------------------------
+ MODULE start/end statement
+ -------------------------------------------------------------------------
+ This module contains methods that are used to understand start/end of
+ statements, transaction boundaries, and aid for proper concurrency
+ control.
+
+ Methods:
+ store_lock()
+ external_lock()
+ start_stmt()
+ lock_count()
+ unlock_row()
+ was_semi_consistent_read()
+ try_semi_consistent_read()
+
+ -------------------------------------------------------------------------
+ MODULE change record
+ -------------------------------------------------------------------------
+ This part of the handler interface is used to change the records
+ after INSERT, DELETE, UPDATE, REPLACE method calls but also other
+ special meta-data operations as ALTER TABLE, LOAD DATA, TRUNCATE.
+
+ These methods are used for insert (write_row), update (update_row)
+ and delete (delete_row). All methods to change data always work on
+ one row at a time. update_row and delete_row also contains the old
+ row.
+ delete_all_rows will delete all rows in the table in one call as a
+ special optimization for DELETE from table;
+
+ Bulk inserts are supported if all underlying handlers support it.
+ start_bulk_insert and end_bulk_insert is called before and after a
+ number of calls to write_row.
+
+ Methods:
+ write_row()
+ update_row()
+ delete_row()
+ delete_all_rows()
+ start_bulk_insert()
+ end_bulk_insert()
+
+ -------------------------------------------------------------------------
+ MODULE full table scan
+ -------------------------------------------------------------------------
+ This module is used for the most basic access method for any table
+ handler. This is to fetch all data through a full table scan. No
+ indexes are needed to implement this part.
+ It contains one method to start the scan (rnd_init) that can also be
+ called multiple times (typical in a nested loop join). Then proceeding
+ to the next record (rnd_next) and closing the scan (rnd_end).
+ To remember a record for later access there is a method (position)
+ and there is a method used to retrieve the record based on the stored
+ position.
+ The position can be a file position, a primary key, a ROWID dependent
+ on the handler below.
+
+ All functions that retrieve records and are callable through the
+ handler interface must indicate whether a record is present after the call
+ or not. Record found is indicated by returning 0 and setting table status
+ to "has row". Record not found is indicated by returning a non-zero value
+ and setting table status to "no row".
+ @see TABLE::set_found_row() and TABLE::set_no_row().
+ By enforcing these rules in the handler interface, storage handler functions
+ need not set any status in struct TABLE. These notes also apply to module
+ index scan, documented below.
+
+ Methods:
+
+ rnd_init()
+ rnd_end()
+ rnd_next()
+ rnd_pos()
+ rnd_pos_by_record()
+ position()
+
+ -------------------------------------------------------------------------
+ MODULE index scan
+ -------------------------------------------------------------------------
+ This part of the handler interface is used to perform access through
+ indexes. The interface is defined as a scan interface but the handler
+ can also use key lookup if the index is a unique index or a primary
+ key index.
+ Index scans are mostly useful for SELECT queries but are an important
+ part also of UPDATE, DELETE, REPLACE and CREATE TABLE table AS SELECT
+ and so forth.
+ Naturally an index is needed for an index scan and indexes can either
+ be ordered, hash based. Some ordered indexes can return data in order
+ but not necessarily all of them.
+ There are many flags that define the behavior of indexes in the
+ various handlers. These methods are found in the optimizer module.
+
+ index_read is called to start a scan of an index. The find_flag defines
+ the semantics of the scan. These flags are defined in
+ include/my_base.h
+ index_read_idx is the same but also initializes index before calling doing
+ the same thing as index_read. Thus it is similar to index_init followed
+ by index_read. This is also how we implement it.
+
+ index_read/index_read_idx does also return the first row. Thus for
+ key lookups, the index_read will be the only call to the handler in
+ the index scan.
+
+ index_init initializes an index before using it and index_end does
+ any end processing needed.
+
+ Methods:
+ index_read_map()
+ index_init()
+ index_end()
+ index_read_idx_map()
+ index_next()
+ index_prev()
+ index_first()
+ index_last()
+ index_next_same()
+ index_read_last_map()
+ read_range_first()
+ read_range_next()
+
+ -------------------------------------------------------------------------
+ MODULE information calls
+ -------------------------------------------------------------------------
+ This calls are used to inform the handler of specifics of the ongoing
+ scans and other actions. Most of these are used for optimisation
+ purposes.
+
+ Methods:
+ info()
+ get_dynamic_partition_info
+ extra()
+ extra_opt()
+ reset()
+
+ -------------------------------------------------------------------------
+ MODULE optimizer support
+ -------------------------------------------------------------------------
+ NOTE:
+ One important part of the public handler interface that is not depicted in
+ the methods is the attribute records which is defined in the base class.
+ This is looked upon directly and is set by calling info(HA_STATUS_INFO) ?
+
+ Methods:
+ min_rows_for_estimate()
+ get_biggest_used_partition()
+ scan_time()
+ read_time()
+ records_in_range()
+ estimate_rows_upper_bound()
+ records()
+
+ -------------------------------------------------------------------------
+ MODULE print messages
+ -------------------------------------------------------------------------
+ This module contains various methods that returns text messages for
+ table types, index type and error messages.
+
+ Methods:
+ table_type()
+ get_row_type()
+ print_error()
+ get_error_message()
+
+ -------------------------------------------------------------------------
+ MODULE handler characteristics
+ -------------------------------------------------------------------------
+ This module contains a number of methods defining limitations and
+ characteristics of the handler (see also documentation regarding the
+ individual flags).
+
+ Methods:
+ table_flags()
+ index_flags()
+ min_of_the_max_uint()
+ max_supported_record_length()
+ max_supported_keys()
+ max_supported_key_parts()
+ max_supported_key_length()
+ max_supported_key_part_length()
+ low_byte_first()
+ extra_rec_buf_length()
+ min_record_length(uint options)
+ primary_key_is_clustered()
+ ha_key_alg get_default_index_algorithm()
+ is_index_algorithm_supported()
+
+ -------------------------------------------------------------------------
+ MODULE compare records
+ -------------------------------------------------------------------------
+ cmp_ref checks if two references are the same. For most handlers this is
+ a simple memcmp of the reference. However some handlers use primary key
+ as reference and this can be the same even if memcmp says they are
+ different. This is due to character sets and end spaces and so forth.
+
+ Methods:
+ cmp_ref()
+
+ -------------------------------------------------------------------------
+ MODULE auto increment
+ -------------------------------------------------------------------------
+ This module is used to handle the support of auto increments.
+
+ This variable in the handler is used as part of the handler interface
+ It is maintained by the parent handler object and should not be
+ touched by child handler objects (see handler.cc for its use).
+
+ Methods:
+ get_auto_increment()
+ release_auto_increment()
+
+ -------------------------------------------------------------------------
+ MODULE initialize handler for HANDLER call
+ -------------------------------------------------------------------------
+ This method is a special InnoDB method called before a HANDLER query.
+
+ Methods:
+ init_table_handle_for_HANDLER()
+
+ -------------------------------------------------------------------------
+ MODULE foreign key support
+ -------------------------------------------------------------------------
+ The following methods are used to implement foreign keys as supported by
+ InnoDB and NDB.
+ get_foreign_key_create_info is used by SHOW CREATE TABLE to get a textual
+ description of how the CREATE TABLE part to define FOREIGN KEY's is done.
+ free_foreign_key_create_info is used to free the memory area that provided
+ this description.
+
+ Methods:
+ get_parent_foreign_key_list()
+ get_foreign_key_create_info()
+ free_foreign_key_create_info()
+ get_foreign_key_list()
+ referenced_by_foreign_key()
+
+ -------------------------------------------------------------------------
+ MODULE fulltext index
+ -------------------------------------------------------------------------
+ Fulltext index support.
+
+ Methods:
+ ft_init_ext_with_hints()
+ ft_init()
+ ft_init_ext()
+ ft_read()
+
+ -------------------------------------------------------------------------
+ MODULE in-place ALTER TABLE
+ -------------------------------------------------------------------------
+ Methods for in-place ALTER TABLE support (implemented by InnoDB and NDB).
+
+ Methods:
+ check_if_supported_inplace_alter()
+ prepare_inplace_alter_table()
+ inplace_alter_table()
+ commit_inplace_alter_table()
+ notify_table_changed()
+
+ -------------------------------------------------------------------------
+ MODULE tablespace support
+ -------------------------------------------------------------------------
+ Methods:
+ discard_or_import_tablespace()
+
+ -------------------------------------------------------------------------
+ MODULE administrative DDL
+ -------------------------------------------------------------------------
+ Methods:
+ optimize()
+ analyze()
+ check()
+ repair()
+ check_and_repair()
+ auto_repair()
+ is_crashed()
+ check_for_upgrade()
+ checksum()
+ assign_to_keycache()
+
+ -------------------------------------------------------------------------
+ MODULE enable/disable indexes
+ -------------------------------------------------------------------------
+ Enable/Disable Indexes are only supported by HEAP and MyISAM.
+
+ Methods:
+ disable_indexes()
+ enable_indexes()
+ indexes_are_disabled()
+
+ -------------------------------------------------------------------------
+ MODULE append_create_info
+ -------------------------------------------------------------------------
+ Only used by MyISAM MERGE tables.
+
+ Methods:
+ append_create_info()
+
+ -------------------------------------------------------------------------
+ MODULE partitioning specific handler API
+ -------------------------------------------------------------------------
+ Methods:
+ get_partition_handler()
+*/
+
+class handler {
+ friend class Partition_handler;
+
+ public:
+ typedef ulonglong Table_flags;
+
+ protected:
+ TABLE_SHARE *table_share; /* The table definition */
+ TABLE *table; /* The current open table */
+ Table_flags cached_table_flags; /* Set on init() and open() */
+
+ ha_rows estimation_rows_to_insert;
+
+ public:
+ handlerton *ht; /* storage engine of this handler */
+ /** Pointer to current row */
+ uchar *ref;
+ /** Pointer to duplicate row */
+ uchar *dup_ref;
+
+ ha_statistics stats;
+
+ /* MultiRangeRead-related members: */
+ range_seq_t mrr_iter; /* Interator to traverse the range sequence */
+ RANGE_SEQ_IF mrr_funcs; /* Range sequence traversal functions */
+ HANDLER_BUFFER *multi_range_buffer; /* MRR buffer info */
+ uint ranges_in_seq; /* Total number of ranges in the traversed sequence */
+ /* true <=> source MRR ranges and the output are ordered */
+ bool mrr_is_output_sorted;
+
+ /* true <=> we're currently traversing a range in mrr_cur_range. */
+ bool mrr_have_range;
+ /* Current range (the one we're now returning rows from) */
+ KEY_MULTI_RANGE mrr_cur_range;
+
+ /*
+ The direction of the current range or index scan. This is used by
+ the ICP implementation to determine if it has reached the end
+ of the current range.
+ */
+ enum enum_range_scan_direction { RANGE_SCAN_ASC, RANGE_SCAN_DESC };
+
+ private:
+ Record_buffer *m_record_buffer = nullptr; ///< Buffer for multi-row reads.
+ /*
+ Storage space for the end range value. Should only be accessed using
+ the end_range pointer. The content is invalid when end_range is NULL.
+ */
+ key_range save_end_range;
+ enum_range_scan_direction range_scan_direction;
+ int key_compare_result_on_equal;
+
+ /**
+ Pointer to the handler of the table in the primary storage engine,
+ if this handler represents a table in a secondary storage engine.
+ */
+ handler *m_primary_handler{nullptr};
+
+ protected:
+ KEY_PART_INFO *range_key_part;
+ bool eq_range;
+ /*
+ true <=> the engine guarantees that returned records are within the range
+ being scanned.
+ */
+ bool in_range_check_pushed_down;
+
+ public:
+ /**
+ End value for a range scan. If this is NULL the range scan has no
+ end value. Should also be NULL when there is no ongoing range scan.
+ Used by the read_range() functions and also evaluated by pushed
+ index conditions.
+ */
+ key_range *end_range;
+ /**
+ Flag which tells if #end_range contains a virtual generated column.
+ The content is invalid when #end_range is @c nullptr.
+ */
+ bool m_virt_gcol_in_end_range = false;
+ uint errkey; /* Last dup key */
+ uint key_used_on_scan;
+ uint active_index;
+ /** Length of ref (1-8 or the clustered key length) */
+ uint ref_length;
+ FT_INFO *ft_handler;
+ enum { NONE = 0, INDEX, RND, SAMPLING } inited;
+ bool implicit_emptied; /* Can be !=0 only if HEAP */
+ const Item *pushed_cond;
+
+ Item *pushed_idx_cond;
+ uint pushed_idx_cond_keyno; /* The index which the above condition is for */
+
+ /**
+ next_insert_id is the next value which should be inserted into the
+ auto_increment column: in a inserting-multi-row statement (like INSERT
+ SELECT), for the first row where the autoinc value is not specified by the
+ statement, get_auto_increment() called and asked to generate a value,
+ next_insert_id is set to the next value, then for all other rows
+ next_insert_id is used (and increased each time) without calling
+ get_auto_increment().
+ */
+ ulonglong next_insert_id;
+ /**
+ insert id for the current row (*autogenerated*; if not
+ autogenerated, it's 0).
+ At first successful insertion, this variable is stored into
+ THD::first_successful_insert_id_in_cur_stmt.
+ */
+ ulonglong insert_id_for_cur_row;
+ /**
+ Interval returned by get_auto_increment() and being consumed by the
+ inserter.
+ */
+ Discrete_interval auto_inc_interval_for_cur_row;
+ /**
+ Number of reserved auto-increment intervals. Serves as a heuristic
+ when we have no estimation of how many records the statement will insert:
+ the more intervals we have reserved, the bigger the next one. Reset in
+ handler::ha_release_auto_increment().
+ */
+ uint auto_inc_intervals_count;
+
+ /**
+ Instrumented table associated with this handler.
+ */
+ PSI_table *m_psi;
+
+ std::mt19937 m_random_number_engine;
+ double m_sampling_percentage;
+
+ private:
+ /** Internal state of the batch instrumentation. */
+ enum batch_mode_t {
+ /** Batch mode not used. */
+ PSI_BATCH_MODE_NONE,
+ /** Batch mode used, before first table io. */
+ PSI_BATCH_MODE_STARTING,
+ /** Batch mode used, after first table io. */
+ PSI_BATCH_MODE_STARTED
+ };
+ /**
+ Batch mode state.
+ @sa start_psi_batch_mode.
+ @sa end_psi_batch_mode.
+ */
+ batch_mode_t m_psi_batch_mode;
+ /**
+ The number of rows in the batch.
+ @sa start_psi_batch_mode.
+ @sa end_psi_batch_mode.
+ */
+ ulonglong m_psi_numrows;
+ /**
+ The current event in a batch.
+ @sa start_psi_batch_mode.
+ @sa end_psi_batch_mode.
+ */
+ PSI_table_locker *m_psi_locker;
+ /**
+ Storage for the event in a batch.
+ @sa start_psi_batch_mode.
+ @sa end_psi_batch_mode.
+ */
+ PSI_table_locker_state m_psi_locker_state;
+
+ public:
+ void unbind_psi();
+ void rebind_psi();
+ /**
+ Put the handler in 'batch' mode when collecting
+ table io instrumented events.
+ When operating in batch mode:
+ - a single start event is generated in the performance schema.
+ - all table io performed between @c start_psi_batch_mode
+ and @c end_psi_batch_mode is not instrumented:
+ the number of rows affected is counted instead in @c m_psi_numrows.
+ - a single end event is generated in the performance schema
+ when the batch mode ends with @c end_psi_batch_mode.
+ */
+ void start_psi_batch_mode();
+ /** End a batch started with @c start_psi_batch_mode. */
+ void end_psi_batch_mode();
+ /**
+ If a PSI batch was started, turn if off.
+ @returns true if it was started.
+ */
+ bool end_psi_batch_mode_if_started() {
+ bool rc = m_psi_batch_mode;
+ if (rc) end_psi_batch_mode();
+ return rc;
+ }
+
+ private:
+ /**
+ The lock type set by when calling::ha_external_lock(). This is
+ propagated down to the storage engine. The reason for also storing
+ it here, is that when doing MRR we need to create/clone a second handler
+ object. This cloned handler object needs to know about the lock_type used.
+ */
+ int m_lock_type;
+ /**
+ Pointer where to store/retrieve the Handler_share pointer.
+ For non partitioned handlers this is &TABLE_SHARE::ha_share.
+ */
+ Handler_share **ha_share;
+
+ /**
+ Some non-virtual ha_* functions, responsible for reading rows,
+ like ha_rnd_pos(), must ensure that virtual generated columns are
+ calculated before they return. For that, they should set this
+ member to true at their start, and check it before they return: if
+ the member is still true, it means they should calculate; if it's
+ false, it means the calculation has been done by some called
+ lower-level function and does not need to be re-done (which is why
+ we need this status flag: to avoid redundant calculations, for
+ performance).
+
+ Note that when updating generated fields, the NULL row status in
+ the underlying TABLE objects matter, so be sure to reset them if needed!
+ */
+ bool m_update_generated_read_fields;
+
+ /* Filter row ids to weed out duplicates when multi-valued index is used */
+ Unique_on_insert *m_unique;
+
+ public:
+ handler(handlerton *ht_arg, TABLE_SHARE *share_arg)
+ : table_share(share_arg),
+ table(0),
+ estimation_rows_to_insert(0),
+ ht(ht_arg),
+ ref(0),
+ range_scan_direction(RANGE_SCAN_ASC),
+ in_range_check_pushed_down(false),
+ end_range(NULL),
+ key_used_on_scan(MAX_KEY),
+ active_index(MAX_KEY),
+ ref_length(sizeof(my_off_t)),
+ ft_handler(0),
+ inited(NONE),
+ implicit_emptied(0),
+ pushed_cond(0),
+ pushed_idx_cond(NULL),
+ pushed_idx_cond_keyno(MAX_KEY),
+ next_insert_id(0),
+ insert_id_for_cur_row(0),
+ auto_inc_intervals_count(0),
+ m_psi(NULL),
+ m_psi_batch_mode(PSI_BATCH_MODE_NONE),
+ m_psi_numrows(0),
+ m_psi_locker(NULL),
+ m_lock_type(F_UNLCK),
+ ha_share(NULL),
+ m_update_generated_read_fields(false),
+ m_unique(nullptr) {
+ DBUG_PRINT("info", ("handler created F_UNLCK %d F_RDLCK %d F_WRLCK %d",
+ F_UNLCK, F_RDLCK, F_WRLCK));
+ }
+
+ virtual ~handler(void) {
+ DBUG_ASSERT(m_psi == NULL);
+ DBUG_ASSERT(m_psi_batch_mode == PSI_BATCH_MODE_NONE);
+ DBUG_ASSERT(m_psi_locker == NULL);
+ DBUG_ASSERT(m_lock_type == F_UNLCK);
+ DBUG_ASSERT(inited == NONE);
+ }
+
+ /*
+ @todo reorganize functions, make proper public/protected/private qualifiers
+ */
+ virtual handler *clone(const char *name, MEM_ROOT *mem_root);
+ /** This is called after create to allow us to set up cached variables */
+ void init() { cached_table_flags = table_flags(); }
+ /* ha_ methods: public wrappers for private virtual API */
+
+ /**
+ Set a record buffer that the storage engine can use for multi-row reads.
+ The buffer has to be provided prior to the first read from an index or a
+ table.
+
+ @param buffer the buffer to use for multi-row reads
+ */
+ void ha_set_record_buffer(Record_buffer *buffer) { m_record_buffer = buffer; }
+
+ /**
+ Get the record buffer that was set with ha_set_record_buffer().
+
+ @return the buffer to use for multi-row reads, or nullptr if there is none
+ */
+ Record_buffer *ha_get_record_buffer() const { return m_record_buffer; }
+
+ /**
+ Does this handler want to get a Record_buffer for multi-row reads
+ via the ha_set_record_buffer() function? And if so, what is the
+ maximum number of records to allocate space for in the buffer?
+
+ Storage engines that support using a Record_buffer should override
+ handler::is_record_buffer_wanted().
+
+ @param[out] max_rows gets set to the maximum number of records to
+ allocate space for in the buffer if the function
+ returns true
+
+ @retval true if the handler would like a Record_buffer
+ @retval false if the handler does not want a Record_buffer
+ */
+ bool ha_is_record_buffer_wanted(ha_rows *const max_rows) const {
+ return is_record_buffer_wanted(max_rows);
+ }
+
+ int ha_open(TABLE *table, const char *name, int mode, int test_if_locked,
+ const dd::Table *table_def);
+ int ha_close(void);
+ int ha_index_init(uint idx, bool sorted);
+ int ha_index_end();
+ int ha_rnd_init(bool scan);
+ int ha_rnd_end();
+ int ha_rnd_next(uchar *buf);
+ // See the comment on m_update_generated_read_fields.
+ int ha_rnd_pos(uchar *buf, uchar *pos);
+ int ha_index_read_map(uchar *buf, const uchar *key, key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+ int ha_index_read_last_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map);
+ int ha_index_read_idx_map(uchar *buf, uint index, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+ int ha_index_next(uchar *buf);
+ int ha_index_prev(uchar *buf);
+ int ha_index_first(uchar *buf);
+ int ha_index_last(uchar *buf);
+ int ha_index_next_same(uchar *buf, const uchar *key, uint keylen);
+ int ha_reset();
+ /* this is necessary in many places, e.g. in HANDLER command */
+ int ha_index_or_rnd_end() {
+ return inited == INDEX ? ha_index_end() : inited == RND ? ha_rnd_end() : 0;
+ }
+ /**
+ The cached_table_flags is set at ha_open and ha_external_lock
+ */
+ Table_flags ha_table_flags() const { return cached_table_flags; }
+ /**
+ These functions represent the public interface to *users* of the
+ handler class, hence they are *not* virtual. For the inheritance
+ interface, see the (private) functions write_row(), update_row(),
+ and delete_row() below.
+ */
+ int ha_external_lock(THD *thd, int lock_type);
+ int ha_write_row(uchar *buf);
+ /**
+ Update the current row.
+
+ @param old_data the old contents of the row
+ @param new_data the new contents of the row
+ @return error status (zero on success, HA_ERR_* error code on error)
+ */
+ int ha_update_row(const uchar *old_data, uchar *new_data);
+ int ha_delete_row(const uchar *buf);
+ void ha_release_auto_increment();
+
+ int ha_check_for_upgrade(HA_CHECK_OPT *check_opt);
+ /** to be actually called to get 'check()' functionality*/
+ int ha_check(THD *thd, HA_CHECK_OPT *check_opt);
+ int ha_repair(THD *thd, HA_CHECK_OPT *check_opt);
+ void ha_start_bulk_insert(ha_rows rows);
+ int ha_end_bulk_insert();
+ int ha_bulk_update_row(const uchar *old_data, uchar *new_data,
+ uint *dup_key_found);
+ int ha_delete_all_rows();
+ int ha_truncate(dd::Table *table_def);
+ int ha_optimize(THD *thd, HA_CHECK_OPT *check_opt);
+ int ha_analyze(THD *thd, HA_CHECK_OPT *check_opt);
+ bool ha_check_and_repair(THD *thd);
+ int ha_disable_indexes(uint mode);
+ int ha_enable_indexes(uint mode);
+ int ha_discard_or_import_tablespace(bool discard, dd::Table *table_def);
+ int ha_rename_table(const char *from, const char *to,
+ const dd::Table *from_table_def, dd::Table *to_table_def);
+ int ha_delete_table(const char *name, const dd::Table *table_def);
+ void ha_drop_table(const char *name);
+
+ int ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info,
+ dd::Table *table_def);
+
+ int ha_prepare_load_table(const TABLE &table);
+
+ int ha_load_table(const TABLE &table);
+
+ int ha_unload_table(const char *db_name, const char *table_name,
+ bool error_if_not_loaded);
+
+ /**
+ Initializes a parallel scan. It creates a parallel_scan_ctx that has to
+ be used across all parallel_scan methods. Also, gets the number of
+ threads that would be spawned for parallel scan.
+ @param[out] scan_ctx The parallel scan context.
+ @param[out] num_threads Number of threads used for the scan.
+ @return error code
+ @retval 0 on success
+ */
+ virtual int parallel_scan_init(void *&scan_ctx MY_ATTRIBUTE((unused)),
+ size_t &num_threads MY_ATTRIBUTE((unused))) {
+ return (0);
+ }
+
+ /**
+ This callback is called by each parallel load thread at the beginning of
+ the parallel load for the adapter scan.
+ @param cookie The cookie for this thread
+ @param ncols Number of columns in each row
+ @param row_len The size of a row in bytes
+ @param col_offsets An array of size ncols, where each element represents
+ the offset of a column in the row data. The memory of
+ this array belongs to the caller and will be free-ed
+ after the pload_end_cbk call.
+ @param null_byte_offsets An array of size ncols, where each element
+ represents the offset of a column in the row data. The
+ memory of this array belongs to the caller and will be
+ free-ed after the pload_end_cbk call.
+ @param null_bitmasks An array of size ncols, where each element
+ represents the bitmask required to get the null bit. The
+ memory of this array belongs to the caller and will be
+ free-ed after the pload_end_cbk call.
+ */
+ using Load_init_cbk = std::function<bool(
+ void *cookie, ulong ncols, ulong row_len, const ulong *col_offsets,
+ const ulong *null_byte_offsets, const ulong *null_bitmasks)>;
+
+ /**
+ This callback is called by each parallel load thread when processing
+ of rows is required for the adapter scan.
+ @param[in] cookie The cookie for this thread
+ @param[in] nrows The nrows that are available
+ @param[in] rowdata The mysql-in-memory row data buffer. This is a memory
+ buffer for nrows records. The length of each record
+ is fixed and communicated via Load_init_cbk
+ @returns true if there is an error, false otherwise.
+ */
+ using Load_cbk = std::function<bool(void *cookie, uint nrows, void *rowdata)>;
+
+ /**
+ This callback is called by each parallel load thread when processing
+ of rows has ended for the adapter scan.
+ @param[in] cookie The cookie for this thread
+ */
+ using Load_end_cbk = std::function<void(void *cookie)>;
+
+ /**
+ Run the parallel read of data.
+ @param[in] scan_ctx Scan context of the parallel read.
+ @param[in,out] thread_ctxs Caller thread contexts.
+ @param[in] init_fn Callback called by each parallel load
+ thread at the beginning of the parallel load.
+ @param[in] load_fn Callback called by each parallel load
+ thread when processing of rows is required.
+ @param[in] end_fn Callback called by each parallel load
+ thread when processing of rows has ended.
+ @return error code
+ @retval 0 on success
+ */
+ virtual int parallel_scan(void *scan_ctx MY_ATTRIBUTE((unused)),
+ void **thread_ctxs MY_ATTRIBUTE((unused)),
+ Load_init_cbk init_fn MY_ATTRIBUTE((unused)),
+ Load_cbk load_fn MY_ATTRIBUTE((unused)),
+ Load_end_cbk end_fn MY_ATTRIBUTE((unused))) {
+ return (0);
+ }
+
+ /**
+ End of the parallel scan.
+ @param[in] scan_ctx A scan context created by parallel_scan_init.
+ @return error code
+ @retval 0 on success
+ */
+ virtual int parallel_scan_end(void *scan_ctx MY_ATTRIBUTE((unused))) {
+ return (0);
+ }
+
+ /**
+ Submit a dd::Table object representing a core DD table having
+ hardcoded data to be filled in by the DDSE. This function can be
+ used for retrieving the hard coded SE private data for the
+ mysql.dd_properties table, before creating or opening it, or for
+ retrieving the hard coded SE private data for a core table,
+ before creating or opening them.
+
+ @param dd_table [in,out] A dd::Table object representing
+ a core DD table.
+ @param reset Reset counters.
+
+ @retval true An error occurred.
+ @retval false Success - no errors.
+ */
+
+ bool ha_get_se_private_data(dd::Table *dd_table, bool reset);
+
+ void adjust_next_insert_id_after_explicit_value(ulonglong nr);
+ int update_auto_increment();
+ virtual void print_error(int error, myf errflag);
+ virtual bool get_error_message(int error, String *buf);
+ uint get_dup_key(int error);
+ /**
+ Retrieves the names of the table and the key for which there was a
+ duplicate entry in the case of HA_ERR_FOREIGN_DUPLICATE_KEY.
+
+ If any of the table or key name is not available this method will return
+ false and will not change any of child_table_name or child_key_name.
+
+ @param [out] child_table_name Table name
+ @param [in] child_table_name_len Table name buffer size
+ @param [out] child_key_name Key name
+ @param [in] child_key_name_len Key name buffer size
+
+ @retval true table and key names were available
+ and were written into the corresponding
+ out parameters.
+ @retval false table and key names were not available,
+ the out parameters were not touched.
+ */
+ virtual bool get_foreign_dup_key(char *child_table_name,
+ uint child_table_name_len,
+ char *child_key_name,
+ uint child_key_name_len);
+ /**
+ Change the internal TABLE_SHARE pointer.
+
+ @param table_arg TABLE object
+ @param share New share to use
+
+ @note Is used in error handling in ha_delete_table.
+ */
+
+ virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share) {
+ table = table_arg;
+ table_share = share;
+ }
+ const TABLE_SHARE *get_table_share() const { return table_share; }
+
+ /* Estimates calculation */
+
+ /**
+ @deprecated This function is deprecated and will be removed in a future
+ version. Use table_scan_cost() instead.
+ */
+
+ virtual double scan_time() {
+ return ulonglong2double(stats.data_file_length) / IO_SIZE + 2;
+ }
+
+ /**
+ The cost of reading a set of ranges from the table using an index
+ to access it.
+
+ @deprecated This function is deprecated and will be removed in a future
+ version. Use read_cost() instead.
+
+ @param index The index number.
+ @param ranges The number of ranges to be read.
+ @param rows Total number of rows to be read.
+
+ This method can be used to calculate the total cost of scanning a table
+ using an index by calling it using read_time(index, 1, table_size).
+ */
+
+ virtual double read_time(uint index MY_ATTRIBUTE((unused)), uint ranges,
+ ha_rows rows) {
+ return rows2double(ranges + rows);
+ }
+
+ /**
+ @deprecated This function is deprecated and will be removed in a future
+ version. Use index_scan_cost() instead.
+ */
+
+ virtual double index_only_read_time(uint keynr, double records);
+
+ /**
+ Cost estimate for doing a complete table scan.
+
+ @note For this version it is recommended that storage engines continue
+ to override scan_time() instead of this function.
+
+ @returns the estimated cost
+ */
+
+ virtual Cost_estimate table_scan_cost();
+
+ /**
+ Cost estimate for reading a number of ranges from an index.
+
+ The cost estimate will only include the cost of reading data that
+ is contained in the index. If the records need to be read, use
+ read_cost() instead.
+
+ @note The ranges parameter is currently ignored and is not taken
+ into account in the cost estimate.
+
+ @note For this version it is recommended that storage engines continue
+ to override index_only_read_time() instead of this function.
+
+ @param index the index number
+ @param ranges the number of ranges to be read
+ @param rows total number of rows to be read
+
+ @returns the estimated cost
+ */
+
+ virtual Cost_estimate index_scan_cost(uint index, double ranges, double rows);
+
+ /**
+ Cost estimate for reading a set of ranges from the table using an index
+ to access it.
+
+ @note For this version it is recommended that storage engines continue
+ to override read_time() instead of this function.
+
+ @param index the index number
+ @param ranges the number of ranges to be read
+ @param rows total number of rows to be read
+
+ @returns the estimated cost
+ */
+
+ virtual Cost_estimate read_cost(uint index, double ranges, double rows);
+
+ /**
+ Return an estimate on the amount of memory the storage engine will
+ use for caching data in memory. If this is unknown or the storage
+ engine does not cache data in memory -1 is returned.
+ */
+ virtual longlong get_memory_buffer_size() const { return -1; }
+
+ /**
+ Return an estimate of how much of the table that is currently stored
+ in main memory.
+
+ This estimate should be the fraction of the table that currently
+ is available in a main memory buffer. The estimate should be in the
+ range from 0.0 (nothing in memory) to 1.0 (entire table in memory).
+
+ @return The fraction of the table in main memory buffer
+ */
+
+ double table_in_memory_estimate() const;
+
+ /**
+ Return an estimate of how much of the index that is currently stored
+ in main memory.
+
+ This estimate should be the fraction of the index that currently
+ is available in a main memory buffer. The estimate should be in the
+ range from 0.0 (nothing in memory) to 1.0 (entire index in memory).
+
+ @param keyno the index to get an estimate for
+
+ @return The fraction of the index in main memory buffer
+ */
+
+ double index_in_memory_estimate(uint keyno) const;
+
+ int ha_sample_init(double sampling_percentage, int sampling_seed,
+ enum_sampling_method sampling_method);
+ int ha_sample_next(uchar *buf);
+ int ha_sample_end();
+
+ private:
+ int check_collation_compatibility();
+
+ /**
+ Make a guestimate for how much of a table or index is in a memory
+ buffer in the case where the storage engine has not provided any
+ estimate for this.
+
+ @param table_index_size size of the table or index
+
+ @return The fraction of the table or index in main memory buffer
+ */
+
+ double estimate_in_memory_buffer(ulonglong table_index_size) const;
+
+ public:
+ virtual ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint *bufsz,
+ uint *flags, Cost_estimate *cost);
+ virtual ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+ uint *bufsz, uint *flags,
+ Cost_estimate *cost);
+ virtual int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint mode,
+ HANDLER_BUFFER *buf);
+
+ int ha_multi_range_read_next(char **range_info);
+
+ int ha_read_range_first(const key_range *start_key, const key_range *end_key,
+ bool eq_range, bool sorted);
+ int ha_read_range_next();
+
+ bool has_transactions() {
+ return (ha_table_flags() & HA_NO_TRANSACTIONS) == 0;
+ }
+ virtual uint extra_rec_buf_length() const { return 0; }
+
+ /**
+ @brief Determine whether an error can be ignored or not.
+
+ @details This method is used to analyze the error to see whether the
+ error is ignorable or not. Such errors will be reported as warnings
+ instead of errors for IGNORE statements. This means that the statement
+ will not abort, but instead continue to the next row.
+
+ HA_ERR_FOUND_DUP_UNIQUE is a special case in MyISAM that means the
+ same thing as HA_ERR_FOUND_DUP_KEY, but can in some cases lead to
+ a slightly different error message.
+
+ @param error error code received from the handler interface (HA_ERR_...)
+
+ @return whether the error is ignorablel or not
+ @retval true the error is ignorable
+ @retval false the error is not ignorable
+ */
+
+ virtual bool is_ignorable_error(int error);
+
+ /**
+ @brief Determine whether an error is fatal or not.
+
+ @details This method is used to analyze the error to see whether the
+ error is fatal or not. A fatal error is an error that will not be
+ possible to handle with SP handlers and will not be subject to
+ retry attempts on the slave.
+
+ @param error error code received from the handler interface (HA_ERR_...)
+
+ @return whether the error is fatal or not
+ @retval true the error is fatal
+ @retval false the error is not fatal
+ */
+
+ virtual bool is_fatal_error(int error);
+
+ protected:
+ virtual int multi_range_read_next(char **range_info);
+
+ /**
+ Number of rows in table. If HA_COUNT_ROWS_INSTANT is set, count is
+ available instantly. Else do a table scan.
+
+ @param num_rows [out] num_rows number of rows in table.
+
+ @retval 0 for OK, one of the HA_xxx values in case of error.
+ */
+ virtual int records(ha_rows *num_rows);
+
+ /**
+ Number of rows in table counted using the secondary index chosen by
+ optimizer. See comments in optimize_aggregated_query() .
+
+ @param num_rows [out] Number of rows in table.
+ @param index Index chosen by optimizer for counting.
+
+ @retval 0 for OK, one of the HA_xxx values in case of error.
+ */
+ virtual int records_from_index(ha_rows *num_rows, uint index);
+
+ private:
+ /**
+ Function will handle the error code from call to records() and
+ records_from_index().
+
+ @param error return code from records() and records_from_index().
+ @param num_rows Check if it contains HA_POS_ERROR in case error < 0.
+
+ @retval 0 for OK, one of the HA_xxx values in case of error.
+ */
+ int handle_records_error(int error, ha_rows *num_rows);
+
+ public:
+ /**
+ Wrapper function to call records() in storage engine.
+
+ @param num_rows [out] Number of rows in table.
+
+ @retval 0 for OK, one of the HA_xxx values in case of error.
+ */
+ int ha_records(ha_rows *num_rows) {
+ return handle_records_error(records(num_rows), num_rows);
+ }
+
+ /**
+ Wrapper function to call records_from_index() in storage engine.
+
+ @param num_rows [out] Number of rows in table.
+ @param index Index chosen by optimizer for counting.
+
+ @retval 0 for OK, one of the HA_xxx values in case of error.
+ */
+ int ha_records(ha_rows *num_rows, uint index) {
+ return handle_records_error(records_from_index(num_rows, index), num_rows);
+ }
+
+ /**
+ Return upper bound of current number of records in the table
+ (max. of how many records one will retrieve when doing a full table scan)
+ If upper bound is not known, HA_POS_ERROR should be returned as a max
+ possible upper bound.
+ */
+ virtual ha_rows estimate_rows_upper_bound() {
+ return stats.records + EXTRA_RECORDS;
+ }
+
+ /**
+ Get real row type for the table created based on one specified by user,
+ CREATE TABLE options and SE capabilities.
+ */
+ virtual enum row_type get_real_row_type(
+ const HA_CREATE_INFO *create_info) const {
+ return (create_info->table_options & HA_OPTION_COMPRESS_RECORD)
+ ? ROW_TYPE_COMPRESSED
+ : ((create_info->table_options & HA_OPTION_PACK_RECORD)
+ ? ROW_TYPE_DYNAMIC
+ : ROW_TYPE_FIXED);
+ }
+
+ /**
+ Get default key algorithm for SE. It is used when user has not provided
+ algorithm explicitly or when algorithm specified is not supported by SE.
+ */
+ virtual enum ha_key_alg get_default_index_algorithm() const {
+ return HA_KEY_ALG_SE_SPECIFIC;
+ }
+
+ /**
+ Check if SE supports specific key algorithm.
+
+ @note This method is never used for FULLTEXT or SPATIAL keys.
+ We rely on handler::ha_table_flags() to check if such keys
+ are supported.
+ */
+ virtual bool is_index_algorithm_supported(enum ha_key_alg key_alg) const {
+ return key_alg == HA_KEY_ALG_SE_SPECIFIC;
+ }
+
+ /**
+ Signal that the table->read_set and table->write_set table maps changed
+ The handler is allowed to set additional bits in the above map in this
+ call. Normally the handler should ignore all calls until we have done
+ a ha_rnd_init() or ha_index_init(), write_row(), update_row or delete_row()
+ as there may be several calls to this routine.
+ */
+ virtual void column_bitmaps_signal();
+ uint get_index(void) const { return active_index; }
+
+ /**
+ @retval 0 Bulk update used by handler
+ @retval 1 Bulk update not used, normal operation used
+ */
+ virtual bool start_bulk_update() { return 1; }
+ /**
+ @retval 0 Bulk delete used by handler
+ @retval 1 Bulk delete not used, normal operation used
+ */
+ virtual bool start_bulk_delete() { return 1; }
+ /**
+ After this call all outstanding updates must be performed. The number
+ of duplicate key errors are reported in the duplicate key parameter.
+ It is allowed to continue to the batched update after this call, the
+ handler has to wait until end_bulk_update with changing state.
+
+ @param dup_key_found Number of duplicate keys found
+
+ @retval 0 Success
+ @retval >0 Error code
+ */
+ virtual int exec_bulk_update(uint *dup_key_found MY_ATTRIBUTE((unused))) {
+ DBUG_ASSERT(false);
+ return HA_ERR_WRONG_COMMAND;
+ }
+ /**
+ Perform any needed clean-up, no outstanding updates are there at the
+ moment.
+ */
+ virtual void end_bulk_update() { return; }
+ /**
+ Execute all outstanding deletes and close down the bulk delete.
+
+ @retval 0 Success
+ @retval >0 Error code
+ */
+ virtual int end_bulk_delete() {
+ DBUG_ASSERT(false);
+ return HA_ERR_WRONG_COMMAND;
+ }
+
+ protected:
+ /**
+ @brief
+ Positions an index cursor to the index specified in the handle
+ ('active_index'). Fetches the row if available. If the key value is null,
+ begin at the first key of the index.
+ @returns 0 if success (found a record); non-zero if no record.
+ */
+ virtual int index_read_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag) {
+ uint key_len = calculate_key_len(table, active_index, keypart_map);
+ return index_read(buf, key, key_len, find_flag);
+ }
+ /**
+ Positions an index cursor to the index specified in argument. Fetches
+ the row if available. If the key value is null, begin at the first key of
+ the index.
+ @sa index_read_map()
+ */
+ virtual int index_read_idx_map(uchar *buf, uint index, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+
+ /*
+ These methods are used to jump to next or previous entry in the index
+ scan. There are also methods to jump to first and last entry.
+ */
+ /// @see index_read_map().
+ virtual int index_next(uchar *) { return HA_ERR_WRONG_COMMAND; }
+
+ /// @see index_read_map().
+ virtual int index_prev(uchar *) { return HA_ERR_WRONG_COMMAND; }
+
+ /// @see index_read_map().
+ virtual int index_first(uchar *) { return HA_ERR_WRONG_COMMAND; }
+
+ /// @see index_read_map().
+ virtual int index_last(uchar *) { return HA_ERR_WRONG_COMMAND; }
+
+ /// @see index_read_map().
+ virtual int index_next_same(uchar *buf, const uchar *key, uint keylen);
+ /**
+ The following functions works like index_read, but it find the last
+ row with the current key value or prefix.
+ @see index_read_map().
+ */
+ virtual int index_read_last_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map) {
+ uint key_len = calculate_key_len(table, active_index, keypart_map);
+ return index_read_last(buf, key, key_len);
+ }
+
+ virtual int read_range_first(const key_range *start_key,
+ const key_range *end_key, bool eq_range,
+ bool sorted);
+ virtual int read_range_next();
+
+ public:
+ /**
+ Set the end position for a range scan. This is used for checking
+ for when to end the range scan and by the ICP code to determine
+ that the next record is within the current range.
+
+ @param range The end value for the range scan
+ @param direction Direction of the range scan
+ */
+ void set_end_range(const key_range *range,
+ enum_range_scan_direction direction);
+ int compare_key(key_range *range);
+ int compare_key_icp(const key_range *range) const;
+ int compare_key_in_buffer(const uchar *buf) const;
+ virtual int ft_init() { return HA_ERR_WRONG_COMMAND; }
+ void ft_end() { ft_handler = NULL; }
+ virtual FT_INFO *ft_init_ext(uint flags MY_ATTRIBUTE((unused)),
+ uint inx MY_ATTRIBUTE((unused)),
+ String *key MY_ATTRIBUTE((unused))) {
+ return NULL;
+ }
+ virtual FT_INFO *ft_init_ext_with_hints(uint inx, String *key,
+ Ft_hints *hints) {
+ return ft_init_ext(hints->get_flags(), inx, key);
+ }
+ int ha_ft_read(uchar *buf);
+ int ha_read_first_row(uchar *buf, uint primary_key);
+
+ protected:
+ /// @see index_read_map().
+ virtual int rnd_next(uchar *buf) = 0;
+ /// @see index_read_map().
+ virtual int rnd_pos(uchar *buf, uchar *pos) = 0;
+
+ virtual int ft_read(uchar *) { return HA_ERR_WRONG_COMMAND; }
+
+ public:
+ /**
+ This function only works for handlers having
+ HA_PRIMARY_KEY_REQUIRED_FOR_POSITION set.
+ It will return the row with the PK given in the record argument.
+ */
+ virtual int rnd_pos_by_record(uchar *record) {
+ int error;
+ DBUG_ASSERT(table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION);
+
+ error = ha_rnd_init(false);
+ if (error != 0) return error;
+
+ position(record);
+ error = ha_rnd_pos(record, ref);
+
+ ha_rnd_end();
+ return error;
+ }
+
+ /**
+ Find number of records in a range.
+
+ Given a starting key, and an ending key estimate the number of rows that
+ will exist between the two. max_key may be empty which in case determine
+ if start_key matches any rows. Used by optimizer to calculate cost of
+ using a particular index.
+
+ @param inx Index number
+ @param min_key Start of range
+ @param max_key End of range
+
+ @return Number of rows in range.
+ */
+
+ virtual ha_rows records_in_range(uint inx MY_ATTRIBUTE((unused)),
+ key_range *min_key MY_ATTRIBUTE((unused)),
+ key_range *max_key MY_ATTRIBUTE((unused))) {
+ return (ha_rows)10;
+ }
+ /*
+ If HA_PRIMARY_KEY_REQUIRED_FOR_POSITION is set, then it sets ref
+ (reference to the row, aka position, with the primary key given in
+ the record).
+ Otherwise it set ref to the current row.
+ */
+ virtual void position(const uchar *record) = 0;
+
+ /**
+ General method to gather info from handler
+
+ ::info() is used to return information to the optimizer.
+ SHOW also makes use of this data Another note, if your handler
+ doesn't proved exact record count, you will probably want to
+ have the following in your code:
+ if (records < 2)
+ records = 2;
+ The reason is that the server will optimize for cases of only a single
+ record. If in a table scan you don't know the number of records
+ it will probably be better to set records to two so you can return
+ as many records as you need.
+
+ Along with records a few more variables you may wish to set are:
+ records
+ deleted
+ data_file_length
+ index_file_length
+ delete_length
+ check_time
+ Take a look at the public variables in handler.h for more information.
+ See also my_base.h for a full description.
+
+ @param flag Specifies what info is requested
+ */
+
+ virtual int info(uint flag) = 0;
+ virtual uint32 calculate_key_hash_value(
+ Field **field_array MY_ATTRIBUTE((unused))) {
+ DBUG_ASSERT(0);
+ return 0;
+ }
+ /**
+ Request storage engine to do an extra operation: enable,disable or run some
+ functionality.
+
+ @param operation the operation to perform
+
+ @returns
+ 0 on success
+ error otherwise
+ */
+ int ha_extra(enum ha_extra_function operation);
+
+ private:
+ /**
+ Storage engine specific implementation of ha_extra()
+
+ @param operation the operation to perform
+
+ @returns
+ 0 on success
+ error otherwise
+ */
+ virtual int extra(enum ha_extra_function operation MY_ATTRIBUTE((unused))) {
+ return 0;
+ }
+
+ public:
+ virtual int extra_opt(enum ha_extra_function operation,
+ ulong cache_size MY_ATTRIBUTE((unused))) {
+ return extra(operation);
+ }
+
+ /**
+ Start read (before write) removal on the current table.
+ @see HA_READ_BEFORE_WRITE_REMOVAL
+ */
+ virtual bool start_read_removal(void) {
+ DBUG_ASSERT(0);
+ return false;
+ }
+
+ /**
+ End read (before write) removal and return the number of rows
+ really written
+ @see HA_READ_BEFORE_WRITE_REMOVAL
+ */
+ virtual ha_rows end_read_removal(void) {
+ DBUG_ASSERT(0);
+ return (ha_rows)0;
+ }
+
+ /**
+ Normally, when running UPDATE or DELETE queries, we need to wait for other
+ transactions to release their locks on a given row before we can read it and
+ potentially update it. However, in READ UNCOMMITTED and READ COMMITTED, we
+ can ignore these locks if we don't intend to modify the row (e.g., because
+ it failed a WHERE). This is signaled through enabling “semi-consistent
+ read”, by calling try_semi_consistent_read(true) (and then setting it back
+ to false after finishing the query).
+
+ If semi-consistent read is enabled, and we are in READ UNCOMMITTED or READ
+ COMMITTED, the storage engine is permitted to return rows that are locked
+ and thus un-updatable. If the optimizer doesn't want the row, e.g., because
+ it got filtered out, it can call unlock_row() as usual. However, if it
+ intends to update the row, it needs to call was_semi_consistent_read()
+ before doing so. If was_semi_consistent_read() returns false, the row was
+ never locked to begin with and can be updated as usual. However, if it
+ returns 1, it was read optimistically, must be discarded (ie., do not try to
+ update the row) and must be re-read with locking enabled. The next read call
+ after was_semi_consistent_read() will automatically re-read the same row,
+ this time with locking enabled.
+
+ Thus, typical use in an UPDATE scenario would look like this:
+
+ file->try_semi_consistent_read(true);
+ file->ha_rnd_init(true);
+ while (file->ha_rnd_next(table->record[0]) == 0) {
+ if (row is filtered...) {
+ file->unlock_row();
+ continue;
+ }
+ if (file->was_semi_consistent_read()) {
+ // Discard the row; next ha_rnd_next() will read it again with
+ // locking.
+ continue;
+ }
+ // Process row here.
+ }
+ file->ha_rnd_end();
+ file->try_semi_consistent_read(false);
+
+ If the transaction isolation level is REPEATABLE READ or SERIALIZABLE,
+ enabling this flag has no effect.
+ */
+ virtual bool was_semi_consistent_read() { return false; }
+ /**
+ Tell the engine whether it should avoid unnecessary lock waits.
+ If yes, in an UPDATE or DELETE, if the row under the cursor was locked
+ by another transaction, the engine may try an optimistic read of
+ the last committed row value under the cursor.
+ */
+ virtual void try_semi_consistent_read(bool) {}
+
+ /**
+ Unlock last accessed row.
+
+ Record currently processed was not in the result set of the statement
+ and is thus unlocked. Used for UPDATE and DELETE queries.
+ */
+
+ virtual void unlock_row() {}
+
+ /**
+ Start a statement when table is locked
+
+ This method is called instead of external lock when the table is locked
+ before the statement is executed.
+
+ @param thd Thread object.
+ @param lock_type Type of external lock.
+
+ @retval >0 Error code.
+ @retval 0 Success.
+ */
+
+ virtual int start_stmt(THD *thd MY_ATTRIBUTE((unused)),
+ thr_lock_type lock_type MY_ATTRIBUTE((unused))) {
+ return 0;
+ }
+ virtual void get_auto_increment(ulonglong offset, ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values);
+ void set_next_insert_id(ulonglong id) {
+ DBUG_PRINT("info", ("auto_increment: next value %lu", (ulong)id));
+ next_insert_id = id;
+ }
+ void restore_auto_increment(ulonglong prev_insert_id) {
+ /*
+ Insertion of a row failed, re-use the lastly generated auto_increment
+ id, for the next row. This is achieved by resetting next_insert_id to
+ what it was before the failed insertion (that old value is provided by
+ the caller). If that value was 0, it was the first row of the INSERT;
+ then if insert_id_for_cur_row contains 0 it means no id was generated
+ for this first row, so no id was generated since the INSERT started, so
+ we should set next_insert_id to 0; if insert_id_for_cur_row is not 0, it
+ is the generated id of the first and failed row, so we use it.
+ */
+ next_insert_id =
+ (prev_insert_id > 0) ? prev_insert_id : insert_id_for_cur_row;
+ }
+
+ /**
+ Update create info as part of ALTER TABLE.
+
+ Forward this handler call to the storage engine foreach
+ partition handler. The data_file_name for each partition may
+ need to be reset if the tablespace was moved. Use a dummy
+ HA_CREATE_INFO structure and transfer necessary data.
+
+ @param create_info Create info from ALTER TABLE.
+ */
+
+ virtual void update_create_info(
+ HA_CREATE_INFO *create_info MY_ATTRIBUTE((unused))) {}
+ virtual int assign_to_keycache(THD *, HA_CHECK_OPT *) {
+ return HA_ADMIN_NOT_IMPLEMENTED;
+ }
+ virtual int preload_keys(THD *, HA_CHECK_OPT *) {
+ return HA_ADMIN_NOT_IMPLEMENTED;
+ }
+ /* end of the list of admin commands */
+
+ /**
+ Check if indexes are disabled.
+
+ @retval 0 Indexes are enabled.
+ @retval != 0 Indexes are disabled.
+ */
+
+ virtual int indexes_are_disabled(void) { return 0; }
+ virtual void append_create_info(String *packet MY_ATTRIBUTE((unused))) {}
+ /**
+ If index == MAX_KEY then a check for table is made and if index <
+ MAX_KEY then a check is made if the table has foreign keys and if
+ a foreign key uses this index (and thus the index cannot be dropped).
+
+ @param index Index to check if foreign key uses it
+
+ @retval true Foreign key defined on table or index
+ @retval false No foreign key defined
+ */
+ virtual bool is_fk_defined_on_table_or_index(
+ uint index MY_ATTRIBUTE((unused))) {
+ return false;
+ }
+ virtual char *get_foreign_key_create_info() {
+ return (NULL);
+ } /* gets foreign key create string from InnoDB */
+ /**
+ Get the list of foreign keys in this table.
+
+ @remark Returns the set of foreign keys where this table is the
+ dependent or child table.
+
+ @param thd The thread handle.
+ @param [out] f_key_list The list of foreign keys.
+
+ @return The handler error code or zero for success.
+ */
+ virtual int get_foreign_key_list(THD *thd MY_ATTRIBUTE((unused)),
+ List<FOREIGN_KEY_INFO> *f_key_list
+ MY_ATTRIBUTE((unused))) {
+ return 0;
+ }
+ /**
+ Get the list of foreign keys referencing this table.
+
+ @remark Returns the set of foreign keys where this table is the
+ referenced or parent table.
+
+ @param thd The thread handle.
+ @param [out] f_key_list The list of foreign keys.
+
+ @return The handler error code or zero for success.
+ */
+ virtual int get_parent_foreign_key_list(THD *thd MY_ATTRIBUTE((unused)),
+ List<FOREIGN_KEY_INFO> *f_key_list
+ MY_ATTRIBUTE((unused))) {
+ return 0;
+ }
+ /**
+ Get the list of tables which are direct or indirect parents in foreign
+ key with cascading actions for this table.
+
+ @remarks Returns the set of parent tables connected by FK clause that
+ can modify the given table.
+
+ @param thd The thread handle.
+ @param[out] fk_table_list List of parent tables (including indirect
+ parents). Elements of the list as well as buffers for database and schema
+ names are allocated from the current memory root.
+
+ @return The handler error code or zero for success
+ */
+ virtual int get_cascade_foreign_key_table_list(
+ THD *thd MY_ATTRIBUTE((unused)),
+ List<st_handler_tablename> *fk_table_list MY_ATTRIBUTE((unused))) {
+ return 0;
+ }
+ virtual uint referenced_by_foreign_key() { return 0; }
+ virtual void init_table_handle_for_HANDLER() {
+ return;
+ } /* prepare InnoDB for HANDLER */
+ virtual void free_foreign_key_create_info(char *) {}
+ /** The following can be called without an open handler */
+ virtual const char *table_type() const = 0;
+
+ virtual ulong index_flags(uint idx, uint part, bool all_parts) const = 0;
+
+ uint max_record_length() const {
+ return std::min(HA_MAX_REC_LENGTH, max_supported_record_length());
+ }
+ uint max_keys() const {
+ return std::min<uint>(MAX_KEY, max_supported_keys());
+ }
+ uint max_key_parts() const {
+ return std::min(MAX_REF_PARTS, max_supported_key_parts());
+ }
+ uint max_key_length() const {
+ return std::min(MAX_KEY_LENGTH, max_supported_key_length());
+ }
+ uint max_key_part_length(HA_CREATE_INFO *create_info) const {
+ return std::min(MAX_KEY_LENGTH, max_supported_key_part_length(create_info));
+ }
+
+ virtual uint max_supported_record_length() const { return HA_MAX_REC_LENGTH; }
+ virtual uint max_supported_keys() const { return 0; }
+ virtual uint max_supported_key_parts() const { return MAX_REF_PARTS; }
+ virtual uint max_supported_key_length() const { return MAX_KEY_LENGTH; }
+ virtual uint max_supported_key_part_length(
+ HA_CREATE_INFO *create_info MY_ATTRIBUTE((unused))) const {
+ return 255;
+ }
+ virtual uint min_record_length(uint options MY_ATTRIBUTE((unused))) const {
+ return 1;
+ }
+
+ virtual bool low_byte_first() const { return 1; }
+ virtual ha_checksum checksum() const { return 0; }
+
+ /**
+ Check if the table is crashed.
+
+ @retval true Crashed
+ @retval false Not crashed
+ */
+
+ virtual bool is_crashed() const { return 0; }
+
+ /**
+ Check if the table can be automatically repaired.
+
+ @retval true Can be auto repaired
+ @retval false Cannot be auto repaired
+ */
+
+ virtual bool auto_repair() const { return 0; }
+
+ /**
+ Get number of lock objects returned in store_lock.
+
+ Returns the number of store locks needed in call to store lock.
+ We return number of partitions we will lock multiplied with number of
+ locks needed by each partition. Assists the above functions in allocating
+ sufficient space for lock structures.
+
+ @returns Number of locks returned in call to store_lock.
+
+ @note lock_count() can return > 1 if the table is MERGE or partitioned.
+ */
+
+ virtual uint lock_count(void) const { return 1; }
+
+ /**
+ Is not invoked for non-transactional temporary tables.
+
+ @note store_lock() can return more than one lock if the table is MERGE
+ or partitioned.
+
+ @note that one can NOT rely on table->in_use in store_lock(). It may
+ refer to a different thread if called from mysql_lock_abort_for_thread().
+
+ @note If the table is MERGE, store_lock() can return less locks
+ than lock_count() claimed. This can happen when the MERGE children
+ are not attached when this is called from another thread.
+
+ The idea with handler::store_lock() is the following:
+
+ The statement decided which locks we should need for the table
+ for updates/deletes/inserts we get WRITE locks, for SELECT... we get
+ read locks.
+
+ Before adding the lock into the table lock handler (see thr_lock.c)
+ mysqld calls store lock with the requested locks. Store lock can now
+ modify a write lock to a read lock (or some other lock), ignore the
+ lock (if we don't want to use MySQL table locks at all) or add locks
+ for many tables (like we do when we are using a MERGE handler).
+
+ In some exceptional cases MySQL may send a request for a TL_IGNORE;
+ This means that we are requesting the same lock as last time and this
+ should also be ignored.
+
+ Called from lock.cc by get_lock_data().
+ */
+ virtual THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type) = 0;
+
+ /**
+ Check if the primary key is clustered or not.
+
+ @retval true Primary key (if there is one) is a clustered
+ key covering all fields
+ @retval false otherwise
+ */
+
+ virtual bool primary_key_is_clustered() const { return false; }
+
+ /**
+ Compare two positions.
+
+ @param ref1 First position.
+ @param ref2 Second position.
+
+ @retval <0 ref1 < ref2.
+ @retval 0 Equal.
+ @retval >0 ref1 > ref2.
+ */
+
+ virtual int cmp_ref(const uchar *ref1, const uchar *ref2) const {
+ return memcmp(ref1, ref2, ref_length);
+ }
+
+ /*
+ Condition pushdown to storage engines
+ */
+
+ /**
+ Push condition down to the table handler.
+
+ @param cond Condition to be pushed. The condition tree
+ must not be modified by the caller.
+ @param other_tbls_ok Are other tables than than 'this' allowed to
+ be referred by the condition terms being pushed.
+
+ @return
+ The 'remainder' condition that caller must use to filter out records.
+ NULL means the handler will not return rows that do not match the
+ passed condition.
+
+ @note
+ handler->ha_reset() call discard any pushed conditions.
+ Calls to rnd_init/rnd_end, index_init/index_end etc do not affect the
+ pushed conditions.
+ */
+ virtual const Item *cond_push(const Item *cond,
+ bool other_tbls_ok MY_ATTRIBUTE((unused))) {
+ DBUG_ASSERT(pushed_cond == NULL);
+ return cond;
+ }
+
+ /**
+ Push down an index condition to the handler.
+
+ The server will use this method to push down a condition it wants
+ the handler to evaluate when retrieving records using a specified
+ index. The pushed index condition will only refer to fields from
+ this handler that is contained in the index (but it may also refer
+ to fields in other handlers). Before the handler evaluates the
+ condition it must read the content of the index entry into the
+ record buffer.
+
+ The handler is free to decide if and how much of the condition it
+ will take responsibility for evaluating. Based on this evaluation
+ it should return the part of the condition it will not evaluate.
+ If it decides to evaluate the entire condition it should return
+ NULL. If it decides not to evaluate any part of the condition it
+ should return a pointer to the same condition as given as argument.
+
+ @param keyno the index number to evaluate the condition on
+ @param idx_cond the condition to be evaluated by the handler
+
+ @return The part of the pushed condition that the handler decides
+ not to evaluate
+ */
+
+ virtual Item *idx_cond_push(uint keyno MY_ATTRIBUTE((unused)),
+ Item *idx_cond) {
+ return idx_cond;
+ }
+
+ /** Reset information about pushed index conditions */
+ virtual void cancel_pushed_idx_cond() {
+ pushed_idx_cond = NULL;
+ pushed_idx_cond_keyno = MAX_KEY;
+ in_range_check_pushed_down = false;
+ }
+
+ /**
+ Reports number of tables included in pushed join which this
+ handler instance is part of. ==0 -> Not pushed
+ */
+ virtual uint number_of_pushed_joins() const { return 0; }
+
+ /**
+ If this handler instance is part of a pushed join sequence
+ returned TABLE instance being root of the pushed query?
+ */
+ virtual const TABLE *member_of_pushed_join() const { return NULL; }
+
+ /**
+ If this handler instance is a child in a pushed join sequence
+ returned TABLE instance being my parent?
+ */
+ virtual const TABLE *parent_of_pushed_join() const { return NULL; }
+
+ int ha_index_read_pushed(uchar *buf, const uchar *key,
+ key_part_map keypart_map);
+
+ int ha_index_next_pushed(uchar *buf);
+
+ protected:
+ virtual int index_read_pushed(uchar *, const uchar *, key_part_map) {
+ return HA_ERR_WRONG_COMMAND;
+ }
+
+ virtual int index_next_pushed(uchar *) { return HA_ERR_WRONG_COMMAND; }
+
+ public:
+ /**
+ Part of old, deprecated in-place ALTER API.
+ */
+ virtual bool check_if_incompatible_data(
+ HA_CREATE_INFO *create_info MY_ATTRIBUTE((unused)),
+ uint table_changes MY_ATTRIBUTE((unused))) {
+ return COMPATIBLE_DATA_NO;
+ }
+
+ /* On-line/in-place/instant ALTER TABLE interface. */
+
+ /*
+ Here is an outline of on-line/in-place ALTER TABLE execution through
+ this interface.
+
+ Phase 1 : Initialization
+ ========================
+ During this phase we determine which algorithm should be used
+ for execution of ALTER TABLE and what level concurrency it will
+ require.
+
+ *) This phase starts by opening the table and preparing description
+ of the new version of the table.
+ *) Then we check if it is impossible even in theory to carry out
+ this ALTER TABLE using the in-place/instant algorithm. For example,
+ because we need to change storage engine or the user has explicitly
+ requested usage of the "copy" algorithm.
+ *) If in-place/instant ALTER TABLE is theoretically possible, we continue
+ by compiling differences between old and new versions of the table
+ in the form of HA_ALTER_FLAGS bitmap. We also build a few
+ auxiliary structures describing requested changes and store
+ all these data in the Alter_inplace_info object.
+ *) Then the handler::check_if_supported_inplace_alter() method is called
+ in order to find if the storage engine can carry out changes requested
+ by this ALTER TABLE using the in-place or instant algorithm.
+ To determine this, the engine can rely on data in HA_ALTER_FLAGS/
+ Alter_inplace_info passed to it as well as on its own checks.
+ If the in-place algorithm can be used for this ALTER TABLE, the level
+ of required concurrency for its execution is also returned.
+ If any errors occur during the handler call, ALTER TABLE is aborted
+ and no further handler functions are called.
+ Note that in cases when there is difference between in-place and
+ instant algorithm and user explicitly asked for usage of in-place
+ algorithm storage engine MUST return one of values corresponding
+ to in-place algorithm and not HA_ALTER_INPLACE_INSTANT from this
+ method.
+ *) Locking requirements of the in-place algorithm are compared to any
+ concurrency requirements specified by user. If there is a conflict
+ between them, we either switch to the copy algorithm or emit an error.
+
+ Phase 2 : Execution
+ ===================
+
+ In this phase the operations are executed.
+
+ *) As the first step, we acquire a lock corresponding to the concurrency
+ level which was returned by handler::check_if_supported_inplace_alter()
+ and requested by the user. This lock is held for most of the
+ duration of in-place ALTER (if HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE
+ or HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE were returned we acquire an
+ exclusive lock for duration of the next step only).
+ For HA_ALTER_INPLACE_INSTANT we keep shared upgradable metadata lock
+ which was acquired at table open time.
+ *) After that we call handler::ha_prepare_inplace_alter_table() to give the
+ storage engine a chance to update its internal structures with a higher
+ lock level than the one that will be used for the main step of algorithm.
+ After that we downgrade the lock if it is necessary.
+ This step should be no-op for instant algorithm.
+ *) After that, the main step of this phase and algorithm is executed.
+ We call the handler::ha_inplace_alter_table() method, which carries out
+ the changes requested by ALTER TABLE but does not makes them visible to
+ other connections yet.
+ This step should be no-op for instant algorithm as well.
+ *) We ensure that no other connection uses the table by upgrading our
+ lock on it to exclusive.
+ *) a) If the previous step succeeds,
+ handler::ha_commit_inplace_alter_table() is called to allow the storage
+ engine to do any final updates to its structures, to make all earlier
+ changes durable and visible to other connections.
+ For instant algorithm this is the step during which SE changes are done.
+ Engines that support atomic DDL only prepare for the commit during this
+ step but do not finalize it. Real commit happens later when the whole
+ statement is committed. Also in some situations statement might be rolled
+ back after call to commit_inplace_alter_table() for such storage engines.
+ In the latter special case SE might require call to
+ handlerton::dict_cache_reset() in order to invalidate its internal table
+ definition cache after rollback.
+ b) If we have failed to upgrade lock or any errors have occurred during
+ the handler functions calls (including commit), we call
+ handler::ha_commit_inplace_alter_table() to rollback all changes which
+ were done during previous steps.
+
+ All the above calls to SE are provided with dd::Table objects describing old
+ and new version of table being altered. Engines which support atomic DDL are
+ allowed to adjust object corresponding to the new version. During phase 3
+ these changes are saved to the data-dictionary.
+
+
+ Phase 3 : Final
+ ===============
+
+ In this phase we:
+
+ a) For engines which don't support atomic DDL:
+
+ *) Update the SQL-layer data-dictionary by replacing description of old
+ version of the table with its new version. This change is immediately
+ committed.
+ *) Inform the storage engine about this change by calling the
+ handler::ha_notify_table_changed() method.
+ *) Process the RENAME clause by calling handler::ha_rename_table() and
+ updating the data-dictionary accordingly. Again this change is
+ immediately committed.
+ *) Destroy the Alter_inplace_info and handler_ctx objects.
+
+ b) For engines which support atomic DDL:
+
+ *) Update the SQL-layer data-dictionary by replacing description of old
+ version of the table with its new version.
+ *) Process the RENAME clause by calling handler::ha_rename_table() and
+ updating the data-dictionary accordingly.
+ *) Commit the statement/transaction.
+ *) Finalize atomic DDL operation by calling handlerton::post_ddl() hook
+ for the storage engine.
+ *) Additionally inform the storage engine about completion of ALTER TABLE
+ for the table by calling the handler::ha_notify_table_changed()
+ method.
+ *) Destroy the Alter_inplace_info and handler_ctx objects.
+ */
+
+ /**
+ Check if a storage engine supports a particular alter table in-place
+
+ @param altered_table TABLE object for new version of table.
+ @param ha_alter_info Structure describing changes to be done
+ by ALTER TABLE and holding data used
+ during in-place alter.
+
+ @retval HA_ALTER_ERROR Unexpected error.
+ @retval HA_ALTER_INPLACE_NOT_SUPPORTED Not supported, must use copy.
+ @retval HA_ALTER_INPLACE_EXCLUSIVE_LOCK Supported, but requires X lock.
+ @retval HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE
+ Supported, but requires SNW lock
+ during main phase. Prepare phase
+ requires X lock.
+ @retval HA_ALTER_INPLACE_SHARED_LOCK Supported, but requires SNW lock.
+ @retval HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE
+ Supported, concurrent
+ reads/writes allowed. However, prepare phase requires X lock.
+ @retval HA_ALTER_INPLACE_NO_LOCK Supported, concurrent
+ reads/writes allowed.
+ @retval HA_ALTER_INPLACE_INSTANT Instant algorithm is supported.
+ Prepare and main phases are
+ no-op. Changes happen during
+ commit phase and it should be
+ "instant". We keep SU lock,
+ allowing concurrent reads and
+ writes during no-op phases and
+ upgrade it to X lock before
+ commit phase.
+
+ @note The default implementation uses the old in-place ALTER API
+ to determine if the storage engine supports in-place ALTER or not.
+
+ @note In cases when there is difference between in-place and instant
+ algorithm and explicit ALGORITHM=INPLACE clause was provided SE MUST
+ return one of values corresponding to in-place algorithm and not
+ HA_ALTER_INPLACE_INSTANT from this method.
+
+ @note Called without holding thr_lock.c lock.
+ */
+ virtual enum_alter_inplace_result check_if_supported_inplace_alter(
+ TABLE *altered_table, Alter_inplace_info *ha_alter_info);
+
+ /**
+ Public functions wrapping the actual handler call.
+ @see prepare_inplace_alter_table()
+ */
+ bool ha_prepare_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ const dd::Table *old_table_def,
+ dd::Table *new_table_def);
+
+ /**
+ Public function wrapping the actual handler call.
+ @see inplace_alter_table()
+ */
+ bool ha_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ const dd::Table *old_table_def,
+ dd::Table *new_table_def) {
+ return inplace_alter_table(altered_table, ha_alter_info, old_table_def,
+ new_table_def);
+ }
+
+ /**
+ Public function wrapping the actual handler call.
+ Allows us to enforce asserts regardless of handler implementation.
+ @see commit_inplace_alter_table()
+ */
+ bool ha_commit_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ bool commit,
+ const dd::Table *old_table_def,
+ dd::Table *new_table_def);
+
+ /**
+ Public function wrapping the actual handler call.
+
+ @see notify_table_changed()
+ */
+ void ha_notify_table_changed(Alter_inplace_info *ha_alter_info) {
+ notify_table_changed(ha_alter_info);
+ }
+
+ protected:
+ /**
+ Allows the storage engine to update internal structures with concurrent
+ writes blocked. If check_if_supported_inplace_alter() returns
+ HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE or
+ HA_ALTER_INPLACE_SHARED_AFTER_PREPARE, this function is called with
+ exclusive lock otherwise the same level of locking as for
+ inplace_alter_table() will be used.
+
+ @note Should be no-op for instant algorithm.
+
+ @note Storage engines are responsible for reporting any errors by
+ calling my_error()/print_error()
+
+ @note If this function reports error, commit_inplace_alter_table()
+ will be called with commit= false.
+
+ @note For partitioning, failing to prepare one partition, means that
+ commit_inplace_alter_table() will be called to roll back changes for
+ all partitions. This means that commit_inplace_alter_table() might be
+ called without prepare_inplace_alter_table() having been called first
+ for a given partition.
+
+ @param altered_table TABLE object for new version of table.
+ @param ha_alter_info Structure describing changes to be done
+ by ALTER TABLE and holding data used
+ during in-place alter.
+ @param old_table_def dd::Table object describing old version of
+ the table.
+ @param new_table_def dd::Table object for the new version of the
+ table. Can be adjusted by this call if SE
+ supports atomic DDL. These changes to the
+ table definition will be persisted in the
+ data-dictionary at statement commit time.
+
+ @retval true Error
+ @retval false Success
+ */
+ virtual bool prepare_inplace_alter_table(
+ TABLE *altered_table MY_ATTRIBUTE((unused)),
+ Alter_inplace_info *ha_alter_info MY_ATTRIBUTE((unused)),
+ const dd::Table *old_table_def MY_ATTRIBUTE((unused)),
+ dd::Table *new_table_def MY_ATTRIBUTE((unused))) {
+ return false;
+ }
+
+ /**
+ Alter the table structure in-place with operations specified using
+ HA_ALTER_FLAGS and Alter_inplace_info. The level of concurrency allowed
+ during this operation depends on the return value from
+ check_if_supported_inplace_alter().
+
+ @note Should be no-op for instant algorithm.
+
+ @note Storage engines are responsible for reporting any errors by
+ calling my_error()/print_error()
+
+ @note If this function reports error, commit_inplace_alter_table()
+ will be called with commit= false.
+
+ @param altered_table TABLE object for new version of table.
+ @param ha_alter_info Structure describing changes to be done
+ by ALTER TABLE and holding data used
+ during in-place alter.
+ @param old_table_def dd::Table object describing old version of
+ the table.
+ @param new_table_def dd::Table object for the new version of the
+ table. Can be adjusted by this call if SE
+ supports atomic DDL. These changes to the
+ table definition will be persisted in the
+ data-dictionary at statement commit time.
+
+ @retval true Error
+ @retval false Success
+ */
+ virtual bool inplace_alter_table(
+ TABLE *altered_table MY_ATTRIBUTE((unused)),
+ Alter_inplace_info *ha_alter_info MY_ATTRIBUTE((unused)),
+ const dd::Table *old_table_def MY_ATTRIBUTE((unused)),
+ dd::Table *new_table_def MY_ATTRIBUTE((unused))) {
+ return false;
+ }
+
+ /**
+ Commit or rollback the changes made during prepare_inplace_alter_table()
+ and inplace_alter_table() inside the storage engine.
+ Note that in case of rollback the allowed level of concurrency during
+ this operation will be the same as for inplace_alter_table() and thus
+ might be higher than during prepare_inplace_alter_table(). (For example,
+ concurrent writes were blocked during prepare, but might not be during
+ rollback).
+
+ @note This is the place where SE changes happen for instant algorithm.
+
+ @note For storage engines supporting atomic DDL this method should only
+ prepare for the commit but do not finalize it. Real commit should happen
+ later when the whole statement is committed. Also in some situations
+ statement might be rolled back after call to commit_inplace_alter_table()
+ for such storage engines. In the latter special case SE might require call
+ to handlerton::dict_cache_reset() in order to invalidate its internal
+ table definition cache after rollback.
+
+ @note Storage engines are responsible for reporting any errors by
+ calling my_error()/print_error()
+
+ @note If this function with commit= true reports error, it will be called
+ again with commit= false.
+
+ @note In case of partitioning, this function might be called for rollback
+ without prepare_inplace_alter_table() having been called first.
+ Also partitioned tables sets ha_alter_info->group_commit_ctx to a NULL
+ terminated array of the partitions handlers and if all of them are
+ committed as one, then group_commit_ctx should be set to NULL to indicate
+ to the partitioning handler that all partitions handlers are committed.
+ @see prepare_inplace_alter_table().
+
+ @param altered_table TABLE object for new version of table.
+ @param ha_alter_info Structure describing changes to be done
+ by ALTER TABLE and holding data used
+ during in-place alter.
+ @param commit True => Commit, False => Rollback.
+ @param old_table_def dd::Table object describing old version of
+ the table.
+ @param new_table_def dd::Table object for the new version of the
+ table. Can be adjusted by this call if SE
+ supports atomic DDL. These changes to the
+ table definition will be persisted in the
+ data-dictionary at statement commit time.
+
+ @retval true Error
+ @retval false Success
+ */
+ virtual bool commit_inplace_alter_table(
+ TABLE *altered_table MY_ATTRIBUTE((unused)),
+ Alter_inplace_info *ha_alter_info MY_ATTRIBUTE((unused)),
+ bool commit MY_ATTRIBUTE((unused)),
+ const dd::Table *old_table_def MY_ATTRIBUTE((unused)),
+ dd::Table *new_table_def MY_ATTRIBUTE((unused))) {
+ /* Nothing to commit/rollback, mark all handlers committed! */
+ ha_alter_info->group_commit_ctx = NULL;
+ return false;
+ }
+
+ /**
+ Notify the storage engine that the table definition has been updated.
+
+ @param ha_alter_info Structure describing changes done by
+ ALTER TABLE and holding data used
+ during in-place alter.
+
+ @note No errors are allowed during notify_table_changed().
+
+ @note For storage engines supporting atomic DDL this method is invoked
+ after the whole ALTER TABLE is completed and committed.
+ Particularly this means that for ALTER TABLE statements with RENAME
+ clause TABLE/handler object used for invoking this method will be
+ associated with new table name. If storage engine needs to know
+ the old schema and table name in this method for some reason it
+ has to use ha_alter_info object to figure it out.
+ */
+ virtual void notify_table_changed(
+ Alter_inplace_info *ha_alter_info MY_ATTRIBUTE((unused))) {}
+
+ public:
+ /* End of On-line/in-place ALTER TABLE interface. */
+
+ /**
+ use_hidden_primary_key() is called in case of an update/delete when
+ (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
+ but we don't have a primary key
+ */
+ virtual void use_hidden_primary_key();
+
+ protected:
+ /* Service methods for use by storage engines. */
+ void ha_statistic_increment(ulonglong System_status_var::*offset) const;
+ THD *ha_thd(void) const;
+
+ /**
+ Acquire the instrumented table information from a table share.
+ @param share a table share
+ @return an instrumented table share, or NULL.
+ */
+ PSI_table_share *ha_table_share_psi(const TABLE_SHARE *share) const;
+
+ /**
+ Default rename_table() and delete_table() rename/delete files with a
+ given name and extensions from handlerton::file_extensions.
+
+ These methods can be overridden, but their default implementation
+ provide useful functionality.
+
+ @param [in] from Path for the old table name.
+ @param [in] to Path for the new table name.
+ @param [in] from_table_def Old version of definition for table
+ being renamed (i.e. prior to rename).
+ @param [in,out] to_table_def New version of definition for table
+ being renamed. Storage engines which
+ support atomic DDL (i.e. having
+ HTON_SUPPORTS_ATOMIC_DDL flag set)
+ are allowed to adjust this object.
+
+ @retval >0 Error.
+ @retval 0 Success.
+ */
+ virtual int rename_table(const char *from, const char *to,
+ const dd::Table *from_table_def,
+ dd::Table *to_table_def);
+
+ /**
+ Delete a table.
+
+ Used to delete a table. By the time delete_table() has been called all
+ opened references to this table will have been closed (and your globally
+ shared references released. The variable name will just be the name of
+ the table. You will need to remove any files you have created at this
+ point. Called for base as well as temporary tables.
+
+ @param name Full path of table name.
+ @param table_def dd::Table describing table being deleted
+ (can be NULL for temporary tables created
+ by optimizer).
+
+ @retval >0 Error.
+ @retval 0 Success.
+ */
+ virtual int delete_table(const char *name, const dd::Table *table_def);
+
+ private:
+ /* Private helpers */
+ void mark_trx_read_write();
+ /*
+ Low-level primitives for storage engines. These should be
+ overridden by the storage engine class. To call these methods, use
+ the corresponding 'ha_*' method above.
+ */
+
+ virtual int open(const char *name, int mode, uint test_if_locked,
+ const dd::Table *table_def) = 0;
+ virtual int close(void) = 0;
+ virtual int index_init(uint idx, bool sorted MY_ATTRIBUTE((unused))) {
+ active_index = idx;
+ return 0;
+ }
+ virtual int index_end() {
+ active_index = MAX_KEY;
+ return 0;
+ }
+ /**
+ rnd_init() can be called two times without rnd_end() in between
+ (it only makes sense if scan=1).
+ then the second call should prepare for the new table scan (e.g
+ if rnd_init allocates the cursor, second call should position it
+ to the start of the table, no need to deallocate and allocate it again
+ */
+ virtual int rnd_init(bool scan) = 0;
+ virtual int rnd_end() { return 0; }
+ /**
+ Write a row.
+
+ write_row() inserts a row. buf is a byte array of data, normally
+ record[0].
+
+ You can use the field information to extract the data from the native byte
+ array type.
+
+ Example of this would be:
+ for (Field **field=table->field ; *field ; field++)
+ {
+ ...
+ }
+
+ @param buf Buffer to write from.
+
+ @return Operation status.
+ @retval 0 Success.
+ @retval != 0 Error code.
+ */
+ virtual int write_row(uchar *buf MY_ATTRIBUTE((unused))) {
+ return HA_ERR_WRONG_COMMAND;
+ }
+
+ /**
+ Update a single row.
+
+ Note: If HA_ERR_FOUND_DUPP_KEY is returned, the handler must read
+ all columns of the row so MySQL can create an error message. If
+ the columns required for the error message are not read, the error
+ message will contain garbage.
+ */
+ virtual int update_row(const uchar *old_data MY_ATTRIBUTE((unused)),
+ uchar *new_data MY_ATTRIBUTE((unused))) {
+ return HA_ERR_WRONG_COMMAND;
+ }
+
+ virtual int delete_row(const uchar *buf MY_ATTRIBUTE((unused))) {
+ return HA_ERR_WRONG_COMMAND;
+ }
+ /**
+ Reset state of file to after 'open'.
+ This function is called after every statement for all tables used
+ by that statement.
+ */
+ virtual int reset() { return 0; }
+ virtual Table_flags table_flags(void) const = 0;
+ /**
+ Is not invoked for non-transactional temporary tables.
+
+ Tells the storage engine that we intend to read or write data
+ from the table. This call is prefixed with a call to handler::store_lock()
+ and is invoked only for those handler instances that stored the lock.
+
+ Calls to @c rnd_init / @c index_init are prefixed with this call. When table
+ IO is complete, we call @code external_lock(F_UNLCK) @endcode.
+ A storage engine writer should expect that each call to
+ @code ::external_lock(F_[RD|WR]LOCK @endcode is followed by a call to
+ @code ::external_lock(F_UNLCK) @endcode. If it is not, it is a bug in MySQL.
+
+ The name and signature originate from the first implementation
+ in MyISAM, which would call @c fcntl to set/clear an advisory
+ lock on the data file in this method.
+
+ Originally this method was used to set locks on file level to enable
+ several MySQL Servers to work on the same data. For transactional
+ engines it has been "abused" to also mean start and end of statements
+ to enable proper rollback of statements and transactions. When LOCK
+ TABLES has been issued the start_stmt method takes over the role of
+ indicating start of statement but in this case there is no end of
+ statement indicator(?).
+
+ Called from lock.cc by lock_external() and unlock_external(). Also called
+ from sql_table.cc by copy_data_between_tables().
+
+ @param thd the current thread
+ @param lock_type F_RDLCK, F_WRLCK, F_UNLCK
+
+ @return non-0 in case of failure, 0 in case of success.
+ When lock_type is F_UNLCK, the return value is ignored.
+ */
+ virtual int external_lock(THD *thd MY_ATTRIBUTE((unused)),
+ int lock_type MY_ATTRIBUTE((unused))) {
+ return 0;
+ }
+ virtual void release_auto_increment() { return; }
+ /** admin commands - called from mysql_admin_table */
+ virtual int check_for_upgrade(HA_CHECK_OPT *) { return 0; }
+ virtual int check(THD *, HA_CHECK_OPT *) { return HA_ADMIN_NOT_IMPLEMENTED; }
+
+ /**
+ In this method check_opt can be modified
+ to specify CHECK option to use to call check()
+ upon the table.
+ */
+ virtual int repair(THD *, HA_CHECK_OPT *) {
+ DBUG_ASSERT(!(ha_table_flags() & HA_CAN_REPAIR));
+ return HA_ADMIN_NOT_IMPLEMENTED;
+ }
+ virtual void start_bulk_insert(ha_rows) {}
+ virtual int end_bulk_insert() { return 0; }
+
+ /**
+ Does this handler want to get a Record_buffer for multi-row reads
+ via the ha_set_record_buffer() function? And if so, what is the
+ maximum number of records to allocate space for in the buffer?
+
+ Storage engines that support using a Record_buffer should override
+ this function and return true for scans that could benefit from a
+ buffer.
+
+ @param[out] max_rows gets set to the maximum number of records to
+ allocate space for in the buffer if the function
+ returns true
+
+ @retval true if the handler would like a Record_buffer
+ @retval false if the handler does not want a Record_buffer
+ */
+ virtual bool is_record_buffer_wanted(ha_rows *const max_rows) const {
+ *max_rows = 0;
+ return false;
+ }
+
+ // Set se_private_id and se_private_data during upgrade
+ virtual bool upgrade_table(THD *thd MY_ATTRIBUTE((unused)),
+ const char *dbname MY_ATTRIBUTE((unused)),
+ const char *table_name MY_ATTRIBUTE((unused)),
+ dd::Table *dd_table MY_ATTRIBUTE((unused))) {
+ return false;
+ }
+
+ virtual int sample_init();
+ virtual int sample_next(uchar *buf);
+ virtual int sample_end();
+
+ /**
+ * Prepares secondary engine for loading a table.
+ *
+ * @param table Table opened in primary storage engine. Its read_set tells
+ * which columns to load.
+ *
+ * @return 0 if success, error code otherwise.
+ */
+ virtual int prepare_load_table(const TABLE &table MY_ATTRIBUTE((unused))) {
+ DBUG_ASSERT(false);
+ return HA_ERR_WRONG_COMMAND;
+ }
+
+ /**
+ * Loads a table into its defined secondary storage engine.
+ *
+ * @param table Table opened in primary storage engine. Its read_set tells
+ * which columns to load.
+ *
+ * @return 0 if success, error code otherwise.
+ */
+ virtual int load_table(const TABLE &table MY_ATTRIBUTE((unused))) {
+ /* purecov: begin inspected */
+ DBUG_ASSERT(false);
+ return HA_ERR_WRONG_COMMAND;
+ /* purecov: end */
+ }
+
+ /**
+ * Unloads a table from its defined secondary storage engine.
+ *
+ * @param db_name Database name.
+ * @param table_name Table name.
+ * @param error_if_not_loaded If true, then errors will be reported by this
+ * function. If false, no errors will be reported
+ * (silently fail). This case of false is useful
+ * during DROP TABLE where a failure to unload
+ * should not prevent dropping the whole table.
+ * @return 0 if success, error code otherwise.
+ */
+ virtual int unload_table(const char *db_name MY_ATTRIBUTE((unused)),
+ const char *table_name MY_ATTRIBUTE((unused)),
+ bool error_if_not_loaded MY_ATTRIBUTE((unused))) {
+ /* purecov: begin inspected */
+ DBUG_ASSERT(false);
+ return HA_ERR_WRONG_COMMAND;
+ /* purecov: end */
+ }
+
+ protected:
+ virtual int index_read(uchar *buf MY_ATTRIBUTE((unused)),
+ const uchar *key MY_ATTRIBUTE((unused)),
+ uint key_len MY_ATTRIBUTE((unused)),
+ enum ha_rkey_function find_flag
+ MY_ATTRIBUTE((unused))) {
+ return HA_ERR_WRONG_COMMAND;
+ }
+ virtual int index_read_last(uchar *buf MY_ATTRIBUTE((unused)),
+ const uchar *key MY_ATTRIBUTE((unused)),
+ uint key_len MY_ATTRIBUTE((unused))) {
+ set_my_errno(HA_ERR_WRONG_COMMAND);
+ return HA_ERR_WRONG_COMMAND;
+ }
+
+ public:
+ /**
+ This method is similar to update_row, however the handler doesn't need
+ to execute the updates at this point in time. The handler can be certain
+ that another call to bulk_update_row will occur OR a call to
+ exec_bulk_update before the set of updates in this query is concluded.
+
+ Note: If HA_ERR_FOUND_DUPP_KEY is returned, the handler must read
+ all columns of the row so MySQL can create an error message. If
+ the columns required for the error message are not read, the error
+ message will contain garbage.
+
+ @param old_data Old record
+ @param new_data New record
+ @param dup_key_found Number of duplicate keys found
+
+ */
+ virtual int bulk_update_row(const uchar *old_data MY_ATTRIBUTE((unused)),
+ uchar *new_data MY_ATTRIBUTE((unused)),
+ uint *dup_key_found MY_ATTRIBUTE((unused))) {
+ DBUG_ASSERT(false);
+ return HA_ERR_WRONG_COMMAND;
+ }
+ /**
+ Delete all rows in a table.
+
+ This is called both for cases of truncate and for cases where the
+ optimizer realizes that all rows will be removed as a result of an
+ SQL statement.
+
+ If the handler don't support this, then this function will
+ return HA_ERR_WRONG_COMMAND and MySQL will delete the rows one
+ by one.
+ */
+ virtual int delete_all_rows() {
+ set_my_errno(HA_ERR_WRONG_COMMAND);
+ return HA_ERR_WRONG_COMMAND;
+ }
+ /**
+ Quickly remove all rows from a table.
+
+ @param[in,out] table_def dd::Table object for table being truncated.
+
+ @remark This method is responsible for implementing MySQL's TRUNCATE
+ TABLE statement, which is a DDL operation. As such, a engine
+ can bypass certain integrity checks and in some cases avoid
+ fine-grained locking (e.g. row locks) which would normally be
+ required for a DELETE statement.
+
+ @remark Typically, truncate is not used if it can result in integrity
+ violation. For example, truncate is not used when a foreign
+ key references the table, but it might be used if foreign key
+ checks are disabled.
+
+ @remark Engine is responsible for resetting the auto-increment counter.
+
+ @remark The table is locked in exclusive mode. All open TABLE/handler
+ instances except the one which is used for truncate() call
+ are closed.
+
+ @note It is assumed that transactional storage engines implementing
+ this method can revert its effects if transaction is rolled
+ back (e.g. because we failed to write statement to the binary
+ log).
+
+ @note Changes to dd::Table object done by this method will be saved
+ to data-dictionary only if storage engine supports atomic DDL
+ (i.e. has HTON_SUPPORTS_ATOMIC_DDL flag set).
+ */
+ virtual int truncate(dd::Table *table_def MY_ATTRIBUTE((unused))) {
+ return HA_ERR_WRONG_COMMAND;
+ }
+ virtual int optimize(THD *, HA_CHECK_OPT *) {
+ return HA_ADMIN_NOT_IMPLEMENTED;
+ }
+ virtual int analyze(THD *, HA_CHECK_OPT *) {
+ return HA_ADMIN_NOT_IMPLEMENTED;
+ }
+
+ /**
+ @brief Check and repair the table if necessary.
+
+ @param thd Thread object
+
+ @retval true Error/Not supported
+ @retval false Success
+
+ @note Called if open_table_from_share fails and is_crashed().
+ */
+
+ virtual bool check_and_repair(THD *thd MY_ATTRIBUTE((unused))) {
+ return true;
+ }
+
+ /**
+ Disable indexes for a while.
+
+ @param mode Mode.
+
+ @retval 0 Success.
+ @retval != 0 Error.
+ */
+
+ virtual int disable_indexes(uint mode MY_ATTRIBUTE((unused))) {
+ return HA_ERR_WRONG_COMMAND;
+ }
+
+ /**
+ Enable indexes again.
+
+ @param mode Mode.
+
+ @retval 0 Success.
+ @retval != 0 Error.
+ */
+
+ virtual int enable_indexes(uint mode MY_ATTRIBUTE((unused))) {
+ return HA_ERR_WRONG_COMMAND;
+ }
+
+ /**
+ Discard or import tablespace.
+
+ @param [in] discard Indicates whether this is discard operation.
+ @param [in,out] table_def dd::Table object describing the table
+ in which tablespace needs to be discarded
+ or imported. This object can be adjusted by
+ storage engine if it supports atomic DDL
+ (i.e. has HTON_SUPPORTS_ATOMIC_DDL flag set).
+ These changes will be persisted in the
+ data-dictionary.
+ @retval 0 Success.
+ @retval != 0 Error.
+ */
+
+ virtual int discard_or_import_tablespace(bool discard MY_ATTRIBUTE((unused)),
+ dd::Table *table_def
+ MY_ATTRIBUTE((unused))) {
+ set_my_errno(HA_ERR_WRONG_COMMAND);
+ return HA_ERR_WRONG_COMMAND;
+ }
+
+ virtual void drop_table(const char *name);
+
+ /**
+ Create table (implementation).
+
+ @param [in] name Table name.
+ @param [in] form TABLE object describing the table to be
+ created.
+ @param [in] info HA_CREATE_INFO describing table.
+ @param [in,out] table_def dd::Table object describing the table
+ to be created. This object can be
+ adjusted by storage engine if it
+ supports atomic DDL (i.e. has
+ HTON_SUPPORTS_ATOMIC_DDL flag set).
+ These changes will be persisted in the
+ data-dictionary. Can be NULL for
+ temporary tables created by optimizer.
+
+ @retval 0 Success.
+ @retval non-0 Error.
+ */
+ virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *info,
+ dd::Table *table_def) = 0;
+
+ virtual bool get_se_private_data(dd::Table *dd_table MY_ATTRIBUTE((unused)),
+ bool reset MY_ATTRIBUTE((unused))) {
+ return false;
+ }
+
+ /**
+ Adjust definition of table to be created by adding implicit columns
+ and indexes necessary for the storage engine.
+
+ @param [in] create_info HA_CREATE_INFO describing the table.
+ @param [in] create_list List of columns in the table.
+ @param [in] key_info Array of KEY objects describing table
+ indexes.
+ @param [in] key_count Number of indexes in the table.
+ @param [in,out] table_obj dd::Table object describing the table
+ to be created. Implicit columns and
+ indexes are to be added to this object.
+ Adjusted table description will be
+ saved into the data-dictionary.
+
+ @retval 0 Success.
+ @retval non-0 Error.
+ */
+ virtual int get_extra_columns_and_keys(
+ const HA_CREATE_INFO *create_info MY_ATTRIBUTE((unused)),
+ const List<Create_field> *create_list MY_ATTRIBUTE((unused)),
+ const KEY *key_info MY_ATTRIBUTE((unused)),
+ uint key_count MY_ATTRIBUTE((unused)),
+ dd::Table *table_obj MY_ATTRIBUTE((unused))) {
+ return 0;
+ }
+
+ virtual bool set_ha_share_ref(Handler_share **arg_ha_share) {
+ ha_share = arg_ha_share;
+ return false;
+ }
+ int get_lock_type() const { return m_lock_type; }
+
+ /**
+ Callback function that will be called by my_prepare_gcolumn_template
+ once the table has been opened.
+ */
+ typedef void (*my_gcolumn_template_callback_t)(const TABLE *, void *);
+ static bool my_prepare_gcolumn_template(THD *thd, const char *db_name,
+ const char *table_name,
+ my_gcolumn_template_callback_t myc,
+ void *ib_table);
+ static bool my_eval_gcolumn_expr_with_open(THD *thd, const char *db_name,
+ const char *table_name,
+ const MY_BITMAP *const fields,
+ uchar *record,
+ const char **mv_data_ptr,
+ ulong *mv_length);
+
+ /**
+ Callback for computing generated column values.
+
+ Storage engines that need to have virtual column values for a row
+ can use this function to get the values computed. The storage
+ engine must have filled in the values for the base columns that
+ the virtual columns depend on.
+
+ @param thd thread handle
+ @param table table object
+ @param fields bitmap of field index of evaluated generated
+ column
+ @param[in,out] record buff of base columns generated column depends.
+ After calling this function, it will be
+ used to return the value of the generated
+ columns.
+ @param[out] mv_data_ptr When given (not null) and the field
+ needs to be calculated is a typed array field, it
+ will contain pointer to field's calculated value.
+ @param[out] mv_length Length of the data above
+
+ @retval true in case of error
+ @retval false on success
+ */
+ static bool my_eval_gcolumn_expr(THD *thd, TABLE *table,
+ const MY_BITMAP *const fields, uchar *record,
+ const char **mv_data_ptr, ulong *mv_length);
+
+ /* This must be implemented if the handlerton's partition_flags() is set. */
+ virtual Partition_handler *get_partition_handler() { return NULL; }
+
+ /**
+ Set se_private_id and se_private_data during upgrade
+
+ @param thd Pointer of THD
+ @param dbname Database name
+ @param table_name Table name
+ @param dd_table dd::Table for the table
+ @param table_arg TABLE object for the table.
+
+ @return Operation status
+ @retval false Success
+ @retval true Error
+ */
+
+ bool ha_upgrade_table(THD *thd, const char *dbname, const char *table_name,
+ dd::Table *dd_table, TABLE *table_arg);
+
+ /**
+ Store a pointer to the handler of the primary table that
+ corresponds to the secondary table in this handler.
+ */
+ void ha_set_primary_handler(handler *primary_handler);
+
+ /**
+ Get a pointer to a handler for the table in the primary storage
+ engine, if this handler is for a table in a secondary storage
+ engine.
+ */
+ handler *ha_get_primary_handler() const { return m_primary_handler; }
+
+ /**
+ Return max limits for a single set of multi-valued keys
+
+ @param[out] num_keys number of keys to store
+ @param[out] keys_length total length of keys, bytes
+ */
+ void ha_mv_key_capacity(uint *num_keys, size_t *keys_length) const {
+ return mv_key_capacity(num_keys, keys_length);
+ }
+
+ private:
+ /**
+ Engine-specific function for ha_can_store_mv_keys().
+ Dummy function. SE's overloaded method is used instead.
+ */
+ /* purecov: begin inspected */
+ virtual void mv_key_capacity(uint *num_keys, size_t *keys_length) const {
+ *num_keys = 0;
+ *keys_length = 0;
+ }
+ /* purecov: end */
+
+ /**
+ Filter duplicate records when multi-valued index is used for retrieval
+
+ @returns
+ true duplicate, such row id was already seen
+ false row id is seen for the first time
+ */
+ bool filter_dup_records();
+
+ protected:
+ Handler_share *get_ha_share_ptr();
+ void set_ha_share_ptr(Handler_share *arg_ha_share);
+ void lock_shared_ha_data();
+ void unlock_shared_ha_data();
+
+ friend class DsMrr_impl;
+};
+
+/**
+ Function identifies any old data type present in table.
+
+ This function was handler::check_old_types().
+ Function is not part of SE API. It is now converted to
+ auxiliary standalone function.
+
+ @param[in] table TABLE object
+ @param[in] check_temporal_upgrade Check if temporal upgrade is needed
+
+ @retval 0 ON SUCCESS
+ @retval error code ON FAILURE
+*/
+
+int check_table_for_old_types(const TABLE *table, bool check_temporal_upgrade);
+
+/*
+ A Disk-Sweep MRR interface implementation
+
+ This implementation makes range (and, in the future, 'ref') scans to read
+ table rows in disk sweeps.
+
+ Currently it is used by MyISAM and InnoDB. Potentially it can be used with
+ any table handler that has non-clustered indexes and on-disk rows.
+*/
+
+class DsMrr_impl {
+ public:
+ DsMrr_impl(handler *owner) : h(owner), table(NULL), h2(NULL) {}
+
+ ~DsMrr_impl() {
+ /*
+ If ha_reset() has not been called then the h2 dialog might still
+ exist. This must be closed and deleted (this is the case for
+ internally created temporary tables).
+ */
+ if (h2) reset();
+ DBUG_ASSERT(h2 == NULL);
+ }
+
+ private:
+ /*
+ The "owner" handler object (the one that calls dsmrr_XXX functions.
+ It is used to retrieve full table rows by calling rnd_pos().
+ */
+ handler *const h;
+ TABLE *table; /* Always equal to h->table */
+
+ /* Secondary handler object. It is used for scanning the index */
+ handler *h2;
+
+ /* Buffer to store rowids, or (rowid, range_id) pairs */
+ uchar *rowids_buf;
+ uchar *rowids_buf_cur; /* Current position when reading/writing */
+ uchar *rowids_buf_last; /* When reading: end of used buffer space */
+ uchar *rowids_buf_end; /* End of the buffer */
+
+ bool dsmrr_eof; /* true <=> We have reached EOF when reading index tuples */
+
+ /* true <=> need range association, buffer holds {rowid, range_id} pairs */
+ bool is_mrr_assoc;
+
+ bool use_default_impl; /* true <=> shortcut all calls to default MRR impl */
+ public:
+ /**
+ Initialize the DsMrr_impl object.
+
+ This object is used for both doing default MRR scans and DS-MRR scans.
+ This function just initializes the object. To do a DS-MRR scan,
+ this must also be initialized by calling dsmrr_init().
+
+ @param table_arg pointer to the TABLE that owns the handler
+ */
+
+ void init(TABLE *table_arg) {
+ DBUG_ASSERT(table_arg != NULL);
+ table = table_arg;
+ }
+
+ int dsmrr_init(RANGE_SEQ_IF *seq_funcs, void *seq_init_param, uint n_ranges,
+ uint mode, HANDLER_BUFFER *buf);
+ void dsmrr_close();
+
+ /**
+ Resets the DS-MRR object to the state it had after being intialized.
+
+ If there is an open scan then this will be closed.
+
+ This function should be called by handler::ha_reset() which is called
+ when a statement is completed in order to make the handler object ready
+ for re-use by a different statement.
+ */
+
+ void reset();
+ int dsmrr_fill_buffer();
+ int dsmrr_next(char **range_info);
+
+ ha_rows dsmrr_info(uint keyno, uint n_ranges, uint keys, uint *bufsz,
+ uint *flags, Cost_estimate *cost);
+
+ ha_rows dsmrr_info_const(uint keyno, RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint *bufsz, uint *flags,
+ Cost_estimate *cost);
+
+ private:
+ bool choose_mrr_impl(uint keyno, ha_rows rows, uint *flags, uint *bufsz,
+ Cost_estimate *cost);
+ bool get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
+ uint *buffer_size, Cost_estimate *cost);
+};
+
+/* lookups */
+handlerton *ha_default_handlerton(THD *thd);
+handlerton *ha_default_temp_handlerton(THD *thd);
+/**
+ Resolve handlerton plugin by name, without checking for "DEFAULT" or
+ HTON_NOT_USER_SELECTABLE.
+
+ @param thd Thread context.
+ @param name Plugin name.
+
+ @return plugin or NULL if not found.
+*/
+plugin_ref ha_resolve_by_name_raw(THD *thd, const LEX_CSTRING &name);
+plugin_ref ha_resolve_by_name(THD *thd, const LEX_CSTRING *name,
+ bool is_temp_table);
+plugin_ref ha_lock_engine(THD *thd, const handlerton *hton);
+handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type);
+handler *get_new_handler(TABLE_SHARE *share, bool partitioned, MEM_ROOT *alloc,
+ handlerton *db_type);
+handlerton *ha_checktype(THD *thd, enum legacy_db_type database_type,
+ bool no_substitute, bool report_error);
+
+static inline enum legacy_db_type ha_legacy_type(const handlerton *db_type) {
+ return (db_type == NULL) ? DB_TYPE_UNKNOWN : db_type->db_type;
+}
+
+const char *ha_resolve_storage_engine_name(const handlerton *db_type);
+
+static inline bool ha_check_storage_engine_flag(const handlerton *db_type,
+ uint32 flag) {
+ return db_type == nullptr ? false : (db_type->flags & flag);
+}
+
+static inline bool ha_storage_engine_is_enabled(const handlerton *db_type) {
+ return (db_type && db_type->create) ? (db_type->state == SHOW_OPTION_YES)
+ : false;
+}
+
+/* basic stuff */
+int ha_init_errors(void);
+int ha_init(void);
+void ha_end();
+int ha_initialize_handlerton(st_plugin_int *plugin);
+int ha_finalize_handlerton(st_plugin_int *plugin);
+
+TYPELIB *ha_known_exts();
+int ha_panic(enum ha_panic_function flag);
+void ha_close_connection(THD *thd);
+void ha_kill_connection(THD *thd);
+/** Invoke handlerton::pre_dd_shutdown() on every storage engine plugin. */
+void ha_pre_dd_shutdown(void);
+
+/**
+ Flush the log(s) of storage engine(s).
+
+ @param binlog_group_flush true if we got invoked by binlog group
+ commit during flush stage, false in other cases.
+ @retval false Succeed
+ @retval true Error
+*/
+bool ha_flush_logs(bool binlog_group_flush = false);
+void ha_drop_database(char *path);
+int ha_create_table(THD *thd, const char *path, const char *db,
+ const char *table_name, HA_CREATE_INFO *create_info,
+ bool update_create_info, bool is_temp_table,
+ dd::Table *table_def);
+
+int ha_delete_table(THD *thd, handlerton *db_type, const char *path,
+ const char *db, const char *alias,
+ const dd::Table *table_def, bool generate_warning);
+bool ha_check_reserved_db_name(const char *name);
+
+/* statistics and info */
+bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat);
+
+typedef bool Log_func(THD *, TABLE *, bool, const uchar *, const uchar *);
+
+int binlog_log_row(TABLE *table, const uchar *before_record,
+ const uchar *after_record, Log_func *log_func);
+
+/* discovery */
+int ha_create_table_from_engine(THD *thd, const char *db, const char *name);
+bool ha_check_if_table_exists(THD *thd, const char *db, const char *name,
+ bool *exists);
+int ha_find_files(THD *thd, const char *db, const char *path, const char *wild,
+ bool dir, List<LEX_STRING> *files);
+int ha_table_exists_in_engine(THD *thd, const char *db, const char *name);
+bool ha_check_if_supported_system_table(handlerton *hton, const char *db,
+ const char *table_name);
+bool ha_rm_tmp_tables(THD *thd, List<LEX_STRING> *files);
+bool default_rm_tmp_tables(handlerton *hton, THD *thd, List<LEX_STRING> *files);
+
+/* key cache */
+extern "C" int ha_init_key_cache(const char *name, KEY_CACHE *key_cache);
+int ha_resize_key_cache(KEY_CACHE *key_cache);
+int ha_change_key_cache(KEY_CACHE *old_key_cache, KEY_CACHE *new_key_cache);
+
+/* transactions: interface to handlerton functions */
+int ha_start_consistent_snapshot(THD *thd);
+int ha_commit_trans(THD *thd, bool all, bool ignore_global_read_lock = false);
+int ha_commit_attachable(THD *thd);
+int ha_rollback_trans(THD *thd, bool all);
+int ha_prepare(THD *thd);
+
+/**
+ recover() step of xa.
+
+ @note
+ there are three modes of operation:
+ - automatic recover after a crash
+ in this case commit_list != 0, tc_heuristic_recover==TC_HEURISTIC_NOT_USED
+ all xids from commit_list are committed, others are rolled back
+ - manual (heuristic) recover
+ in this case commit_list==0, tc_heuristic_recover != TC_HEURISTIC_NOT_USED
+ DBA has explicitly specified that all prepared transactions should
+ be committed (or rolled back).
+ - no recovery (MySQL did not detect a crash)
+ in this case commit_list==0, tc_heuristic_recover == TC_HEURISTIC_NOT_USED
+ there should be no prepared transactions in this case.
+*/
+
+typedef ulonglong my_xid; // this line is the same as in log_event.h
+int ha_recover(const memroot_unordered_set<my_xid> *commit_list);
+
+/**
+ Perform SE-specific cleanup after recovery of transactions.
+
+ @note SE supporting atomic DDL can use this method to perform
+ post-DDL actions for DDL statements which were committed
+ or rolled back during recovery stage.
+*/
+void ha_post_recover();
+
+/*
+ transactions: interface to low-level handlerton functions. These are
+ intended to be used by the transaction coordinators to
+ commit/prepare/rollback transactions in the engines.
+*/
+int ha_commit_low(THD *thd, bool all, bool run_after_commit = true);
+int ha_prepare_low(THD *thd, bool all);
+int ha_rollback_low(THD *thd, bool all);
+
+/* transactions: these functions never call handlerton functions directly */
+int ha_enable_transaction(THD *thd, bool on);
+
+/* savepoints */
+int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv);
+bool ha_rollback_to_savepoint_can_release_mdl(THD *thd);
+int ha_savepoint(THD *thd, SAVEPOINT *sv);
+int ha_release_savepoint(THD *thd, SAVEPOINT *sv);
+
+/* Build pushed joins in handlers implementing this feature */
+int ha_make_pushed_joins(THD *thd, const AQP::Join_plan *plan);
+
+/* these are called by storage engines */
+void trans_register_ha(THD *thd, bool all, handlerton *ht,
+ const ulonglong *trxid);
+
+int ha_reset_logs(THD *thd);
+int ha_binlog_index_purge_file(THD *thd, const char *file);
+void ha_reset_slave(THD *thd);
+void ha_binlog_log_query(THD *thd, handlerton *db_type,
+ enum_binlog_command binlog_command, const char *query,
+ size_t query_length, const char *db,
+ const char *table_name);
+void ha_binlog_wait(THD *thd);
+
+/* It is required by basic binlog features on both MySQL server and libmysqld */
+int ha_binlog_end(THD *thd);
+
+const char *get_canonical_filename(handler *file, const char *path,
+ char *tmp_path);
+
+const char *table_case_name(const HA_CREATE_INFO *info, const char *name);
+
+void print_keydup_error(TABLE *table, KEY *key, const char *msg, myf errflag);
+void print_keydup_error(TABLE *table, KEY *key, myf errflag);
+
+void ha_set_normalized_disabled_se_str(const std::string &disabled_se_str);
+bool ha_is_storage_engine_disabled(handlerton *se_engine);
+
+bool ha_notify_exclusive_mdl(THD *thd, const MDL_key *mdl_key,
+ ha_notification_type notification_type,
+ bool *victimized);
+bool ha_notify_alter_table(THD *thd, const MDL_key *mdl_key,
+ ha_notification_type notification_type);
+
+int commit_owned_gtids(THD *thd, bool all, bool *need_clear_ptr);
+int commit_owned_gtid_by_partial_command(THD *thd);
+bool set_tx_isolation(THD *thd, enum_tx_isolation tx_isolation, bool one_shot);
+
+/** Generate a string representation of an `ha_rkey_function` enum value.
+ * @param[in] r value to turn into string
+ * @return a string, e.g. "HA_READ_KEY_EXACT" if r == HA_READ_KEY_EXACT */
+const char *ha_rkey_function_to_str(enum ha_rkey_function r);
+
+/** Generate a human readable string that describes a table structure. For
+ * example:
+ * t1 (`c1` char(60) not null, `c2` char(60), hash unique index0(`c1`, `c2`))
+ * @param[in] table_name name of the table to be described
+ * @param[in] mysql_table table structure
+ * @return a string similar to a CREATE TABLE statement */
+std::string table_definition(const char *table_name, const TABLE *mysql_table);
+
+#ifndef DBUG_OFF
+/** Generate a human readable string that describes the contents of a row. The
+ * row must be in the same format as provided to handler::write_row(). For
+ * example, given this table structure:
+ * t1 (`pk` int(11) not null,
+ * `col_int_key` int(11),
+ * `col_varchar_key` varchar(1),
+ * hash unique index0(`pk`, `col_int_key`, `col_varchar_key`))
+ *
+ * something like this will be generated (without the new lines):
+ *
+ * len=16,
+ * raw=..........c.....,
+ * hex=f9 1d 00 00 00 08 00 00 00 01 63 a5 a5 a5 a5 a5,
+ * human=(`pk`=29, `col_int_key`=8, `col_varchar_key`=c)
+ *
+ * @param[in] mysql_row row to dump
+ * @param[in] mysql_table table to which the row belongs, for querying metadata
+ * @return textual dump of the row */
+std::string row_to_string(const uchar *mysql_row, TABLE *mysql_table);
+
+/** Generate a human readable string that describes indexed cells that are given
+ * to handler::index_read() as input. The generated string is similar to the one
+ * generated by row_to_string(), but only contains the cells covered by the
+ * given index.
+ * @param[in] indexed_cells raw buffer in handler::index_read() input format
+ * @param[in] indexed_cells_len length of indexed_cells in bytes
+ * @param[in] mysql_index the index that covers the cells, for querying metadata
+ * @return textual dump of the cells */
+std::string indexed_cells_to_string(const uchar *indexed_cells,
+ uint indexed_cells_len,
+ const KEY &mysql_index);
+#endif /* DBUG_OFF */
+
+/*
+ This class is used by INFORMATION_SCHEMA.FILES to read SE specific
+ tablespace dynamic metadata. Some member like m_type and id, is not
+ really dynamic, but as this information is not stored in data dictionary
+ in a generic format and still is SE specific Some member like m_type and
+ id, is not really dynamic, but as this information is not stored in data
+ dictionary in a generic format and still needs SE specific decision, we
+ are requesting the same from SE.
+*/
+
+class ha_tablespace_statistics {
+ public:
+ ha_tablespace_statistics()
+ : m_id(0),
+ m_logfile_group_number(-1),
+ m_free_extents(0),
+ m_total_extents(0),
+ m_extent_size(0),
+ m_initial_size(0),
+ m_maximum_size(0),
+ m_autoextend_size(0),
+ m_version(-1),
+ m_data_free(0) {}
+
+ ulonglong m_id;
+ dd::String_type m_type;
+ dd::String_type m_logfile_group_name; // NDB only
+ ulonglong m_logfile_group_number; // NDB only
+ ulonglong m_free_extents;
+ ulonglong m_total_extents;
+ ulonglong m_extent_size;
+ ulonglong m_initial_size;
+ ulonglong m_maximum_size;
+ ulonglong m_autoextend_size;
+ ulonglong m_version; // NDB only
+ dd::String_type m_row_format; // NDB only
+ ulonglong m_data_free; // InnoDB
+ dd::String_type m_status;
+ dd::String_type m_extra; // NDB only
+};
+
+#endif /* HANDLER_INCLUDED */
diff --git a/contrib/libs/libmysql_r/sql/key.h b/contrib/libs/libmysql_r/sql/key.h
new file mode 100644
index 0000000000..f9f9581fef
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/key.h
@@ -0,0 +1,345 @@
+/* Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef KEY_INCLUDED
+#define KEY_INCLUDED
+
+#include <stddef.h>
+#include <sys/types.h>
+
+#include "lex_string.h"
+#include "my_base.h" /* ha_rows, ha_key_alg */
+#include "my_dbug.h"
+#include "my_inttypes.h"
+#include "sql/key_spec.h" /* fk_option */
+#include "sql/sql_plugin_ref.h" /* plugin_ref */
+
+class Field;
+class String;
+struct MY_BITMAP;
+struct TABLE;
+
+class FOREIGN_KEY {
+ public:
+ const char *name;
+ const char *unique_index_name;
+ uint key_parts;
+ LEX_CSTRING *key_part;
+ LEX_CSTRING *fk_key_part;
+ LEX_CSTRING ref_db;
+ LEX_CSTRING ref_table;
+ fk_option delete_opt;
+ fk_option update_opt;
+ fk_match_opt match_opt;
+};
+
+class KEY_PART_INFO { /* Info about a key part */
+ public:
+ Field *field;
+ uint offset; /* offset in record (from 0) */
+ uint null_offset; /* Offset to null_bit in record */
+ /* Length of key part in bytes, excluding NULL flag and length bytes */
+ uint16 length;
+ /*
+ Number of bytes required to store the keypart value. This may be
+ different from the "length" field as it also counts
+ - possible NULL-flag byte (see HA_KEY_NULL_LENGTH)
+ - possible HA_KEY_BLOB_LENGTH bytes needed to store actual value length.
+ */
+ uint16 store_length;
+ uint16 fieldnr; /* Fieldnum in UNIREG */
+ uint16 key_part_flag{0}; /* 0 or HA_REVERSE_SORT */
+ uint8 type;
+ uint8 null_bit{0}; /* Position to null_bit */
+ /**
+ True - if key part allows trivial binary comparison,
+ False - if charset collation function needs to be involved.
+
+ @note Not set for KEY_PART_INFO which are used for creating tables,
+ only set when table is opened or for internal temporary tables.
+
+ This value is set a bit too optimistically and disregards the way
+ in which value is stored in record (e.g. it is true for BLOB types).
+ So in practice key_cmp_if_same() also has to check key_part_flag for
+ presence of HA_BLOB_PART, HA_VAR_LENGTH_PART and HA_BIT_PART flags.
+ */
+ bool bin_cmp;
+ void init_from_field(Field *fld); /** Fill data from given field */
+ void init_flags(); /** Set key_part_flag from field */
+};
+
+/**
+ Data type for records per key estimates that are stored in the
+ KEY::rec_per_key_float[] array.
+*/
+typedef float rec_per_key_t;
+
+/**
+ If an entry for a key part in KEY::rec_per_key_float[] has this value,
+ then the storage engine has not provided a value for it and the rec_per_key
+ value for this key part is unknown.
+*/
+#define REC_PER_KEY_UNKNOWN -1.0f
+
+/**
+ If the "in memory estimate" for a table (in
+ ha_statistics.table_in_mem_estimate) or index (in
+ KEY::m_in_memory_estimate) is not known or not set by the storage
+ engine, then it should have the following value.
+*/
+#define IN_MEMORY_ESTIMATE_UNKNOWN -1.0
+
+class KEY {
+ public:
+ /** Tot length of key */
+ uint key_length;
+ /** dupp key and pack flags */
+ ulong flags;
+ /** dupp key and pack flags for actual key parts */
+ ulong actual_flags;
+ /** How many key_parts */
+ uint user_defined_key_parts;
+ /** How many key_parts including hidden parts */
+ uint actual_key_parts;
+ /**
+ Key parts allocated for primary key parts extension but
+ not used due to some reasons(no primary key, duplicated key parts)
+ */
+ uint unused_key_parts;
+ /** Should normally be = actual_key_parts */
+ uint usable_key_parts;
+ uint block_size;
+ enum ha_key_alg algorithm;
+ /**
+ A flag which indicates that index algorithm for this key was explicitly
+ specified by user. So, for example, it should be mentioned in SHOW CREATE
+ TABLE output.
+ */
+ bool is_algorithm_explicit;
+ /**
+ Note that parser is used when the table is opened for use, and
+ parser_name is used when the table is being created.
+ */
+ /** Fulltext [pre]parser */
+ plugin_ref parser;
+ /** Fulltext [pre]parser name */
+ LEX_CSTRING parser_name;
+
+ KEY_PART_INFO *key_part;
+ /** Name of key */
+ const char *name;
+
+ /**
+ Array of AVG(number of records with the same field value) for 1st ... Nth
+ key part. 0 means 'not known'. For internally created temporary tables this
+ member is NULL.
+ */
+ ulong *rec_per_key;
+
+ /**
+ @retval true if this is a functional index (at least one of the key parts
+ is a functional key part).
+ @retval false if this isn't a functional index.
+ */
+ bool is_functional_index() const;
+
+ private:
+ /**
+ Estimate for how much of the index data that is currently
+ available in a memory buffer. Valid range is [0..1]. This will be
+ initialized to a IN_MEMORY_ESTIMATE_UNKNOWN. If it still has this
+ value when used, it means that the storage engine has not supplied
+ a value.
+ */
+ double m_in_memory_estimate;
+
+ /**
+ Array of AVG(number of records with the same field value) for 1st ... Nth
+ key part. For internally created temporary tables this member is
+ NULL. This is the same information as stored in the above
+ rec_per_key array but using float values instead of integer
+ values. If the storage engine has supplied values in this array,
+ these will be used. Otherwise the value in rec_per_key will be
+ used. @todo In the next release the rec_per_key array above
+ should be removed and only this should be used.
+ */
+ rec_per_key_t *rec_per_key_float;
+
+ public:
+ /**
+ True if this index is visible to the query optimizer. The optimizer may
+ only use visible indexes.
+ */
+ bool is_visible;
+
+ TABLE *table;
+ LEX_CSTRING comment;
+
+ /**
+ Check if records per key estimate is available for given key part.
+
+ @param key_part_no key part number, must be in [0, KEY::actual_key_parts)
+
+ @return true if records per key estimate is available, false otherwise
+ */
+
+ bool has_records_per_key(uint key_part_no) const {
+ DBUG_ASSERT(key_part_no < actual_key_parts);
+
+ return ((rec_per_key_float &&
+ rec_per_key_float[key_part_no] != REC_PER_KEY_UNKNOWN) ||
+ (rec_per_key && rec_per_key[key_part_no] != 0));
+ }
+
+ /**
+ Retrieve an estimate for the average number of records per distinct value,
+ when looking only at the first key_part_no+1 columns.
+
+ If no record per key estimate is available for this key part,
+ REC_PER_KEY_UNKNOWN is returned.
+
+ @param key_part_no key part number, must be in [0, KEY::actual_key_parts)
+
+ @return Number of records having the same key value
+ @retval REC_PER_KEY_UNKNOWN no records per key estimate available
+ @retval != REC_PER_KEY_UNKNOWN record per key estimate
+ */
+
+ rec_per_key_t records_per_key(uint key_part_no) const {
+ DBUG_ASSERT(key_part_no < actual_key_parts);
+
+ /*
+ If the storage engine has provided rec per key estimates as float
+ then use this. If not, use the integer version.
+ */
+ if (rec_per_key_float[key_part_no] != REC_PER_KEY_UNKNOWN)
+ return rec_per_key_float[key_part_no];
+
+ return (rec_per_key[key_part_no] != 0)
+ ? static_cast<rec_per_key_t>(rec_per_key[key_part_no])
+ : REC_PER_KEY_UNKNOWN;
+ }
+
+ /**
+ Set the records per key estimate for a key part.
+
+ The records per key estimate must be in [1.0,..> or take the value
+ REC_PER_KEY_UNKNOWN.
+
+ @param key_part_no the number of key parts that the estimate includes,
+ must be in [0, KEY::actual_key_parts)
+ @param rec_per_key_est new records per key estimate
+ */
+
+ void set_records_per_key(uint key_part_no, rec_per_key_t rec_per_key_est) {
+ DBUG_ASSERT(key_part_no < actual_key_parts);
+ DBUG_ASSERT(rec_per_key_est == REC_PER_KEY_UNKNOWN ||
+ rec_per_key_est >= 1.0);
+ DBUG_ASSERT(rec_per_key_float != NULL);
+
+ rec_per_key_float[key_part_no] = rec_per_key_est;
+ }
+
+ /**
+ Check if this key supports storing records per key information.
+
+ @return true if it has support for storing records per key information,
+ false otherwise.
+ */
+
+ bool supports_records_per_key() const {
+ if (rec_per_key_float != NULL && rec_per_key != NULL) return true;
+
+ return false;
+ }
+
+ /**
+ Assign storage for the rec per key arrays to the KEY object.
+
+ This is used when allocating memory and creating KEY objects. The
+ caller is responsible for allocating the correct size for the
+ two arrays. If needed, the caller is also responsible for
+ de-allocating the memory when the KEY object is no longer used.
+
+ @param rec_per_key_arg pointer to allocated array for storing
+ records per key using ulong
+ @param rec_per_key_float_arg pointer to allocated array for storing
+ records per key using float
+ */
+
+ void set_rec_per_key_array(ulong *rec_per_key_arg,
+ rec_per_key_t *rec_per_key_float_arg) {
+ rec_per_key = rec_per_key_arg;
+ rec_per_key_float = rec_per_key_float_arg;
+ }
+
+ /**
+ Retrieve the estimate for how much of the index data that is available
+ in a memory buffer.
+
+ The returned estimate will be in the interval [0..1].
+
+ @return Estimate for how much of index data is available in memory buffer
+ @retval IN_MEMORY_ESTIMATE_UNKNOWN no estimate available
+ @retval != IN_MEMORY_ESTIMATE_UNKNOWN estimate
+ */
+
+ double in_memory_estimate() const {
+ DBUG_ASSERT(m_in_memory_estimate == IN_MEMORY_ESTIMATE_UNKNOWN ||
+ (m_in_memory_estimate >= 0.0 && m_in_memory_estimate <= 1.0));
+
+ return m_in_memory_estimate;
+ }
+
+ /**
+ Set the estimate for how much of this index that is currently in a
+ memory buffer.
+
+ The estimate must be in the interval [0..1] or take the value
+ IN_MEMORY_ESTIMATE_UNKNOWN.
+ */
+
+ void set_in_memory_estimate(double in_memory_estimate) {
+ DBUG_ASSERT(in_memory_estimate == IN_MEMORY_ESTIMATE_UNKNOWN ||
+ (in_memory_estimate >= 0.0 && in_memory_estimate <= 1.0));
+
+ m_in_memory_estimate = in_memory_estimate;
+ }
+};
+
+int find_ref_key(KEY *key, uint key_count, uchar *record, Field *field,
+ uint *key_length, uint *keypart);
+void key_copy(uchar *to_key, const uchar *from_record, const KEY *key_info,
+ uint key_length);
+void key_restore(uchar *to_record, const uchar *from_key, const KEY *key_info,
+ uint key_length);
+bool key_cmp_if_same(TABLE *form, const uchar *key, uint index,
+ uint key_length);
+void key_unpack(String *to, TABLE *table, KEY *key);
+void field_unpack(String *to, Field *field, uint max_length, bool prefix_key);
+bool is_key_used(TABLE *table, uint idx, const MY_BITMAP *fields);
+int key_cmp(KEY_PART_INFO *key_part, const uchar *key, uint key_length);
+int key_cmp2(KEY_PART_INFO *key_part, const uchar *key1, uint key1_length,
+ const uchar *key2, uint key2_length);
+int key_rec_cmp(KEY **key_info, uchar *a, uchar *b);
+
+#endif /* KEY_INCLUDED */
diff --git a/contrib/libs/libmysql_r/sql/key_spec.h b/contrib/libs/libmysql_r/sql/key_spec.h
new file mode 100644
index 0000000000..56dea9fc6f
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/key_spec.h
@@ -0,0 +1,302 @@
+/* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef KEY_SPEC_INCLUDED
+#define KEY_SPEC_INCLUDED
+
+#include <sys/types.h>
+
+#include "lex_string.h"
+#include "m_string.h"
+#include "my_base.h"
+#include "sql/mem_root_array.h"
+#include "sql/sql_list.h"
+
+class Create_field;
+class Item;
+class THD;
+struct MEM_ROOT;
+
+enum keytype {
+ KEYTYPE_PRIMARY,
+ KEYTYPE_UNIQUE,
+ KEYTYPE_MULTIPLE,
+ KEYTYPE_FULLTEXT,
+ KEYTYPE_SPATIAL,
+ KEYTYPE_FOREIGN
+};
+
+enum fk_option {
+ FK_OPTION_UNDEF,
+ FK_OPTION_RESTRICT,
+ FK_OPTION_CASCADE,
+ FK_OPTION_SET_NULL,
+ FK_OPTION_NO_ACTION,
+ FK_OPTION_DEFAULT
+};
+
+enum fk_match_opt {
+ FK_MATCH_UNDEF,
+ FK_MATCH_FULL,
+ FK_MATCH_PARTIAL,
+ FK_MATCH_SIMPLE
+};
+
+enum enum_order { ORDER_NOT_RELEVANT = 1, ORDER_ASC, ORDER_DESC };
+
+class KEY_CREATE_INFO {
+ public:
+ enum ha_key_alg algorithm = HA_KEY_ALG_SE_SPECIFIC;
+ /**
+ A flag which indicates that index algorithm was explicitly specified
+ by user.
+ */
+ bool is_algorithm_explicit = false;
+ ulong block_size = 0;
+ LEX_CSTRING parser_name = {NullS, 0};
+ LEX_CSTRING comment = {NullS, 0};
+ bool is_visible = true;
+
+ KEY_CREATE_INFO() = default;
+
+ explicit KEY_CREATE_INFO(bool is_visible_arg) : is_visible(is_visible_arg) {}
+};
+
+extern KEY_CREATE_INFO default_key_create_info;
+
+class Key_part_spec {
+ public:
+ Key_part_spec(Item *expression, enum_order order)
+ : m_is_ascending((order == ORDER_DESC) ? false : true),
+ m_is_explicit(order != ORDER_NOT_RELEVANT),
+ m_field_name(nullptr),
+ m_prefix_length(0),
+ m_expression(expression),
+ m_has_expression(true) {}
+
+ Key_part_spec(const char *column_name, Item *expression, enum_order order)
+ : m_is_ascending((order == ORDER_DESC) ? false : true),
+ m_is_explicit(order != ORDER_NOT_RELEVANT),
+ m_field_name(column_name),
+ m_prefix_length(0),
+ m_expression(expression),
+ m_has_expression(true) {}
+
+ Key_part_spec(LEX_CSTRING column_name, uint prefix_length, enum_order order)
+ : m_is_ascending((order == ORDER_DESC) ? false : true),
+ m_is_explicit(order != ORDER_NOT_RELEVANT),
+ m_field_name(column_name.str),
+ m_prefix_length(prefix_length),
+ m_expression(nullptr),
+ m_has_expression(false) {}
+
+ bool operator==(const Key_part_spec &other) const;
+ /**
+ Construct a copy of this Key_part_spec. field_name is copied
+ by-pointer as it is known to never change. At the same time
+ 'length' may be reset in mysql_prepare_create_table, and this
+ is why we supply it with a copy.
+
+ @return If out of memory, 0 is returned and an error is set in
+ THD.
+ */
+ Key_part_spec *clone(MEM_ROOT *mem_root) const {
+ return new (mem_root) Key_part_spec(*this);
+ }
+
+ const char *get_field_name() const { return m_field_name; }
+
+ uint get_prefix_length() const { return m_prefix_length; }
+
+ Item *get_expression() const {
+ DBUG_ASSERT(has_expression());
+ return m_expression;
+ }
+
+ /**
+ @retval true if this is an ascending index.
+ @retval false if this is a descending index.
+ */
+ bool is_ascending() const { return m_is_ascending; }
+
+ /**
+ @retval true if the user explicitly specified the index direction when
+ creating the index.
+ @retval false if the user didn't specify the index direction.
+ */
+ bool is_explicit() const { return m_is_explicit; }
+
+ /**
+ Resolve the expression that this key part contains. Should only be called
+ if has_expression() returns true.
+
+ @param thd thread handler.
+
+ @retval true if an error occurred.
+ @retval false on success.
+ */
+ bool resolve_expression(THD *thd);
+
+ /**
+ Set the name and the prefix length of the column this key part references.
+ The supplied column name string should have a lifetime equal to or longer
+ than this Key_part_spec
+
+ @param name the new column that this key part points to.
+  @param prefix_length the prefix length of the index, or 0 if no length is
+ specified.
+ */
+ void set_name_and_prefix_length(const char *name, uint prefix_length);
+
+ /**
+ @retval true if this index has an expression. In that case, this a
+ functional key part.
+ @retval false if this index doesn't have an expression. In that case this
+ key part references a normal column.
+ */
+ bool has_expression() const { return m_has_expression; }
+
+ private:
+ /// true <=> ascending, false <=> descending.
+ const bool m_is_ascending;
+
+ /// true <=> ASC/DESC is explicitly specified, false <=> implicit ASC
+ const bool m_is_explicit;
+
+ /// The name of the column that this key part points to.
+ const char *m_field_name;
+
+ /// The prefix length of this index.
+ uint m_prefix_length;
+
+ /**
+ The indexed expression if this is a functional key part. If this key part
+ points to a "normal" column, m_expression is nullptr.
+ */
+ Item *m_expression;
+
+ /**
+ Whether this key part has an expression or not. If so, this is a functional
+ key part.
+ */
+ bool m_has_expression;
+};
+
+class Key_spec {
+ public:
+ const keytype type;
+ const KEY_CREATE_INFO key_create_info;
+ Mem_root_array<Key_part_spec *> columns;
+ LEX_CSTRING name;
+ const bool generated;
+ /**
+ A flag to determine if we will check for duplicate indexes.
+ This typically means that the key information was specified
+ directly by the user (set by the parser) or a column
+ associated with it was dropped.
+ */
+ const bool check_for_duplicate_indexes;
+
+ Key_spec(MEM_ROOT *mem_root, keytype type_par, const LEX_CSTRING &name_arg,
+ const KEY_CREATE_INFO *key_info_arg, bool generated_arg,
+ bool check_for_duplicate_indexes_arg, List<Key_part_spec> &cols)
+ : type(type_par),
+ key_create_info(*key_info_arg),
+ columns(mem_root),
+ name(name_arg),
+ generated(generated_arg),
+ check_for_duplicate_indexes(check_for_duplicate_indexes_arg) {
+ columns.reserve(cols.elements);
+ List_iterator<Key_part_spec> it(cols);
+ Key_part_spec *column;
+ while ((column = it++)) columns.push_back(column);
+ }
+
+ virtual ~Key_spec() {}
+};
+
+class Foreign_key_spec : public Key_spec {
+ public:
+ const LEX_CSTRING ref_db;
+ const LEX_CSTRING orig_ref_db;
+ const LEX_CSTRING ref_table;
+ const LEX_CSTRING orig_ref_table;
+ Mem_root_array<Key_part_spec *> ref_columns;
+ const fk_option delete_opt;
+ const fk_option update_opt;
+ const fk_match_opt match_opt;
+
+ Foreign_key_spec(MEM_ROOT *mem_root, const LEX_CSTRING &name_arg,
+ List<Key_part_spec> cols, const LEX_CSTRING &ref_db_arg,
+ const LEX_CSTRING &orig_ref_db_arg,
+ const LEX_CSTRING &ref_table_arg,
+ const LEX_CSTRING &orig_ref_table_arg,
+ List<Key_part_spec> *ref_cols, fk_option delete_opt_arg,
+ fk_option update_opt_arg, fk_match_opt match_opt_arg)
+ : Key_spec(mem_root, KEYTYPE_FOREIGN, name_arg, &default_key_create_info,
+ false,
+ false, // We don't check for duplicate FKs.
+ cols),
+ ref_db(ref_db_arg),
+ orig_ref_db(orig_ref_db_arg),
+ ref_table(ref_table_arg),
+ orig_ref_table(orig_ref_table_arg),
+ ref_columns(mem_root),
+ delete_opt(delete_opt_arg),
+ update_opt(update_opt_arg),
+ match_opt(match_opt_arg) {
+ if (ref_cols) {
+ ref_columns.reserve(ref_cols->elements);
+ List_iterator<Key_part_spec> it(*ref_cols);
+ Key_part_spec *ref_column;
+ while ((ref_column = it++)) ref_columns.push_back(ref_column);
+ }
+ }
+
+ /**
+ Check if the foreign key name has valid length and its options
+ are compatible with columns on which the FK is created.
+
+ @param thd Thread handle
+ @param table_name Table name (for error reporting)
+ @param table_fields List of columns
+
+ @retval false Key valid
+ @retval true Key invalid
+ */
+ bool validate(THD *thd, const char *table_name,
+ List<Create_field> &table_fields) const;
+};
+
+/**
+ Test if a foreign key (= generated key) is a prefix of the given key
+ (ignoring key name, key type and order of columns)
+
+ @note This is only used to test if an index for a FOREIGN KEY exists.
+ We only compare field names.
+
+ @retval false Generated key is a prefix of other key
+ @retval true Not equal
+*/
+bool foreign_key_prefix(const Key_spec *a, const Key_spec *b);
+
+#endif // KEY_SPEC_INCLUDED
diff --git a/contrib/libs/libmysql_r/sql/lock.h b/contrib/libs/libmysql_r/sql/lock.h
new file mode 100644
index 0000000000..c4f1b3e6d4
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/lock.h
@@ -0,0 +1,69 @@
+/* Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef LOCK_INCLUDED
+#define LOCK_INCLUDED
+
+#include <stddef.h>
+#include <sys/types.h>
+#include <string>
+
+#include "map_helpers.h"
+#include "sql/mdl.h"
+
+class THD;
+// Forward declarations
+struct TABLE;
+struct THR_LOCK_DATA;
+
+struct MYSQL_LOCK {
+ TABLE **table;
+ uint table_count, lock_count;
+ THR_LOCK_DATA **locks;
+};
+
+MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, size_t count,
+ uint flags);
+void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
+void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
+void mysql_unlock_some_tables(THD *thd, TABLE **table, uint count);
+void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked, TABLE *table);
+void mysql_lock_abort_for_thread(THD *thd, TABLE *table);
+MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a, MYSQL_LOCK *b);
+/* Lock based on name */
+bool lock_schema_name(THD *thd, const char *db);
+
+// Hash set to hold set of tablespace names.
+typedef malloc_unordered_set<std::string> Tablespace_hash_set;
+
+// Lock tablespace names.
+bool lock_tablespace_names(THD *thd, Tablespace_hash_set *tablespace_set,
+ ulong lock_wait_timeout);
+
+/* Lock based on stored routine name */
+bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type,
+ const char *db, const char *name);
+
+/* Acquire protection against the global read lock. */
+bool acquire_shared_global_read_lock(THD *thd, unsigned long lock_wait_timeout);
+
+#endif /* LOCK_INCLUDED */
diff --git a/contrib/libs/libmysql_r/sql/log_event.h b/contrib/libs/libmysql_r/sql/log_event.h
new file mode 100644
index 0000000000..1945e010f7
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/log_event.h
@@ -0,0 +1,4197 @@
+/* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+ @file sql/log_event.h
+
+ @brief Binary log event definitions. This includes generic code
+ common to all types of log events, as well as specific code for each
+ type of log event.
+
+ @addtogroup Replication
+ @{
+*/
+
+#ifndef _log_event_h
+#define _log_event_h
+
+#include <atomic>
+#include <functional>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+
+#include "binlog_event.h"
+#include "control_events.h"
+#include "lex_string.h"
+#include "load_data_events.h"
+#include "m_string.h" // native_strncasecmp
+#include "my_bitmap.h" // MY_BITMAP
+#include "my_dbug.h"
+#include "my_inttypes.h"
+#include "my_psi_config.h"
+#include "my_sharedlib.h"
+#include "my_sys.h"
+#include "my_thread_local.h"
+#include "mysql/components/services/psi_stage_bits.h"
+#include "mysql/service_mysql_alloc.h"
+#include "mysql/udf_registration_types.h"
+#include "mysql_com.h" // SERVER_VERSION_LENGTH
+#include "partition_info.h"
+#include "rows_event.h"
+#include "sql/query_options.h" // OPTION_AUTO_IS_NULL
+#include "sql/rpl_gtid.h" // enum_gtid_type
+#include "sql/rpl_utility.h" // Hash_slave_rows
+#include "sql/sql_const.h"
+#include "sql/thr_malloc.h"
+#include "sql_string.h"
+#include "statement_events.h"
+#include "typelib.h" // TYPELIB
+#include "uuid.h"
+
+class THD;
+class Table_id;
+struct CHARSET_INFO;
+
+enum class enum_row_image_type;
+class Basic_ostream;
+
+#ifdef MYSQL_SERVER
+#include <stdio.h>
+
+#include "my_compiler.h"
+#include "sql/key.h"
+#error #include "sql/rpl_filter.h" // rpl_filter
+#error #include "sql/table.h"
+#error #include "sql/xa.h"
+#endif
+
+#ifndef MYSQL_SERVER
+#include "sql/rpl_tblmap.h" // table_mapping
+#endif
+
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+
+#ifdef HAVE_PSI_STAGE_INTERFACE
+#include "mysql/psi/mysql_stage.h"
+#endif
+
+#ifndef MYSQL_SERVER
+class Format_description_log_event;
+#endif
+
+extern PSI_memory_key key_memory_Incident_log_event_message;
+extern PSI_memory_key key_memory_Rows_query_log_event_rows_query;
+extern "C" MYSQL_PLUGIN_IMPORT ulong server_id;
+
+/* Forward declarations */
+using binary_log::Binary_log_event;
+using binary_log::checksum_crc32;
+using binary_log::enum_binlog_checksum_alg;
+using binary_log::Format_description_event;
+using binary_log::Log_event_footer;
+using binary_log::Log_event_header;
+using binary_log::Log_event_type;
+
+typedef ulonglong sql_mode_t;
+struct db_worker_hash_entry;
+
+extern "C" MYSQL_PLUGIN_IMPORT char server_version[SERVER_VERSION_LENGTH];
+#if defined(MYSQL_SERVER)
+int ignored_error_code(int err_code);
+#endif
+#define PREFIX_SQL_LOAD "SQL_LOAD-"
+
+/**
+ Maximum length of the name of a temporary file
+ PREFIX LENGTH - 9
+ UUID - UUID_LENGTH
+ SEPARATORS - 2
+ SERVER ID - 10 (range of server ID 1 to (2^32)-1 = 4,294,967,295)
+ FILE ID - 10 (uint)
+ EXTENSION - 7 (Assuming that the extension is always less than 7
+ characters)
+*/
+#define TEMP_FILE_MAX_LEN UUID_LENGTH + 38
+
+/**
+ Either assert or return an error.
+
+ In debug build, the condition will be checked, but in non-debug
+ builds, the error code given will be returned instead.
+
+ @param COND Condition to check
+ @param ERRNO Error number to return in non-debug builds
+*/
+#ifdef DBUG_OFF
+#define ASSERT_OR_RETURN_ERROR(COND, ERRNO) \
+ do { \
+ if (!(COND)) return ERRNO; \
+ } while (0)
+#else
+#define ASSERT_OR_RETURN_ERROR(COND, ERRNO) DBUG_ASSERT(COND)
+#endif
+
+#define LOG_EVENT_OFFSET 4
+
+#define NUM_LOAD_DELIM_STRS 5
+
+/*****************************************************************************
+
+ MySQL Binary Log
+
+ This log consists of events. Each event has a fixed-length header,
+ possibly followed by a variable length data body.
+
+ The data body consists of an optional fixed length segment (post-header)
+ and an optional variable length segment.
+
+ See the #defines below for the format specifics.
+
+ The events which really update data are Query_log_event,
+ Execute_load_query_log_event and old Load_log_event and
+ Execute_load_log_event events (Execute_load_query is used together with
+ Begin_load_query and Append_block events to replicate LOAD DATA INFILE.
+ Create_file/Append_block/Execute_load (which includes Load_log_event)
+ were used to replicate LOAD DATA before the 5.0.3).
+
+ ****************************************************************************/
+
+#define MAX_LOG_EVENT_HEADER \
+ ( /* in order of Query_log_event::write */ \
+ (LOG_EVENT_HEADER_LEN + /* write_header */ \
+ Binary_log_event::QUERY_HEADER_LEN + /* write_data */ \
+ Binary_log_event:: \
+ EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN) + /*write_post_header_for_derived \
+ */ \
+ MAX_SIZE_LOG_EVENT_STATUS + /* status */ \
+ NAME_LEN + \
+ 1)
+
+/*
+ The new option is added to handle large packets that are sent from the master
+ to the slave. It is used to increase the thd(max_allowed) for both the
+ DUMP thread on the master and the SQL/IO thread on the slave.
+*/
+#define MAX_MAX_ALLOWED_PACKET 1024 * 1024 * 1024
+
+/* slave event post-header (this event is never written) */
+
+#define SL_MASTER_PORT_OFFSET 8
+#define SL_MASTER_POS_OFFSET 0
+#define SL_MASTER_HOST_OFFSET 10
+
+/* Intvar event post-header */
+
+/* Intvar event data */
+#define I_TYPE_OFFSET 0
+#define I_VAL_OFFSET 1
+
+/* 4 bytes which all binlogs should begin with */
+#define BINLOG_MAGIC "\xfe\x62\x69\x6e"
+#define BINLOG_MAGIC_SIZE 4
+
+/**
+ @addtogroup group_cs_binglog_event_header_flags Binlog Event Header Flags
+ @ingroup group_cs
+ @{
+*/
+
+/*
+ The 2 flags below were useless :
+ - the first one was never set
+ - the second one was set in all Rotate events on the master, but not used for
+ anything useful.
+ So they are now removed and their place may later be reused for other
+ flags. Then one must remember that Rotate events in 4.x have
+ LOG_EVENT_FORCED_ROTATE_F set, so one should not rely on the value of the
+ replacing flag when reading a Rotate event.
+ I keep the defines here just to remember what they were.
+
+ #define LOG_EVENT_TIME_F 0x1
+ #define LOG_EVENT_FORCED_ROTATE_F 0x2
+*/
+
+/**
+ @def LOG_EVENT_THREAD_SPECIFIC_F
+
+ If the query depends on the thread (for example: TEMPORARY TABLE).
+ Currently this is used by mysqlbinlog to know it must print
+ SET @@PSEUDO_THREAD_ID=xx; before the query (it would not hurt to print it
+ for every query but this would be slow).
+*/
+#define LOG_EVENT_THREAD_SPECIFIC_F 0x4
+
+/**
+ @def LOG_EVENT_SUPPRESS_USE_F
+
+ Suppress the generation of 'USE' statements before the actual
+ statement. This flag should be set for any events that does not need
+ the current database set to function correctly. Most notable cases
+ are 'CREATE DATABASE' and 'DROP DATABASE'.
+
+ This flags should only be used in exceptional circumstances, since
+ it introduce a significant change in behaviour regarding the
+ replication logic together with the flags --binlog-do-db and
+ --replicated-do-db.
+ */
+#define LOG_EVENT_SUPPRESS_USE_F 0x8
+
+/*
+ Note: this is a place holder for the flag
+ LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F (0x10), which is not used any
+ more, please do not reused this value for other flags.
+ */
+
+/**
+ @def LOG_EVENT_ARTIFICIAL_F
+
+ Artificial events are created arbitarily and not written to binary
+ log
+
+ These events should not update the master log position when slave
+ SQL thread executes them.
+*/
+#define LOG_EVENT_ARTIFICIAL_F 0x20
+
+/**
+ @def LOG_EVENT_RELAY_LOG_F
+
+ Events with this flag set are created by slave IO thread and written
+ to relay log
+*/
+#define LOG_EVENT_RELAY_LOG_F 0x40
+
+/**
+ @def LOG_EVENT_IGNORABLE_F
+
+ For an event, 'e', carrying a type code, that a slave,
+ 's', does not recognize, 's' will check 'e' for
+ LOG_EVENT_IGNORABLE_F, and if the flag is set, then 'e'
+ is ignored. Otherwise, 's' acknowledges that it has
+ found an unknown event in the relay log.
+*/
+#define LOG_EVENT_IGNORABLE_F 0x80
+
+/**
+ @def LOG_EVENT_NO_FILTER_F
+
+ Events with this flag are not filtered (e.g. on the current
+ database) and are always written to the binary log regardless of
+ filters.
+*/
+#define LOG_EVENT_NO_FILTER_F 0x100
+
+/**
+ MTS: group of events can be marked to force its execution
+ in isolation from any other Workers.
+ So it's a marker for Coordinator to memorize and perform necessary
+ operations in order to guarantee no interference from other Workers.
+ The flag can be set ON only for an event that terminates its group.
+ Typically that is done for a transaction that contains
+ a query accessing more than OVER_MAX_DBS_IN_EVENT_MTS databases.
+*/
+#define LOG_EVENT_MTS_ISOLATE_F 0x200
+
+/** @}*/
+
+/**
+ @def OPTIONS_WRITTEN_TO_BIN_LOG
+
+ OPTIONS_WRITTEN_TO_BIN_LOG are the bits of thd->options which must
+ be written to the binlog. OPTIONS_WRITTEN_TO_BIN_LOG could be
+ written into the Format_description_log_event, so that if later we
+ don't want to replicate a variable we did replicate, or the
+ contrary, it's doable. But it should not be too hard to decide once
+ for all of what we replicate and what we don't, among the fixed 32
+ bits of thd->options.
+
+ I (Guilhem) have read through every option's usage, and it looks
+ like OPTION_AUTO_IS_NULL and OPTION_NO_FOREIGN_KEYS are the only
+ ones which alter how the query modifies the table. It's good to
+ replicate OPTION_RELAXED_UNIQUE_CHECKS too because otherwise, the
+ slave may insert data slower than the master, in InnoDB.
+ OPTION_BIG_SELECTS is not needed (the slave thread runs with
+ max_join_size=HA_POS_ERROR) and OPTION_BIG_TABLES is not needed
+ either, as the manual says (because a too big in-memory temp table
+ is automatically written to disk).
+*/
+#define OPTIONS_WRITTEN_TO_BIN_LOG \
+ (OPTION_AUTO_IS_NULL | OPTION_NO_FOREIGN_KEY_CHECKS | \
+ OPTION_RELAXED_UNIQUE_CHECKS | OPTION_NOT_AUTOCOMMIT)
+
+/* Shouldn't be defined before */
+#define EXPECTED_OPTIONS \
+ ((1ULL << 14) | (1ULL << 26) | (1ULL << 27) | (1ULL << 19))
+
+#if OPTIONS_WRITTEN_TO_BIN_LOG != EXPECTED_OPTIONS
+#error OPTIONS_WRITTEN_TO_BIN_LOG must NOT change their values!
+#endif
+#undef EXPECTED_OPTIONS /* You shouldn't use this one */
+
+/**
+ Maximum value of binlog logical timestamp.
+*/
+const int64 SEQ_MAX_TIMESTAMP = LLONG_MAX;
+
+/**
+ This method is used to extract the partition_id
+ from a partitioned table.
+
+ @param part_info an object of class partition_info it will be used
+ to call the methods responsible for returning the
+ value of partition_id
+
+ @retval The return value is the partition_id.
+
+*/
+int get_rpl_part_id(partition_info *part_info);
+
+#ifdef MYSQL_SERVER
+class Item;
+class Protocol;
+class Slave_reporting_capability;
+class Slave_worker;
+class sql_exchange;
+template <class T>
+class List;
+#endif
+
+class Relay_log_info;
+
+#ifndef MYSQL_SERVER
+enum enum_base64_output_mode {
+ BASE64_OUTPUT_NEVER = 0,
+ BASE64_OUTPUT_AUTO = 1,
+ BASE64_OUTPUT_UNSPEC = 2,
+ BASE64_OUTPUT_DECODE_ROWS = 3,
+ /* insert new output modes here */
+ BASE64_OUTPUT_MODE_COUNT
+};
+
+/*
+ A structure for mysqlbinlog to know how to print events
+
+ This structure is passed to the event's print() methods,
+
+ There are two types of settings stored here:
+ 1. Last db, flags2, sql_mode etc comes from the last printed event.
+ They are stored so that only the necessary USE and SET commands
+ are printed.
+ 2. Other information on how to print the events, e.g. short_form,
+ hexdump_from. These are not dependent on the last event.
+*/
+struct PRINT_EVENT_INFO {
+ /*
+ Settings for database, sql_mode etc that comes from the last event
+ that was printed. We cache these so that we don't have to print
+ them if they are unchanged.
+ */
+ // TODO: have the last catalog here ??
+ char db[FN_REFLEN + 1]; // TODO: make this a LEX_STRING when thd->db is
+ bool flags2_inited;
+ uint32 flags2;
+ bool sql_mode_inited;
+ sql_mode_t sql_mode; /* must be same as THD.variables.sql_mode */
+ ulong auto_increment_increment, auto_increment_offset;
+ bool charset_inited;
+ char charset[6]; // 3 variables, each of them storable in 2 bytes
+ char time_zone_str[MAX_TIME_ZONE_NAME_LENGTH];
+ uint lc_time_names_number;
+ uint charset_database_number;
+ uint default_collation_for_utf8mb4_number;
+ uint8_t sql_require_primary_key;
+ my_thread_id thread_id;
+ bool thread_id_printed;
+ uint8_t default_table_encryption;
+
+ PRINT_EVENT_INFO();
+
+ ~PRINT_EVENT_INFO() {
+ close_cached_file(&head_cache);
+ close_cached_file(&body_cache);
+ close_cached_file(&footer_cache);
+ }
+ bool init_ok() /* tells if construction was successful */
+ {
+ return my_b_inited(&head_cache) && my_b_inited(&body_cache) &&
+ my_b_inited(&footer_cache);
+ }
+
+ /* Settings on how to print the events */
+ // True if the --short-form flag was specified
+ bool short_form;
+ // The X in --base64-output=X
+ enum_base64_output_mode base64_output_mode;
+ // True if the --skip-gtids flag was specified.
+ bool skip_gtids;
+ /*
+ This is set whenever a Format_description_event is printed.
+ Later, when an event is printed in base64, this flag is tested: if
+ no Format_description_event has been seen, it is unsafe to print
+ the base64 event, so an error message is generated.
+ */
+ bool printed_fd_event;
+ my_off_t hexdump_from;
+ uint8 common_header_len;
+ char delimiter[16];
+
+ uint verbose;
+ table_mapping m_table_map;
+ table_mapping m_table_map_ignored;
+
+ /*
+ These three caches are used by the row-based replication events to
+ collect the header information and the main body of the events
+ making up a statement and in footer section any verbose related details
+ or comments related to the statment.
+ */
+ IO_CACHE head_cache;
+ IO_CACHE body_cache;
+ IO_CACHE footer_cache;
+ /* Indicate if the body cache has unflushed events */
+ bool have_unflushed_events;
+
+ /*
+ True if an event was skipped while printing the events of
+ a transaction and no COMMIT statement or XID event was ever
+ output (ie, was filtered out as well). This can be triggered
+ by the --database option of mysqlbinlog.
+
+ False, otherwise.
+ */
+ bool skipped_event_in_transaction;
+
+ bool print_table_metadata;
+};
+#endif
+
+/*
+ A specific to the database-scheduled MTS type.
+*/
+struct Mts_db_names {
+ const char *name[MAX_DBS_IN_EVENT_MTS];
+ int num;
+};
+
+/**
+ @class Log_event
+
+ This is the abstract base class for binary log events.
+
+ @section Log_event_binary_format Binary Format
+
+ The format of the event is described @ref Binary_log_event_format "here".
+
+ @subsection Log_event_format_of_atomic_primitives Format of Atomic Primitives
+
+ - All numbers, whether they are 16-, 24-, 32-, or 64-bit numbers,
+ are stored in little endian, i.e., the least significant byte first,
+ unless otherwise specified.
+
+*/
+class Log_event {
+ public:
+ /**
+ Enumeration of what kinds of skipping (and non-skipping) that can
+ occur when the slave executes an event.
+
+ @see shall_skip
+ @see do_shall_skip
+ */
+ enum enum_skip_reason {
+ /**
+ Don't skip event.
+ */
+ EVENT_SKIP_NOT,
+
+ /**
+ Skip event by ignoring it.
+
+ This means that the slave skip counter will not be changed.
+ */
+ EVENT_SKIP_IGNORE,
+
+ /**
+ Skip event and decrease skip counter.
+ */
+ EVENT_SKIP_COUNT
+ };
+
+ protected:
+ enum enum_event_cache_type {
+ EVENT_INVALID_CACHE = 0,
+ /*
+ If possible the event should use a non-transactional cache before
+ being flushed to the binary log. This means that it must be flushed
+ right after its correspondent statement is completed.
+ */
+ EVENT_STMT_CACHE,
+ /*
+ The event should use a transactional cache before being flushed to
+ the binary log. This means that it must be flushed upon commit or
+ rollback.
+ */
+ EVENT_TRANSACTIONAL_CACHE,
+ /*
+ The event must be written directly to the binary log without going
+ through any cache.
+ */
+ EVENT_NO_CACHE,
+ /*
+ If there is a need for different types, introduce them before this.
+ */
+ EVENT_CACHE_COUNT
+ };
+
+ enum enum_event_logging_type {
+ EVENT_INVALID_LOGGING = 0,
+ /*
+ The event must be written to a cache and upon commit or rollback
+ written to the binary log.
+ */
+ EVENT_NORMAL_LOGGING,
+ /*
+ The event must be written to an empty cache and immediatly written
+ to the binary log without waiting for any other event.
+ */
+ EVENT_IMMEDIATE_LOGGING,
+ /*
+ If there is a need for different types, introduce them before this.
+ */
+ EVENT_CACHE_LOGGING_COUNT
+ };
+
+ /**
+ Writes the common header of this event to the given memory buffer.
+
+ This does not update the checksum.
+
+ @note This has the following form:
+
+ +---------+---------+---------+------------+-----------+-------+
+ |timestamp|type code|server_id|event_length|end_log_pos|flags |
+ |4 bytes |1 byte |4 bytes |4 bytes |4 bytes |2 bytes|
+ +---------+---------+---------+------------+-----------+-------+
+
+ @param buf Memory buffer to write to. This must be at least
+ LOG_EVENT_HEADER_LEN bytes long.
+
+ @return The number of bytes written, i.e., always
+ LOG_EVENT_HEADER_LEN.
+ */
+ uint32 write_header_to_memory(uchar *buf);
+ /**
+ Writes the common-header of this event to the given output stream and
+ updates the checksum.
+
+ @param ostream The event will be written to this output stream.
+
+ @param data_length The length of the post-header section plus the
+ length of the data section; i.e., the length of the event minus
+ the common-header and the checksum.
+ */
+ bool write_header(Basic_ostream *ostream, size_t data_length);
+ bool write_footer(Basic_ostream *ostream);
+ bool need_checksum();
+
+ public:
+ /*
+ A temp buffer for read_log_event; it is later analysed according to the
+ event's type, and its content is distributed in the event-specific fields.
+ */
+ char *temp_buf;
+
+ /*
+ This variable determines whether the event is responsible for deallocating
+ the memory pointed by temp_buf. When set to true temp_buf is deallocated
+ and when it is set to false just make temp_buf point to NULL.
+ */
+ bool m_free_temp_buf_in_destructor;
+
+ /* The number of seconds the query took to run on the master. */
+ ulong exec_time;
+
+ /*
+ The master's server id (is preserved in the relay log; used to
+ prevent from infinite loops in circular replication).
+ */
+ uint32 server_id;
+
+ /**
+ A storage to cache the global system variable's value.
+ Handling of a separate event will be governed its member.
+ */
+ ulong rbr_exec_mode;
+
+ /**
+ Defines the type of the cache, if any, where the event will be
+ stored before being flushed to disk.
+ */
+ enum_event_cache_type event_cache_type;
+
+ /**
+ Defines when information, i.e. event or cache, will be flushed
+ to disk.
+ */
+ enum_event_logging_type event_logging_type;
+ /**
+ Placeholder for event checksum while writing to binlog.
+ */
+ ha_checksum crc;
+ /**
+ Index in @c rli->gaq array to indicate a group that this event is
+ purging. The index is set by Coordinator to a group terminator
+ event is checked by Worker at the event execution. The indexed
+ data represent the Worker progress status.
+ */
+ ulong mts_group_idx;
+
+ /**
+ The Log_event_header class contains the variable present
+ in the common header
+ */
+ binary_log::Log_event_header *common_header;
+
+ /**
+ The Log_event_footer class contains the variable present
+ in the common footer. Currently, footer contains only the checksum_alg.
+ */
+ binary_log::Log_event_footer *common_footer;
+ /**
+ MTS: associating the event with either an assigned Worker or Coordinator.
+ Additionally the member serves to tag deferred (IRU) events to avoid
+ the event regular time destruction.
+ */
+ Relay_log_info *worker;
+
+ /**
+ A copy of the main rli value stored into event to pass to MTS worker rli
+ */
+ ulonglong future_event_relay_log_pos;
+
+#ifdef MYSQL_SERVER
+ THD *thd;
+ /**
+ Partition info associate with event to deliver to MTS event applier
+ */
+ db_worker_hash_entry *mts_assigned_partitions[MAX_DBS_IN_EVENT_MTS];
+
+ Log_event(Log_event_header *header, Log_event_footer *footer,
+ enum_event_cache_type cache_type_arg,
+ enum_event_logging_type logging_type_arg);
+ Log_event(THD *thd_arg, uint16 flags_arg,
+ enum_event_cache_type cache_type_arg,
+ enum_event_logging_type logging_type_arg, Log_event_header *header,
+ Log_event_footer *footer);
+ /*
+ init_show_field_list() prepares the column names and types for the
+ output of SHOW BINLOG EVENTS; it is used only by SHOW BINLOG
+ EVENTS.
+ */
+ static void init_show_field_list(List<Item> *field_list);
+
+ int net_send(Protocol *protocol, const char *log_name, my_off_t pos);
+
+ /**
+ Stores a string representation of this event in the Protocol.
+ This is used by SHOW BINLOG EVENTS.
+
+ @retval 0 success
+ @retval nonzero error
+ */
+ virtual int pack_info(Protocol *protocol);
+
+ virtual const char *get_db();
+#else // ifdef MYSQL_SERVER
+ /* print*() functions are used by mysqlbinlog */
+ virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const = 0;
+ void print_timestamp(IO_CACHE *file, time_t *ts) const;
+ void print_header(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info,
+ bool is_more) const;
+ void print_base64(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info,
+ bool is_more) const;
+#endif // ifdef MYSQL_SERVER ... else
+
+ void *operator new(size_t size);
+
+ static void operator delete(void *ptr, size_t) { my_free(ptr); }
+
+ /* Placement version of the above operators */
+ static void *operator new(size_t, void *ptr) { return ptr; }
+ static void operator delete(void *, void *) {}
+ /**
+ Write the given buffer to the given output stream, updating the
+ checksum if checksums are enabled.
+
+ @param ostream The output stream to write to.
+ @param buf The buffer to write.
+ @param data_length The number of bytes to write.
+
+ @retval false Success.
+ @retval true Error.
+ */
+ bool wrapper_my_b_safe_write(Basic_ostream *ostream, const uchar *buf,
+ size_t data_length);
+
+#ifdef MYSQL_SERVER
+ virtual bool write(Basic_ostream *ostream) {
+ return (write_header(ostream, get_data_size()) ||
+ write_data_header(ostream) || write_data_body(ostream) ||
+ write_footer(ostream));
+ }
+
+ time_t get_time();
+
+ virtual bool write_data_header(Basic_ostream *) { return 0; }
+ virtual bool write_data_body(Basic_ostream *) { return 0; }
+#endif
+
+ Log_event_type get_type_code() const { return common_header->type_code; }
+
+ /**
+ Return true if the event has to be logged using SBR for DMLs.
+ */
+ virtual bool is_sbr_logging_format() const { return false; }
+ /**
+ Return true if the event has to be logged using RBR for DMLs.
+ */
+ virtual bool is_rbr_logging_format() const { return false; }
+
+ /*
+ is_valid is event specific sanity checks to determine that the
+ object is correctly initialized.
+ */
+ bool is_valid();
+ void set_artificial_event() {
+ common_header->flags |= LOG_EVENT_ARTIFICIAL_F;
+ /*
+ Artificial events are automatically generated and do not exist
+ in master's binary log, so log_pos should be set to 0.
+ */
+ common_header->log_pos = 0;
+ }
+ void set_relay_log_event() { common_header->flags |= LOG_EVENT_RELAY_LOG_F; }
+ bool is_artificial_event() const {
+ return common_header->flags & LOG_EVENT_ARTIFICIAL_F;
+ }
+ bool is_relay_log_event() const {
+ return common_header->flags & LOG_EVENT_RELAY_LOG_F;
+ }
+ bool is_ignorable_event() const {
+ return common_header->flags & LOG_EVENT_IGNORABLE_F;
+ }
+ bool is_no_filter_event() const {
+ return common_header->flags & LOG_EVENT_NO_FILTER_F;
+ }
+ inline bool is_using_trans_cache() const {
+ return (event_cache_type == EVENT_TRANSACTIONAL_CACHE);
+ }
+ inline bool is_using_stmt_cache() const {
+ return (event_cache_type == EVENT_STMT_CACHE);
+ }
+ inline bool is_using_immediate_logging() const {
+ return (event_logging_type == EVENT_IMMEDIATE_LOGGING);
+ }
+
+ /*
+ For the events being decoded in BAPI, common_header should
+ point to the header object which is contained within the class
+ Binary_log_event.
+ */
+ Log_event(Log_event_header *header, Log_event_footer *footer);
+
+ virtual ~Log_event() { free_temp_buf(); }
+ void register_temp_buf(char *buf, bool free_in_destructor = true) {
+ m_free_temp_buf_in_destructor = free_in_destructor;
+ temp_buf = buf;
+ }
+ void free_temp_buf() {
+ if (temp_buf) {
+ if (m_free_temp_buf_in_destructor) my_free(temp_buf);
+ temp_buf = nullptr;
+ }
+ }
+ /*
+ Get event length for simple events. For complicated events the length
+ is calculated during write()
+ */
+ virtual size_t get_data_size() { return 0; }
+ /**
+ Returns the human readable name of the given event type.
+ */
+ static const char *get_type_str(Log_event_type type);
+ /**
+ Returns the human readable name of this event's type.
+ */
+ const char *get_type_str() const;
+ /* Return start of query time or current time */
+
+#if defined(MYSQL_SERVER)
+ /**
+ Is called from get_mts_execution_mode() to
+
+ @return true if the event needs applying with synchronization
+ agaist Workers, otherwise
+ false
+
+ @note There are incompatile combinations such as referred further events
+ are wrapped with BEGIN/COMMIT. Such cases should be identified
+ by the caller and treats correspondingly.
+
+ todo: to mts-support Old master Load-data related events
+ */
+ bool is_mts_sequential_exec() {
+ switch (get_type_code()) {
+ case binary_log::STOP_EVENT:
+ case binary_log::ROTATE_EVENT:
+ case binary_log::SLAVE_EVENT:
+ case binary_log::FORMAT_DESCRIPTION_EVENT:
+ case binary_log::INCIDENT_EVENT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private:
+ /*
+ possible decisions by get_mts_execution_mode().
+ The execution mode can be PARALLEL or not (thereby sequential
+ unless impossible at all). When it's sequential it further breaks into
+ ASYNChronous and SYNChronous.
+ */
+ enum enum_mts_event_exec_mode {
+ /*
+ Event is run by a Worker.
+ */
+ EVENT_EXEC_PARALLEL,
+ /*
+ Event is run by Coordinator.
+ */
+ EVENT_EXEC_ASYNC,
+ /*
+ Event is run by Coordinator and requires synchronization with Workers.
+ */
+ EVENT_EXEC_SYNC,
+ /*
+ Event can't be executed neither by Workers nor Coordinator.
+ */
+ EVENT_EXEC_CAN_NOT
+ };
+
+ /**
+ MTS Coordinator finds out a way how to execute the current event.
+
+ Besides the parallelizable case, some events have to be applied by
+ Coordinator concurrently with Workers and some to require synchronization
+ with Workers (@c see wait_for_workers_to_finish) before to apply them.
+
+ @param mts_in_group the being group parsing status, true
+ means inside the group
+
+ @retval EVENT_EXEC_PARALLEL if event is executed by a Worker
+ @retval EVENT_EXEC_ASYNC if event is executed by Coordinator
+ @retval EVENT_EXEC_SYNC if event is executed by Coordinator
+ with synchronization against the Workers
+ */
+ enum enum_mts_event_exec_mode get_mts_execution_mode(bool mts_in_group) {
+ /*
+ Slave workers are unable to handle Format_description_log_event,
+ Rotate_log_event and Previous_gtids_log_event correctly.
+ However, when a transaction spans multiple relay logs, these
+ events occur in the middle of a transaction. The way we handle
+ this is by marking the events as 'ASYNC', meaning that the
+ coordinator thread will handle the events without stopping the
+ worker threads.
+
+ @todo Refactor this: make Log_event::get_slave_worker handle
+ transaction boundaries in a more robust way, so that it is able
+ to process Format_description_log_event, Rotate_log_event, and
+ Previous_gtids_log_event. Then, when these events occur in the
+ middle of a transaction, make them part of the transaction so
+ that the worker that handles the transaction handles these
+ events too. /Sven
+ */
+ if (
+ /*
+ When a Format_description_log_event occurs in the middle of
+ a transaction, it either has the slave's server_id, or has
+ end_log_pos==0.
+
+ @todo This does not work when master and slave have the same
+ server_id and replicate-same-server-id is enabled, since
+ events that are not in the middle of a transaction will be
+ executed in ASYNC mode in that case.
+ */
+ (get_type_code() == binary_log::FORMAT_DESCRIPTION_EVENT &&
+ ((server_id == (uint32)::server_id) ||
+ (common_header->log_pos == 0))) ||
+ /*
+ All Previous_gtids_log_events in the relay log are generated
+ by the slave. They don't have any meaning to the applier, so
+ they can always be ignored by the applier. So we can process
+ them asynchronously by the coordinator. It is also important
+ to not feed them to workers because that confuses
+ get_slave_worker.
+ */
+ (get_type_code() == binary_log::PREVIOUS_GTIDS_LOG_EVENT) ||
+ /*
+ Rotate_log_event can occur in the middle of a transaction.
+ When this happens, either it is a Rotate event generated on
+ the slave which has the slave's server_id, or it is a Rotate
+ event that originates from a master but has end_log_pos==0.
+ */
+ (get_type_code() == binary_log::ROTATE_EVENT &&
+ ((server_id == (uint32)::server_id) ||
+ (common_header->log_pos == 0 && mts_in_group))))
+ return EVENT_EXEC_ASYNC;
+ else if (is_mts_sequential_exec())
+ return EVENT_EXEC_SYNC;
+ else
+ return EVENT_EXEC_PARALLEL;
+ }
+
+ /**
+ @return index in [0, M] range to indicate
+ to be assigned worker;
+ M is the max index of the worker pool.
+ */
+ Slave_worker *get_slave_worker(Relay_log_info *rli);
+
+ /*
+ Group of events can be marked to force its execution
+ in isolation from any other Workers.
+ Typically that is done for a transaction that contains
+ a query accessing more than OVER_MAX_DBS_IN_EVENT_MTS databases.
+ Factually that's a sequential mode where a Worker remains to
+ be the applier.
+ */
+ virtual void set_mts_isolate_group() {
+ DBUG_ASSERT(ends_group() || get_type_code() == binary_log::QUERY_EVENT ||
+ get_type_code() == binary_log::EXECUTE_LOAD_QUERY_EVENT);
+ common_header->flags |= LOG_EVENT_MTS_ISOLATE_F;
+ }
+
+ public:
+ /**
+ The method fills in pointers to event's database name c-strings
+ to a supplied array.
+ In other than Query-log-event case the returned array contains
+ just one item.
+ @param[out] arg pointer to a struct containing char* array
+ pointers to be filled in and the number
+ of filled instances.
+ @param rpl_filter pointer to a replication filter.
+
+ @return number of the filled intances indicating how many
+ databases the event accesses.
+ */
+ virtual uint8 get_mts_dbs(Mts_db_names *arg,
+ Rpl_filter *rpl_filter MY_ATTRIBUTE((unused))) {
+ arg->name[0] = get_db();
+
+ return arg->num = mts_number_dbs();
+ }
+
+ /**
+ @return true if events carries partitioning data (database names).
+ */
+ bool contains_partition_info(bool);
+
+ /*
+ @return the number of updated by the event databases.
+
+ @note In other than Query-log-event case that's one.
+ */
+ virtual uint8 mts_number_dbs() { return 1; }
+
+ /**
+ @return true if the terminal event of a group is marked to
+ execute in isolation from other Workers,
+ false otherwise
+ */
+ bool is_mts_group_isolated() {
+ return common_header->flags & LOG_EVENT_MTS_ISOLATE_F;
+ }
+
+ /**
+ Events of a certain type can start or end a group of events treated
+ transactionally wrt binlog.
+
+ Public access is required by implementation of recovery + skip.
+
+ @return true if the event starts a group (transaction)
+ false otherwise
+ */
+#endif
+ virtual bool starts_group() const { return false; }
+ /**
+ @return true if the event ends a group (transaction)
+ false otherwise
+ */
+ virtual bool ends_group() const { return false; }
+#ifdef MYSQL_SERVER
+ /**
+ Apply the event to the database.
+
+ This function represents the public interface for applying an
+ event.
+
+ @see do_apply_event
+ */
+ int apply_event(Relay_log_info *rli);
+
+ /**
+ Apply the GTID event in curr_group_data to the database.
+
+ @param rli Pointer to coordinato's relay log info.
+
+ @retval 0 success
+ @retval 1 error
+ */
+ inline int apply_gtid_event(Relay_log_info *rli);
+
+ /**
+ Update the relay log position.
+
+ This function represents the public interface for "stepping over"
+ the event and will update the relay log information.
+
+ @see do_update_pos
+ */
+ int update_pos(Relay_log_info *rli) { return do_update_pos(rli); }
+
+ /**
+ Decide if the event shall be skipped, and the reason for skipping
+ it.
+
+ @see do_shall_skip
+ */
+ enum_skip_reason shall_skip(Relay_log_info *rli) {
+ DBUG_ENTER("Log_event::shall_skip");
+ enum_skip_reason ret = do_shall_skip(rli);
+ DBUG_PRINT("info", ("skip reason=%d=%s", ret,
+ ret == EVENT_SKIP_NOT
+ ? "NOT"
+ : ret == EVENT_SKIP_IGNORE ? "IGNORE" : "COUNT"));
+ DBUG_RETURN(ret);
+ }
+
+ /**
+ Primitive to apply an event to the database.
+
+ This is where the change to the database is made.
+
+ @note The primitive is protected instead of private, since there
+ is a hierarchy of actions to be performed in some cases.
+
+ @see Format_description_log_event::do_apply_event()
+
+ @param rli Pointer to relay log info structure
+
+ @retval 0 Event applied successfully
+ @retval errno Error code if event application failed
+ */
+ virtual int do_apply_event(Relay_log_info const *rli MY_ATTRIBUTE((unused))) {
+ return 0; /* Default implementation does nothing */
+ }
+
+ virtual int do_apply_event_worker(Slave_worker *w);
+
+ protected:
+ /**
+ Helper function to ignore an event w.r.t. the slave skip counter.
+
+ This function can be used inside do_shall_skip() for functions
+ that cannot end a group. If the slave skip counter is 1 when
+ seeing such an event, the event shall be ignored, the counter
+ left intact, and processing continue with the next event.
+
+ A typical usage is:
+ @code
+ enum_skip_reason do_shall_skip(Relay_log_info *rli) {
+ return continue_group(rli);
+ }
+ @endcode
+
+ @return Skip reason
+ */
+ enum_skip_reason continue_group(Relay_log_info *rli);
+
+ /**
+ Advance relay log coordinates.
+
+ This function is called to advance the relay log coordinates to
+ just after the event. It is essential that both the relay log
+ coordinate and the group log position is updated correctly, since
+ this function is used also for skipping events.
+
+ Normally, each implementation of do_update_pos() shall:
+
+ - Update the event position to refer to the position just after
+ the event.
+
+ - Update the group log position to refer to the position just
+ after the event <em>if the event is last in a group</em>
+
+ @param rli Pointer to relay log info structure
+
+ @retval 0 Coordinates changed successfully
+ @retval errno Error code if advancing failed (usually just
+ 1). Observe that handler errors are returned by the
+ do_apply_event() function, and not by this one.
+ */
+ virtual int do_update_pos(Relay_log_info *rli);
+
+ /**
+ Decide if this event shall be skipped or not and the reason for
+ skipping it.
+
+ The default implementation decide that the event shall be skipped
+ if either:
+
+ - the server id of the event is the same as the server id of the
+ server and <code>rli->replicate_same_server_id</code> is true,
+ or
+
+ - if <code>rli->slave_skip_counter</code> is greater than zero.
+
+ @see do_apply_event
+ @see do_update_pos
+
+ @retval Log_event::EVENT_SKIP_NOT
+ The event shall not be skipped and should be applied.
+
+ @retval Log_event::EVENT_SKIP_IGNORE
+ The event shall be skipped by just ignoring it, i.e., the slave
+ skip counter shall not be changed. This happends if, for example,
+ the originating server id of the event is the same as the server
+ id of the slave.
+
+ @retval Log_event::EVENT_SKIP_COUNT
+ The event shall be skipped because the slave skip counter was
+ non-zero. The caller shall decrease the counter by one.
+ */
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+#endif
+};
+
+/*
+ One class for each type of event.
+ Two constructors for each class:
+ - one to create the event for logging (when the server acts as a master),
+ called after an update to the database is done,
+ which accepts parameters like the query, the database, the options for LOAD
+ DATA INFILE...
+ - one to create the event from a packet (when the server acts as a slave),
+ called before reproducing the update, which accepts parameters (like a
+ buffer). Used to read from the master, from the relay log, and in
+ mysqlbinlog. This constructor must be format-tolerant.
+*/
+
+/**
+ A Query event is written to the binary log whenever the database is
+ modified on the master, unless row based logging is used.
+
+ Query_log_event is created for logging, and is called after an update to the
+ database is done. It is used when the server acts as the master.
+
+ Virtual inheritance is required here to handle the diamond problem in
+ the class @c Execute_load_query_log_event.
+ The diamond structure is explained in @c Excecute_load_query_log_event
+
+ @internal
+ The inheritance structure is as follows:
+
+ Binary_log_event
+ ^
+ |
+ |
+ Query_event Log_event
+ \ /
+ <<virtual>>\ /
+ \ /
+ Query_log_event
+ @endinternal
+*/
+class Query_log_event : public virtual binary_log::Query_event,
+ public Log_event {
+ protected:
+ Log_event_header::Byte *data_buf;
+
+ public:
+ /*
+ For events created by Query_log_event::do_apply_event (and
+ Load_log_event::do_apply_event()) we need the *original* thread
+ id, to be able to log the event with the original (=master's)
+ thread id (fix for BUG#1686).
+ */
+ my_thread_id slave_proxy_id;
+
+ /**
+ True if this is a ROLLBACK event injected by the mts coordinator to finish a
+ group corresponding to a partial transaction in the relay log.
+ False otherwise and by default, as it must be explicitly set to true by the
+ coordinator.
+ */
+ bool rollback_injected_by_coord = false;
+
+ /**
+ The flag indicates whether the DDL query has been (already)
+ committed or not. It's initialized as OFF at the event instantiation,
+ flips ON when the DDL transaction has been committed with
+ all its possible extra statement due to replication or GTID.
+
+ The flag status is also checked in few places to catch uncommitted
+ transactions which can normally happen due to filtering out. In
+ such a case the commit is deferred to @c Log_event::do_update_pos().
+ */
+ bool has_ddl_committed;
+
+#ifdef MYSQL_SERVER
+
+ Query_log_event(THD *thd_arg, const char *query_arg, size_t query_length,
+ bool using_trans, bool immediate, bool suppress_use,
+ int error, bool ignore_command = false);
+ const char *get_db() override { return db; }
+
+ /**
+ @param[out] arg pointer to a struct containing char* array
+ pointers be filled in and the number of
+ filled instances.
+ In case the number exceeds MAX_DBS_IN_EVENT_MTS,
+ the overfill is indicated with assigning the number to
+ OVER_MAX_DBS_IN_EVENT_MTS.
+ @param rpl_filter pointer to a replication filter.
+
+ @return number of databases in the array or OVER_MAX_DBS_IN_EVENT_MTS.
+ */
+ virtual uint8 get_mts_dbs(Mts_db_names *arg,
+ Rpl_filter *rpl_filter) override {
+ if (mts_accessed_dbs == OVER_MAX_DBS_IN_EVENT_MTS) {
+ // the empty string db name is special to indicate sequential applying
+ mts_accessed_db_names[0][0] = 0;
+ } else {
+ for (uchar i = 0; i < mts_accessed_dbs; i++) {
+ const char *db_name = mts_accessed_db_names[i];
+
+ // Only default database is rewritten.
+ if (!rpl_filter->is_rewrite_empty() && !strcmp(get_db(), db_name)) {
+ size_t dummy_len;
+ const char *db_filtered =
+ rpl_filter->get_rewrite_db(db_name, &dummy_len);
+ // db_name != db_filtered means that db_name is rewritten.
+ if (strcmp(db_name, db_filtered)) db_name = db_filtered;
+ }
+ arg->name[i] = db_name;
+ }
+ }
+ return arg->num = mts_accessed_dbs;
+ }
+
+ void attach_temp_tables_worker(THD *, const Relay_log_info *);
+ void detach_temp_tables_worker(THD *, const Relay_log_info *);
+
+ virtual uchar mts_number_dbs() override { return mts_accessed_dbs; }
+
+ int pack_info(Protocol *protocol) override;
+#else
+ void print_query_header(IO_CACHE *file,
+ PRINT_EVENT_INFO *print_event_info) const;
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+ static bool rewrite_db_in_buffer(char **buf, ulong *event_len,
+ const Format_description_event &fde);
+#endif
+
+ Query_log_event();
+
+ Query_log_event(const char *buf,
+ const Format_description_event *description_event,
+ Log_event_type event_type);
+ ~Query_log_event() override {
+ if (data_buf) my_free(data_buf);
+ }
+#ifdef MYSQL_SERVER
+ bool write(Basic_ostream *ostream) override;
+ virtual bool write_post_header_for_derived(Basic_ostream *) { return false; }
+#endif
+
+ /*
+ Returns number of bytes additionally written to post header by derived
+ events (so far it is only Execute_load_query event).
+ */
+ virtual ulong get_post_header_size_for_derived() { return 0; }
+ /* Writes derived event-specific part of post header. */
+
+ public: /* !!! Public in this patch to allow old usage */
+#if defined(MYSQL_SERVER)
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli) override;
+ virtual int do_apply_event(Relay_log_info const *rli) override;
+ virtual int do_update_pos(Relay_log_info *rli) override;
+
+ int do_apply_event(Relay_log_info const *rli, const char *query_arg,
+ size_t q_len_arg);
+#endif /* MYSQL_SERVER */
+ /*
+ If true, the event always be applied by slave SQL thread or be printed by
+ mysqlbinlog
+ */
+ bool is_trans_keyword() const {
+ /*
+ Before the patch for bug#50407, The 'SAVEPOINT and ROLLBACK TO'
+ queries input by user was written into log events directly.
+ So the keywords can be written in both upper case and lower case
+ together, strncasecmp is used to check both cases. they also could be
+ binlogged with comments in the front of these keywords. for examples:
+ / * bla bla * / SAVEPOINT a;
+ / * bla bla * / ROLLBACK TO a;
+ but we don't handle these cases and after the patch, both quiries are
+ binlogged in upper case with no comments.
+ */
+ return !strncmp(query, "BEGIN", q_len) ||
+ !strncmp(query, "COMMIT", q_len) ||
+ !native_strncasecmp(query, "SAVEPOINT", 9) ||
+ !native_strncasecmp(query, "ROLLBACK", 8) ||
+ !native_strncasecmp(query, STRING_WITH_LEN("XA START")) ||
+ !native_strncasecmp(query, STRING_WITH_LEN("XA END")) ||
+ !native_strncasecmp(query, STRING_WITH_LEN("XA PREPARE")) ||
+ !native_strncasecmp(query, STRING_WITH_LEN("XA COMMIT")) ||
+ !native_strncasecmp(query, STRING_WITH_LEN("XA ROLLBACK"));
+ }
+
+ /**
+ When a query log event contains a non-transaction control statement, we
+ assume that it is changing database content (DML) and was logged using
+ binlog_format=statement.
+
+ @return True the event represents a statement that was logged using SBR
+ that can change database content.
+ False for transaction control statements.
+ */
+ bool is_sbr_logging_format() const override { return !is_trans_keyword(); }
+
+ /**
+ Notice, DDL queries are logged without BEGIN/COMMIT parentheses
+ and identification of such single-query group
+ occures within logics of @c get_slave_worker().
+ */
+
+ bool starts_group() const override {
+ return !strncmp(query, "BEGIN", q_len) ||
+ !strncmp(query, STRING_WITH_LEN("XA START"));
+ }
+
+ virtual bool ends_group() const override {
+ return !strncmp(query, "COMMIT", q_len) ||
+ (!native_strncasecmp(query, STRING_WITH_LEN("ROLLBACK")) &&
+ native_strncasecmp(query, STRING_WITH_LEN("ROLLBACK TO "))) ||
+ !strncmp(query, STRING_WITH_LEN("XA ROLLBACK"));
+ }
+ static size_t get_query(const char *buf, size_t length,
+ const Format_description_event *fd_event,
+ const char **query_arg);
+
+ bool is_query_prefix_match(const char *pattern, uint p_len) {
+ return !strncmp(query, pattern, p_len);
+ }
+
+ private:
+ /** Whether or not the statement represented by this event requires
+ `Q_SQL_REQUIRE_PRIMARY_KEY` to be logged along aside. */
+ bool need_sql_require_primary_key{false};
+
+ /** Whether or not the statement represented by this event requires
+ `Q_DEFAULT_TABLE_ENCRYPTION` to be logged along aside. */
+ bool needs_default_table_encryption{false};
+};
+
+/**
+ @class Format_description_log_event
+
+ For binlog version 4.
+ This event is saved by threads which read it, as they need it for future
+ use (to decode the ordinary events).
+ This is the subclass of Format_description_event
+
+ @internal
+ The inheritance structure in the current design for the classes is
+ as follows:
+
+ Binary_log_event
+ ^
+ |
+ |
+ Format_description_event Log_event
+ \ /
+ \ /
+ \ /
+ Format_description_log_event
+ @endinternal
+ @section Format_description_log_event_binary_format Binary Format
+*/
+
+class Format_description_log_event : public Format_description_event,
+ public Log_event {
+ public:
+ /*
+ MTS Workers and Coordinator share the event and that affects its
+ destruction. Instantiation is always done by Coordinator/SQL thread.
+ Workers are allowed to destroy only "obsolete" instances, those
+ that are not actual for Coordinator anymore but needed to Workers
+ that are processing queued events depending on the old instance.
+ The counter of a new FD is incremented by Coordinator or Worker at
+ time of {Relay_log_info,Slave_worker}::set_rli_description_event()
+ execution.
+ In the same methods the counter of the "old" FD event is decremented
+ and when it drops to zero the old FD is deleted.
+ The latest read from relay-log event is to be
+ destroyed by Coordinator/SQL thread at its thread exit.
+ Notice the counter is processed even in the single-thread mode where
+ decrement and increment are done by the single SQL thread.
+ */
+ std::atomic<int32> atomic_usage_counter{0};
+
+ Format_description_log_event();
+ Format_description_log_event(
+ const char *buf, const Format_description_event *description_event);
+#ifdef MYSQL_SERVER
+ bool write(Basic_ostream *ostream) override;
+ int pack_info(Protocol *protocol) override;
+#else
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+
+ size_t get_data_size() override {
+ /*
+ The vector of post-header lengths is considered as part of the
+ post-header, because in a given version it never changes (contrary to the
+ query in a Query_log_event).
+ */
+ return Binary_log_event::FORMAT_DESCRIPTION_HEADER_LEN;
+ }
+
+ protected:
+#if defined(MYSQL_SERVER)
+ virtual int do_apply_event(Relay_log_info const *rli) override;
+ virtual int do_update_pos(Relay_log_info *rli) override;
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli) override;
+#endif
+};
+
+/**
+ @class Intvar_log_event
+
+ The class derives from the class Intvar_event in Binlog API,
+ defined in the header binlog_event.h. An Intvar_log_event is
+ created just before a Query_log_event, if the query uses one
+ of the variables LAST_INSERT_ID or INSERT_ID. This class is used
+ by the slave for applying the event.
+
+ @internal
+ The inheritance structure in the current design for the classes is
+ as follows:
+
+ Binary_log_event
+ ^
+ |
+ |
+ Intvar_event Log_event
+ \ /
+ \ /
+ \ /
+ Intvar_log_event
+ @endinternal
+*/
+class Intvar_log_event : public binary_log::Intvar_event, public Log_event {
+ public:
+#ifdef MYSQL_SERVER
+ Intvar_log_event(THD *thd_arg, uchar type_arg, ulonglong val_arg,
+ enum_event_cache_type cache_type_arg,
+ enum_event_logging_type logging_type_arg)
+ : binary_log::Intvar_event(type_arg, val_arg),
+ Log_event(thd_arg, 0, cache_type_arg, logging_type_arg, header(),
+ footer()) {
+ common_header->set_is_valid(true);
+ }
+ int pack_info(Protocol *protocol) override;
+#else
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+
+ Intvar_log_event(const char *buf,
+ const Format_description_event *description_event);
+ ~Intvar_log_event() override {}
+ size_t get_data_size() override {
+ return 9; /* sizeof(type) + sizeof(val) */
+ ;
+ }
+#ifdef MYSQL_SERVER
+ bool write(Basic_ostream *ostream) override;
+#endif
+
+ bool is_sbr_logging_format() const override { return true; }
+
+ private:
+#if defined(MYSQL_SERVER)
+ virtual int do_apply_event(Relay_log_info const *rli) override;
+ virtual int do_update_pos(Relay_log_info *rli) override;
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli) override;
+#endif
+};
+
+/**
+ @class Rand_log_event
+
+ Logs random seed used by the next RAND(), and by PASSWORD() in 4.1.0.
+ 4.1.1 does not need it (it's repeatable again) so this event needn't be
+ written in 4.1.1 for PASSWORD() (but the fact that it is written is just a
+ waste, it does not cause bugs).
+
+ The state of the random number generation consists of 128 bits,
+ which are stored internally as two 64-bit numbers.
+
+ @internal
+ The inheritance structure in the current design for the classes is
+ as follows:
+ Binary_log_event
+ ^
+ |
+ |
+ Rand_event Log_event
+ \ /
+ \ /
+ \ /
+ Rand_log_event
+ @endinternal
+*/
+class Rand_log_event : public binary_log::Rand_event, public Log_event {
+ public:
+#ifdef MYSQL_SERVER
+ Rand_log_event(THD *thd_arg, ulonglong seed1_arg, ulonglong seed2_arg,
+ enum_event_cache_type cache_type_arg,
+ enum_event_logging_type logging_type_arg)
+ : binary_log::Rand_event(seed1_arg, seed2_arg),
+ Log_event(thd_arg, 0, cache_type_arg, logging_type_arg, header(),
+ footer()) {
+ common_header->set_is_valid(true);
+ }
+ int pack_info(Protocol *protocol) override;
+#else
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+
+ Rand_log_event(const char *buf,
+ const Format_description_event *description_event);
+ ~Rand_log_event() override {}
+ size_t get_data_size() override { return 16; /* sizeof(ulonglong) * 2*/ }
+#ifdef MYSQL_SERVER
+ bool write(Basic_ostream *ostream) override;
+#endif
+
+ bool is_sbr_logging_format() const override { return true; }
+
+ private:
+#if defined(MYSQL_SERVER)
+ virtual int do_apply_event(Relay_log_info const *rli) override;
+ virtual int do_update_pos(Relay_log_info *rli) override;
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli) override;
+#endif
+};
+
+/**
+ @class Xid_log_event
+
+ This is the subclass of Xid_event defined in libbinlogevent,
+ An XID event is generated for a commit of a transaction that modifies one or
+ more tables of an XA-capable storage engine
+ Logs xid of the transaction-to-be-committed in the 2pc protocol.
+ Has no meaning in replication, slaves ignore it
+ The inheritance structure in the current design for the classes is
+ as follows
+
+ @internal
+ The inheritance structure in the current design for the classes is
+ as follows:
+ Binary_log_event
+ ^
+ |
+ |
+ Xid_event Log_event
+ \ /
+ \ /
+ \ /
+ Xid_log_event
+ @endinternal
+*/
+#ifndef MYSQL_SERVER
+typedef ulonglong my_xid; // this line is the same as in handler.h
+#endif
+
+class Xid_apply_log_event : public Log_event {
+ protected:
+#ifdef MYSQL_SERVER
+ Xid_apply_log_event(THD *thd_arg, Log_event_header *header_arg,
+ Log_event_footer *footer_arg)
+ : Log_event(thd_arg, 0, Log_event::EVENT_TRANSACTIONAL_CACHE,
+ Log_event::EVENT_NORMAL_LOGGING, header_arg, footer_arg) {}
+#endif
+ Xid_apply_log_event(Log_event_header *header_arg,
+ Log_event_footer *footer_arg)
+ : Log_event(header_arg, footer_arg) {}
+ ~Xid_apply_log_event() override {}
+ virtual bool ends_group() const override { return true; }
+#if defined(MYSQL_SERVER)
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli) override;
+ virtual int do_apply_event(Relay_log_info const *rli) override;
+ virtual int do_apply_event_worker(Slave_worker *rli) override;
+ virtual bool do_commit(THD *thd_arg) = 0;
+#endif
+};
+
+class Xid_log_event : public binary_log::Xid_event, public Xid_apply_log_event {
+ public:
+#ifdef MYSQL_SERVER
+ Xid_log_event(THD *thd_arg, my_xid x)
+ : binary_log::Xid_event(x),
+ Xid_apply_log_event(thd_arg, header(), footer()) {
+ common_header->set_is_valid(true);
+ }
+ int pack_info(Protocol *protocol) override;
+#else
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+
+ Xid_log_event(const char *buf,
+ const Format_description_event *description_event);
+ ~Xid_log_event() override {}
+ size_t get_data_size() override { return sizeof(xid); }
+#ifdef MYSQL_SERVER
+ bool write(Basic_ostream *ostream) override;
+#endif
+ private:
+#if defined(MYSQL_SERVER)
+ bool do_commit(THD *thd_arg) override;
+#endif
+};
+
+/**
+ @class XA_prepare_log_event
+
+ Similar to Xid_log_event except that
+ - it is specific to XA transaction
+ - it carries out the prepare logics rather than the final committing
+ when @c one_phase member is off.
+ From the groupping perspective the event finalizes the current "prepare" group
+ started with XA START Query-log-event.
+ When @c one_phase is false Commit of Rollback for XA transaction are
+ logged separately to the prepare-group events so being a groups of
+ their own.
+*/
+
+class XA_prepare_log_event : public binary_log::XA_prepare_event,
+ public Xid_apply_log_event {
+ private:
+ /* Total size of buffers to hold serialized members of XID struct */
+ static const int xid_bufs_size = 12;
+
+ public:
+#ifdef MYSQL_SERVER
+ XA_prepare_log_event(THD *thd_arg, XID *xid_arg, bool one_phase_arg = false)
+ : binary_log::XA_prepare_event((void *)xid_arg, one_phase_arg),
+ Xid_apply_log_event(thd_arg, header(), footer()) {}
+#endif
+ XA_prepare_log_event(const char *buf,
+ const Format_description_event *description_event)
+ : binary_log::XA_prepare_event(buf, description_event),
+ Xid_apply_log_event(header(), footer()) {
+ DBUG_ENTER(
+ "XA_prepare_log_event::XA_prepare_log_event(const char*, const "
+ "Format_description_log_event *)");
+ xid = nullptr;
+ DBUG_VOID_RETURN;
+ }
+ Log_event_type get_type_code() { return binary_log::XA_PREPARE_LOG_EVENT; }
+ size_t get_data_size() override {
+ return xid_bufs_size + my_xid.gtrid_length + my_xid.bqual_length;
+ }
+#ifdef MYSQL_SERVER
+ bool write(Basic_ostream *ostream) override;
+#else
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+#if defined(MYSQL_SERVER)
+ int pack_info(Protocol *protocol) override;
+ bool do_commit(THD *thd) override;
+#endif
+};
+
+/**
+ @class User_var_log_event
+
+ Every time a query uses the value of a user variable, a User_var_log_event is
+ written before the Query_log_event, to set the user variable.
+
+ @internal
+ The inheritance structure in the current design for the classes is
+ as follows:
+ Binary_log_event
+ ^
+ |
+ |
+ User_var_event Log_event
+ \ /
+ \ /
+ \ /
+ User_var_log_event
+ @endinternal
+*/
+class User_var_log_event : public binary_log::User_var_event, public Log_event {
+ public:
+#ifdef MYSQL_SERVER
+ bool deferred;
+ query_id_t query_id;
+ User_var_log_event(THD *thd_arg, const char *name_arg, uint name_len_arg,
+ char *val_arg, ulong val_len_arg, Item_result type_arg,
+ uint charset_number_arg, uchar flags_arg,
+ enum_event_cache_type cache_type_arg,
+ enum_event_logging_type logging_type_arg)
+ : binary_log::User_var_event(name_arg, name_len_arg, val_arg, val_len_arg,
+ type_arg, charset_number_arg, flags_arg),
+ Log_event(thd_arg, 0, cache_type_arg, logging_type_arg, header(),
+ footer()),
+ deferred(false) {
+ common_header->set_is_valid(name != 0);
+ }
+ int pack_info(Protocol *protocol) override;
+#else
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+
+ User_var_log_event(const char *buf,
+ const Format_description_event *description_event);
+ ~User_var_log_event() override {}
+#ifdef MYSQL_SERVER
+ bool write(Basic_ostream *ostream) override;
+ /*
+ Getter and setter for deferred User-event.
+ Returns true if the event is not applied directly
+ and which case the applier adjusts execution path.
+ */
+ bool is_deferred() { return deferred; }
+ /*
+ In case of the deffered applying the variable instance is flagged
+ and the parsing time query id is stored to be used at applying time.
+ */
+ void set_deferred(query_id_t qid) {
+ deferred = true;
+ query_id = qid;
+ }
+#endif
+
+ bool is_sbr_logging_format() const override { return true; }
+
+ private:
+#if defined(MYSQL_SERVER)
+ virtual int do_apply_event(Relay_log_info const *rli) override;
+ virtual int do_update_pos(Relay_log_info *rli) override;
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli) override;
+#endif
+};
+
+/**
+ @class Stop_log_event
+
+*/
+class Stop_log_event : public binary_log::Stop_event, public Log_event {
+ public:
+#ifdef MYSQL_SERVER
+ Stop_log_event()
+ : Log_event(header(), footer(), Log_event::EVENT_INVALID_CACHE,
+ Log_event::EVENT_INVALID_LOGGING) {
+ common_header->set_is_valid(true);
+ }
+
+#else
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+
+ Stop_log_event(const char *buf,
+ const Format_description_event *description_event)
+ : binary_log::Stop_event(buf, description_event),
+ Log_event(header(), footer()) {
+ DBUG_ENTER(
+ "Stop_log_event::Stop_log_event(const char*, const "
+ "Format_description_log_event *)");
+ DBUG_VOID_RETURN;
+ }
+
+ ~Stop_log_event() override {}
+ Log_event_type get_type_code() { return binary_log::STOP_EVENT; }
+
+ private:
+#if defined(MYSQL_SERVER)
+ virtual int do_update_pos(Relay_log_info *rli) override;
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *) override {
+ /*
+ Events from ourself should be skipped, but they should not
+ decrease the slave skip counter.
+ */
+ if (this->server_id == ::server_id)
+ return Log_event::EVENT_SKIP_IGNORE;
+ else
+ return Log_event::EVENT_SKIP_NOT;
+ }
+#endif
+};
+
+/**
+ @class Rotate_log_event
+
+ This will be deprecated when we move to using sequence ids.
+ This class is a subclass of Rotate_event, defined in binlogevent, and is used
+ by the slave for updating the position in the relay log.
+
+ It is used by the master inorder to write the rotate event in the binary log.
+
+ @internal
+ The inheritance structure in the current design for the classes is
+ as follows:
+
+ Binary_log_event
+ ^
+ |
+ |
+ Rotate_event Log_event
+ \ /
+ \ /
+ \ /
+ Rotate_log_event
+ @endinternal
+*/
+class Rotate_log_event : public binary_log::Rotate_event, public Log_event {
+ public:
+#ifdef MYSQL_SERVER
+ Rotate_log_event(const char *new_log_ident_arg, size_t ident_len_arg,
+ ulonglong pos_arg, uint flags);
+ int pack_info(Protocol *protocol) override;
+#else
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+
+ Rotate_log_event(const char *buf,
+ const Format_description_event *description_event);
+ ~Rotate_log_event() override {}
+ size_t get_data_size() override {
+ return ident_len + Binary_log_event::ROTATE_HEADER_LEN;
+ }
+#ifdef MYSQL_SERVER
+ bool write(Basic_ostream *ostream) override;
+#endif
+
+ private:
+#if defined(MYSQL_SERVER)
+ virtual int do_update_pos(Relay_log_info *rli) override;
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli) override;
+#endif
+};
+
+/**
+ @class Append_block_log_event
+
+ This event is created to contain the file data. One LOAD_DATA_INFILE
+ can have 0 or more instances of this event written to the binary log
+ depending on the size of the file.
+
+ @internal
+ The inheritance structure is as follows
+
+ Binary_log_event
+ ^
+ |
+ |
+ B_l:A_B_E Log_event
+ \ /
+ \ /
+ <<vir>>\ /
+ \ /
+ Append_block_log_event
+ B_l: Namespace Binary_log
+ A_B_E: class Append_block_event
+ @endinternal
+
+*/
+class Append_block_log_event : public virtual binary_log::Append_block_event,
+ public Log_event {
+ public:
+#ifdef MYSQL_SERVER
+ Append_block_log_event(THD *thd, const char *db_arg, uchar *block_arg,
+ uint block_len_arg, bool using_trans);
+ int pack_info(Protocol *protocol) override;
+ virtual int get_create_or_append() const;
+#else
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+
+ Append_block_log_event(const char *buf,
+ const Format_description_event *description_event);
+ ~Append_block_log_event() override {}
+ size_t get_data_size() override {
+ return block_len + Binary_log_event::APPEND_BLOCK_HEADER_LEN;
+ }
+#ifdef MYSQL_SERVER
+ bool write(Basic_ostream *ostream) override;
+ const char *get_db() override { return db; }
+#endif
+
+ bool is_sbr_logging_format() const override { return true; }
+
+ private:
+#if defined(MYSQL_SERVER)
+ virtual int do_apply_event(Relay_log_info const *rli) override;
+#endif
+};
+
+/**
+ @class Delete_file_log_event
+
+ Delete_file_log_event is created when the LOAD_DATA query fails on the
+ master for some reason, and the slave should be notified to abort the
+ load. The event is required since the master starts writing the loaded
+ block into the binary log before the statement ends. In case of error,
+ the slave should abort, and delete any temporary file created while
+ applying the (NEW_)LOAD_EVENT.
+
+ @internal
+ The inheritance structure is as follows
+
+ Binary_log_event
+ ^
+ |
+ |
+ B_l:D_F_E Log_event
+ \ /
+ \ /
+ \ /
+ \ /
+ Delete_file_log_event
+
+ B_l: Namespace Binary_log
+ D_F_E: class Delete_file_event
+ @endinternal
+
+*/
+class Delete_file_log_event : public binary_log::Delete_file_event,
+ public Log_event {
+ public:
+#ifdef MYSQL_SERVER
+ Delete_file_log_event(THD *thd, const char *db_arg, bool using_trans);
+ int pack_info(Protocol *protocol) override;
+#else
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info, bool enable_local);
+#endif
+
+ Delete_file_log_event(const char *buf,
+ const Format_description_event *description_event);
+ ~Delete_file_log_event() override {}
+ size_t get_data_size() override {
+ return Binary_log_event::DELETE_FILE_HEADER_LEN;
+ }
+#ifdef MYSQL_SERVER
+ bool write(Basic_ostream *ostream) override;
+ const char *get_db() override { return db; }
+#endif
+
+ bool is_sbr_logging_format() const override { return true; }
+
+ private:
+#if defined(MYSQL_SERVER)
+ virtual int do_apply_event(Relay_log_info const *rli) override;
+#endif
+};
+
+/**
+ @class Begin_load_query_log_event
+
+ Event for the first block of file to be loaded, its only difference from
+ Append_block event is that this event creates or truncates existing file
+ before writing data.
+
+ @internal
+ The inheritance structure is as follows
+
+ Binary_log_event
+ ^
+ |
+ |
+ |
+ Log_event B_l:A_B_E
+ ^ /\
+ | / \
+ | <<vir>>/ \ <<vir>>
+ | / \
+ | / \
+ | / \
+ Append_block_log_event B_l:B_L_Q_E
+ \ /
+ \ /
+ \ /
+ \ /
+ \ /
+ Begin_load_query_log_event
+
+ B_l: Namespace Binary_log
+ A_B_E: class Append_block_event
+ B_L_Q_E: Begin_load_query_event
+ @endinternal
+
+ @section Begin_load_query_log_event_binary_format Binary Format
+*/
+class Begin_load_query_log_event : public Append_block_log_event,
+ public binary_log::Begin_load_query_event {
+ public:
+#ifdef MYSQL_SERVER
+ Begin_load_query_log_event(THD *thd_arg, const char *db_arg, uchar *block_arg,
+ uint block_len_arg, bool using_trans);
+ Begin_load_query_log_event(THD *thd);
+ int get_create_or_append() const override;
+#endif
+ Begin_load_query_log_event(const char *buf,
+ const Format_description_event *description_event);
+ ~Begin_load_query_log_event() override {}
+
+ private:
+#if defined(MYSQL_SERVER)
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli) override;
+#endif
+};
+
+/**
+ @class Execute_load_query_log_event
+
+ Event responsible for LOAD DATA execution, it similar to Query_log_event
+ but before executing the query it substitutes original filename in LOAD DATA
+ query with name of temporary file.
+
+ @internal
+ The inheritance structure is as follows:
+
+ Binary_log_event
+ ^
+ |
+ |
+ |
+ Log_event B_l:Query_event
+ ^ /\
+ | / \
+ | <<vir>>/ \ <<vir>>
+ | / \
+ | / \
+ | / \
+ Query_log_event B_l:E_L_Q_E
+ \ /
+ \ /
+ \ /
+ \ /
+ \ /
+ Execute_load_query_log_event
+
+ B_l: Namespace Binary_log
+ E_L_Q_E: class Execute_load_query
+ @endinternal
+
+ @section Execute_load_query_log_event_binary_format Binary Format
+*/
+class Execute_load_query_log_event
+ : public Query_log_event,
+ public binary_log::Execute_load_query_event {
+ public:
+#ifdef MYSQL_SERVER
+ Execute_load_query_log_event(
+ THD *thd, const char *query_arg, ulong query_length,
+ uint fn_pos_start_arg, uint fn_pos_end_arg,
+ binary_log::enum_load_dup_handling dup_handling_arg, bool using_trans,
+ bool immediate, bool suppress_use, int errcode);
+ int pack_info(Protocol *protocol) override;
+#else
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+ /* Prints the query as LOAD DATA LOCAL and with rewritten filename */
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info,
+ const char *local_fname) const;
+#endif
+ Execute_load_query_log_event(
+ const char *buf, const Format_description_event *description_event);
+ ~Execute_load_query_log_event() override {}
+
+ ulong get_post_header_size_for_derived() override;
+#ifdef MYSQL_SERVER
+ bool write_post_header_for_derived(Basic_ostream *ostream) override;
+#endif
+
+ bool is_sbr_logging_format() const override { return true; }
+
+ private:
+#if defined(MYSQL_SERVER)
+ virtual int do_apply_event(Relay_log_info const *rli) override;
+#endif
+};
+
+#if defined MYSQL_SERVER
+class Load_query_generator {
+ public:
+ Load_query_generator(THD *thd_arg, const sql_exchange *ex, const char *db_arg,
+ const char *table_name_arg, bool is_concurrent_arg,
+ bool replace, bool ignore);
+
+ const String *generate(size_t *fn_start, size_t *fn_end);
+
+ private:
+ const size_t BUF_SIZE = 2048;
+ String str;
+ char *buf[2048];
+
+ THD *thd;
+ const sql_exchange *sql_ex;
+ const char *db;
+ const char *table_name;
+ const char *fname;
+
+ bool is_concurrent;
+ bool has_replace;
+ bool has_ignore;
+};
+#endif
+#ifndef MYSQL_SERVER
+/**
+ @class Unknown_log_event
+
+*/
+class Unknown_log_event : public binary_log::Unknown_event, public Log_event {
+ public:
+ /**
+ Even if this is an unknown event, we still pass description_event to
+ Log_event's ctor, this way we can extract maximum information from the
+ event's header (the unique ID for example).
+ */
+ Unknown_log_event(const char *buf,
+ const Format_description_event *description_event)
+ : binary_log::Unknown_event(buf, description_event),
+ Log_event(header(), footer()) {
+ DBUG_ENTER(
+ "Unknown_log_event::Unknown_log_event(const char *, const "
+ "Format_description_log_event *)");
+ if (!is_valid()) DBUG_VOID_RETURN;
+ common_header->set_is_valid(true);
+ DBUG_VOID_RETURN;
+ }
+
+ ~Unknown_log_event() override {}
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+ Log_event_type get_type_code() { return binary_log::UNKNOWN_EVENT; }
+};
+#endif
+char *str_to_hex(char *to, const char *from, size_t len);
+
+/**
+ @class Table_map_log_event
+
+ Table_map_log_event which maps a table definition to a number.
+
+ @internal
+ The inheritance structure in the current design for the classes is
+ as follows:
+
+ Binary_log_event
+ ^
+ |
+ |
+ Table_map_event Log_event
+ \ /
+ \ /
+ \ /
+ Table_map_log_event
+ @endinternal
+*/
+class Table_map_log_event : public binary_log::Table_map_event,
+ public Log_event {
+ public:
+ /** Constants */
+ enum { TYPE_CODE = binary_log::TABLE_MAP_EVENT };
+
+ /**
+ Enumeration of the errors that can be returned.
+ */
+ enum enum_error {
+ ERR_OPEN_FAILURE = -1, /**< Failure to open table */
+ ERR_OK = 0, /**< No error */
+ ERR_TABLE_LIMIT_EXCEEDED = 1, /**< No more room for tables */
+ ERR_OUT_OF_MEM = 2, /**< Out of memory */
+ ERR_BAD_TABLE_DEF = 3, /**< Table definition does not match */
+ ERR_RBR_TO_SBR = 4 /**< daisy-chanining RBR to SBR not allowed */
+ };
+
+ enum enum_flag {
+ /**
+ Nothing here right now, but the flags support is there in
+ preparation for changes that are coming. Need to add a
+ constant to make it compile under HP-UX: aCC does not like
+ empty enumerations.
+ */
+ ENUM_FLAG_COUNT
+ };
+
+ /** Special constants representing sets of flags */
+ enum {
+ TM_NO_FLAGS = 0U,
+ TM_BIT_LEN_EXACT_F = (1U << 0),
+ TM_REFERRED_FK_DB_F = (1U << 1)
+ };
+
+ flag_set get_flags(flag_set flag) const { return m_flags & flag; }
+
+#ifdef MYSQL_SERVER
+ Table_map_log_event(THD *thd_arg, TABLE *tbl, const Table_id &tid,
+ bool is_transactional);
+#endif
+ Table_map_log_event(const char *buf,
+ const Format_description_event *description_event);
+
+ ~Table_map_log_event() override;
+
+#ifndef MYSQL_SERVER
+ table_def *create_table_def() {
+ DBUG_ASSERT(m_colcnt > 0);
+ return new table_def(m_coltype, m_colcnt, m_field_metadata,
+ m_field_metadata_size, m_null_bits, m_flags);
+ }
+ static bool rewrite_db_in_buffer(char **buf, ulong *event_len,
+ const Format_description_event &fde);
+#endif
+ const Table_id &get_table_id() const { return m_table_id; }
+ const char *get_table_name() const { return m_tblnam.c_str(); }
+ const char *get_db_name() const { return m_dbnam.c_str(); }
+
+ virtual size_t get_data_size() override { return m_data_size; }
+#ifdef MYSQL_SERVER
+ virtual int save_field_metadata();
+ virtual bool write_data_header(Basic_ostream *ostream) override;
+ virtual bool write_data_body(Basic_ostream *ostream) override;
+ virtual const char *get_db() override { return m_dbnam.c_str(); }
+ virtual uint8 mts_number_dbs() override {
+ return get_flags(TM_REFERRED_FK_DB_F) ? OVER_MAX_DBS_IN_EVENT_MTS : 1;
+ }
+ /**
+ @param[out] arg pointer to a struct containing char* array
+ pointers be filled in and the number of filled instances.
+ @param rpl_filter pointer to a replication filter.
+
+ @return number of databases in the array: either one or
+ OVER_MAX_DBS_IN_EVENT_MTS, when the Table map event reports
+ foreign keys constraint.
+ */
+ virtual uint8 get_mts_dbs(Mts_db_names *arg,
+ Rpl_filter *rpl_filter) override {
+ const char *db_name = get_db();
+
+ if (!rpl_filter->is_rewrite_empty() && !get_flags(TM_REFERRED_FK_DB_F)) {
+ size_t dummy_len;
+ const char *db_filtered = rpl_filter->get_rewrite_db(db_name, &dummy_len);
+ // db_name != db_filtered means that db_name is rewritten.
+ if (strcmp(db_name, db_filtered)) db_name = db_filtered;
+ }
+
+ if (!get_flags(TM_REFERRED_FK_DB_F)) arg->name[0] = db_name;
+
+ return arg->num = mts_number_dbs();
+ }
+
+#endif
+
+#if defined(MYSQL_SERVER)
+ virtual int pack_info(Protocol *protocol) override;
+#endif
+
+#ifndef MYSQL_SERVER
+ virtual void print(FILE *file,
+ PRINT_EVENT_INFO *print_event_info) const override;
+
+ /**
+ Print column metadata. Its format looks like:
+ # Columns(colume_name type, colume_name type, ...)
+ if colume_name field is not logged into table_map_log_event, then
+ only type is printed.
+
+ @@param[out] file the place where colume metadata is printed
+ @@param[in] The metadata extracted from optional metadata fields
+ */
+ void print_columns(IO_CACHE *file,
+ const Optional_metadata_fields &fields) const;
+ /**
+ Print primary information. Its format looks like:
+ # Primary Key(colume_name, column_name(prifix), ...)
+ if colume_name field is not logged into table_map_log_event, then
+ colume index is printed.
+
+ @@param[out] file the place where primary key is printed
+ @@param[in] The metadata extracted from optional metadata fields
+ */
+ void print_primary_key(IO_CACHE *file,
+ const Optional_metadata_fields &fields) const;
+#endif
+
+ bool is_rbr_logging_format() const override { return true; }
+
+ private:
+#if defined(MYSQL_SERVER)
+ virtual int do_apply_event(Relay_log_info const *rli) override;
+ virtual int do_update_pos(Relay_log_info *rli) override;
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli) override;
+#endif
+
+#ifdef MYSQL_SERVER
+ TABLE *m_table;
+
+ // Metadata fields buffer
+ StringBuffer<1024> m_metadata_buf;
+
+ /**
+ Capture the optional metadata fields which should be logged into
+ table_map_log_event and serialize them into m_metadata_buf.
+ */
+ void init_metadata_fields();
+ bool init_signedness_field();
+ /**
+ Capture and serialize character sets. Character sets for
+ character columns (TEXT etc) and character sets for ENUM and SET
+ columns are stored in different metadata fields. The reason is
+ that TEXT character sets are included even when
+ binlog_row_metadata=MINIMAL, whereas ENUM and SET character sets
+ are included only when binlog_row_metadata=FULL.
+
+ @param include_type Predicate to determine if a given Field object
+ is to be included in the metadata field.
+
+ @param default_charset_type Type code when storing in "default
+ charset" format. (See comment above Table_maps_log_event in
+ libbinlogevents/include/rows_event.h)
+
+ @param column_charset_type Type code when storing in "column
+ charset" format. (See comment above Table_maps_log_event in
+ libbinlogevents/include/rows_event.h)
+ */
+ bool init_charset_field(std::function<bool(const Field *)> include_type,
+ Optional_metadata_field_type default_charset_type,
+ Optional_metadata_field_type column_charset_type);
+ bool init_column_name_field();
+ bool init_set_str_value_field();
+ bool init_enum_str_value_field();
+ bool init_geometry_type_field();
+ bool init_primary_key_field();
+#endif
+
+#ifndef MYSQL_SERVER
+ class Charset_iterator;
+ class Default_charset_iterator;
+ class Column_charset_iterator;
+#endif
+};
+
+#ifdef HAVE_PSI_STAGE_INTERFACE
+/*
+ Helper class for PSI context while applying a Rows_log_event.
+ */
+class Rows_applier_psi_stage {
+ private:
+ Rows_applier_psi_stage(const Rows_applier_psi_stage &rhs);
+ Rows_applier_psi_stage &operator=(const Rows_applier_psi_stage &rhs);
+
+ /**
+ A cached pointer to this stage PSI_stage_progress.
+ */
+ PSI_stage_progress *m_progress;
+
+ /**
+ Counter that is unconditionally incremented on each row that is processed.
+ This is helpful in case estimation is needed after started processing
+ a Rows_log_event.
+ */
+ ulonglong m_n_rows_applied;
+
+ public:
+ Rows_applier_psi_stage() : m_progress(nullptr), m_n_rows_applied(0) {}
+
+ void set_progress(PSI_stage_progress *progress) { m_progress = progress; }
+
+ /**
+ If instrumentation is enabled this member function SHALL return true.
+ @return true if instrumentation is enabled for the given stage, false
+ otherwise.
+ */
+ bool is_enabled() { return m_progress != nullptr; }
+
+ /**
+ This member function shall update the progress and reestimate the remaining
+ work needed. This MUST be called after setting n_rows_applied correctly
+ by calling inc_n_rows_applied beforehand.
+
+ Cursor, begin and end are used in case estimation is needed.
+
+ @param cursor Pointer to where we are in the buffer of rows to be processed.
+ @param begin Pointer to the beginning of the rows buffer.
+ @param end Pointer to the end of the rows buffer.
+ */
+ void update_work_estimated_and_completed(const uchar *cursor,
+ const uchar *begin,
+ const uchar *end) {
+ if (!is_enabled()) return;
+
+ ulonglong estimated = mysql_stage_get_work_estimated(m_progress);
+
+ /* Estimate if need be. */
+ if (estimated == 0) {
+ DBUG_ASSERT(cursor > begin);
+ ulonglong avg_row_change_size = (cursor - begin) / m_n_rows_applied;
+ estimated = (end - begin) / avg_row_change_size;
+ mysql_stage_set_work_estimated(m_progress, estimated);
+ }
+
+ /* reset estimated if done more work than estimated */
+ if (m_n_rows_applied > estimated)
+ mysql_stage_set_work_estimated(m_progress, m_n_rows_applied);
+ mysql_stage_set_work_completed(m_progress, m_n_rows_applied);
+ }
+
+ /**
+ Resets this object.
+ */
+ void end_work() {
+ m_progress = nullptr;
+ m_n_rows_applied = 0;
+ }
+
+ /**
+ Updates the counter of processed rows.
+ @param delta the amount of increment to be done.
+ */
+ void inc_n_rows_applied(ulonglong delta) { m_n_rows_applied += delta; }
+
+ /**
+ Gets the value of the counter of rows that have been processed.
+ @return the value of the counter of rows processed so far.
+ */
+ ulonglong get_n_rows_applied() { return m_n_rows_applied; }
+};
+#endif
+
+/**
+ @class Rows_log_event
+
+ Common base class for all row-containing log events.
+
+ RESPONSIBILITIES
+
+ Encode the common parts of all events containing rows, which are:
+ - Write data header and data body to an IO_CACHE.
+
+ Virtual inheritance is required here to handle the diamond problem in
+ the class Write_rows_log_event, Update_rows_log_event and
+ Delete_rows_log_event.
+ The diamond structure is explained in @c Write_rows_log_event,
+ @c Update_rows_log_event,
+ @c Delete_rows_log_event
+
+ @internal
+ The inheritance structure in the current design for the classes is
+ as follows:
+
+ Binary_log_event
+ ^
+ |
+ |
+ Rows_event Log_event
+ \ /
+ <<vir>>\ /
+ \ /
+ Rows_log_event
+ @endinternal
+
+*/
+class Rows_log_event : public virtual binary_log::Rows_event, public Log_event {
+#ifdef HAVE_PSI_STAGE_INTERFACE
+ protected:
+ Rows_applier_psi_stage m_psi_progress;
+#endif
+
+ public:
+ typedef uint16 flag_set;
+
+ enum row_lookup_mode {
+ ROW_LOOKUP_UNDEFINED = 0,
+ ROW_LOOKUP_NOT_NEEDED = 1,
+ ROW_LOOKUP_INDEX_SCAN = 2,
+ ROW_LOOKUP_TABLE_SCAN = 3,
+ ROW_LOOKUP_HASH_SCAN = 4
+ };
+
+ /**
+ Enumeration of the errors that can be returned.
+ */
+ enum enum_error {
+ ERR_OPEN_FAILURE = -1, /**< Failure to open table */
+ ERR_OK = 0, /**< No error */
+ ERR_TABLE_LIMIT_EXCEEDED = 1, /**< No more room for tables */
+ ERR_OUT_OF_MEM = 2, /**< Out of memory */
+ ERR_BAD_TABLE_DEF = 3, /**< Table definition does not match */
+ ERR_RBR_TO_SBR = 4 /**< daisy-chanining RBR to SBR not allowed */
+ };
+
+ /* Special constants representing sets of flags */
+ enum { RLE_NO_FLAGS = 0U };
+
+ ~Rows_log_event() override;
+
+ void set_flags(flag_set flags_arg) { m_flags |= flags_arg; }
+ void clear_flags(flag_set flags_arg) { m_flags &= ~flags_arg; }
+ flag_set get_flags(flag_set flags_arg) const { return m_flags & flags_arg; }
+
+ virtual Log_event_type
+ get_general_type_code() = 0; /* General rows op type, no version */
+
+#if defined(MYSQL_SERVER)
+ virtual int pack_info(Protocol *protocol) override;
+#endif
+
+#ifndef MYSQL_SERVER
+ void print_verbose(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info);
+ size_t print_verbose_one_row(IO_CACHE *file, table_def *td,
+ PRINT_EVENT_INFO *print_event_info,
+ MY_BITMAP *cols_bitmap, const uchar *ptr,
+ const uchar *prefix,
+ enum_row_image_type row_image_type);
+#endif
+
+#ifdef MYSQL_SERVER
+ int add_row_data(uchar *data, size_t length) {
+ return do_add_row_data(data, length);
+ }
+#endif
+
+ /* Member functions to implement superclass interface */
+ virtual size_t get_data_size() override;
+
+ MY_BITMAP const *get_cols() const { return &m_cols; }
+ MY_BITMAP const *get_cols_ai() const { return &m_cols_ai; }
+ size_t get_width() const { return m_width; }
+ const Table_id &get_table_id() const { return m_table_id; }
+
+#if defined(MYSQL_SERVER)
+ /**
+ Compares the table's read/write_set with the columns included in
+ this event's before-image and/or after-image. Each subclass
+ (Write/Update/Delete) implements this function by comparing on the
+ image(s) pertinent to the subclass.
+
+ @param[in] table The table to compare this events bitmaps
+ against.
+
+ @retval true if sets match
+ @retval false otherwise (following bitmap_cmp return logic).
+ */
+ virtual bool read_write_bitmaps_cmp(const TABLE *table) const = 0;
+#endif
+
+#ifdef MYSQL_SERVER
+ virtual bool write_data_header(Basic_ostream *ostream) override;
+ virtual bool write_data_body(Basic_ostream *ostream) override;
+ virtual const char *get_db() override { return m_table->s->db.str; }
+#endif
+
+ uint m_row_count; /* The number of rows added to the event */
+
+ protected:
+ /*
+ The constructors are protected since you're supposed to inherit
+ this class, not create instances of this class.
+ */
+#ifdef MYSQL_SERVER
+ Rows_log_event(THD *, TABLE *, const Table_id &table_id,
+ MY_BITMAP const *cols, bool is_transactional,
+ Log_event_type event_type,
+ const unsigned char *extra_row_ndb_info);
+#endif
+ Rows_log_event(const char *row_data,
+ const Format_description_event *description_event);
+
+#ifndef MYSQL_SERVER
+ void print_helper(FILE *, PRINT_EVENT_INFO *) const;
+#endif
+
+#ifdef MYSQL_SERVER
+ virtual int do_add_row_data(uchar *data, size_t length);
+#endif
+
+#ifdef MYSQL_SERVER
+ TABLE *m_table; /* The table the rows belong to */
+#endif
+ MY_BITMAP m_cols; /* Bitmap denoting columns available */
+#ifdef MYSQL_SERVER
+ /**
+ Hash table that will hold the entries for while using HASH_SCAN
+ algorithm to search and update/delete rows.
+ */
+ Hash_slave_rows m_hash;
+
+ /**
+ The algorithm to use while searching for rows using the before
+ image.
+ */
+ uint m_rows_lookup_algorithm;
+#endif
+ /*
+ Bitmap for columns available in the after image, if present. These
+ fields are only available for Update_rows events. Observe that the
+ width of both the before image COLS vector and the after image
+ COLS vector is the same: the number of columns of the table on the
+ master.
+ */
+ MY_BITMAP m_cols_ai;
+
+ /* Bit buffers in the same memory as the class */
+ uint32 m_bitbuf[128 / (sizeof(uint32) * 8)];
+ uint32 m_bitbuf_ai[128 / (sizeof(uint32) * 8)];
+
+ /*
+ is_valid depends on the value of m_rows_buf, so while changing the value
+ of m_rows_buf check if is_valid also needs to be modified
+ */
+ uchar *m_rows_buf; /* The rows in packed format */
+ uchar *m_rows_cur; /* One-after the end of the data */
+ uchar *m_rows_end; /* One-after the end of the allocated space */
+
+ /* helper functions */
+
+#if defined(MYSQL_SERVER)
+ const uchar *m_curr_row; /* Start of the row being processed */
+ const uchar *m_curr_row_end; /* One-after the end of the current row */
+ uchar *m_key; /* Buffer to keep key value during searches */
+ uint m_key_index;
+ KEY *m_key_info; /* Points to description of index #m_key_index */
+ class Key_compare {
+ public:
+ /**
+ @param ki Where to find KEY description
+ @note m_distinct_keys is instantiated when Rows_log_event is constructed;
+ it stores a Key_compare object internally. However at that moment, the
+ index (KEY*) to use for comparisons, is not yet known. So, at
+ instantiation, we indicate the Key_compare the place where it can
+ find the KEY* when needed (this place is Rows_log_event::m_key_info),
+ Key_compare remembers the place in member m_key_info.
+ Before we need to do comparisons - i.e. before we need to insert
+ elements, we update Rows_log_event::m_key_info once for all.
+ */
+ Key_compare(KEY **ki = nullptr) : m_key_info(ki) {}
+ bool operator()(uchar *k1, uchar *k2) const {
+ return key_cmp2((*m_key_info)->key_part, k1, (*m_key_info)->key_length,
+ k2, (*m_key_info)->key_length) < 0;
+ }
+
+ private:
+ KEY **m_key_info;
+ };
+ std::set<uchar *, Key_compare> m_distinct_keys;
+ std::set<uchar *, Key_compare>::iterator m_itr;
+ /**
+ A spare buffer which will be used when saving the distinct keys
+ for doing an index scan with HASH_SCAN search algorithm.
+ */
+ uchar *m_distinct_key_spare_buf;
+
+ /**
+ Unpack the current row image from the event into m_table->record[0].
+
+ @param rli The applier context.
+
+ @param cols The bitmap of columns included in the update.
+
+ @param is_after_image Should be true if this is an after-image,
+ false if it is a before-image.
+
+ @param only_seek @see unpack_row()
+
+ @retval 0 Success
+
+ @retval ER_* On error, it is guaranteed that the error has been
+ reported through my_error, and the corresponding ER_* code is
+ returned. Currently the error codes are: EE_OUTOFMEMORY,
+ ER_SLAVE_CORRUPT_EVENT, or various JSON errors when applying JSON
+ diffs (ER_COULD_NOT_APPLY_JSON_DIFF, ER_INVALID_JSON_BINARY_DATA,
+ and maybe others).
+ */
+ int unpack_current_row(const Relay_log_info *const rli, MY_BITMAP const *cols,
+ bool is_after_image, bool only_seek = false);
+
+ /*
+ This member function is called when deciding the algorithm to be used to
+ find the rows to be updated on the slave during row based replication.
+ This this functions sets the m_rows_lookup_algorithm and also the
+ m_key_index with the key index to be used if the algorithm is dependent on
+ an index.
+ */
+ void decide_row_lookup_algorithm_and_key();
+
+ /*
+ Encapsulates the operations to be done before applying
+ row event for update and delete.
+ */
+ int row_operations_scan_and_key_setup();
+
+ /*
+ Encapsulates the operations to be done after applying
+ row event for update and delete.
+ */
+ int row_operations_scan_and_key_teardown(int error);
+
+ /**
+ Helper function to check whether there is an auto increment
+ column on the table where the event is to be applied.
+
+ @return true if there is an autoincrement field on the extra
+ columns, false otherwise.
+ */
+ bool is_auto_inc_in_extra_columns();
+#endif
+
+ bool is_rbr_logging_format() const override { return true; }
+
+ private:
+#if defined(MYSQL_SERVER)
+ virtual int do_apply_event(Relay_log_info const *rli) override;
+ virtual int do_update_pos(Relay_log_info *rli) override;
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli) override;
+
+ /*
+ Primitive to prepare for a sequence of row executions.
+
+ DESCRIPTION
+
+ Before doing a sequence of do_prepare_row() and do_exec_row()
+ calls, this member function should be called to prepare for the
+ entire sequence. Typically, this member function will allocate
+ space for any buffers that are needed for the two member
+ functions mentioned above.
+
+ RETURN VALUE
+
+ The member function will return 0 if all went OK, or a non-zero
+ error code otherwise.
+ */
+ virtual int do_before_row_operations(
+ const Slave_reporting_capability *const log) = 0;
+
+ /*
+ Primitive to clean up after a sequence of row executions.
+
+ DESCRIPTION
+
+ After doing a sequence of do_prepare_row() and do_exec_row(),
+ this member function should be called to clean up and release
+ any allocated buffers.
+
+ The error argument, if non-zero, indicates an error which happened during
+ row processing before this function was called. In this case, even if
+ function is successful, it should return the error code given in the
+ argument.
+ */
+ virtual int do_after_row_operations(
+ const Slave_reporting_capability *const log, int error) = 0;
+
+ /*
+ Primitive to do the actual execution necessary for a row.
+
+ DESCRIPTION
+ The member function will do the actual execution needed to handle a row.
+ The row is located at m_curr_row. When the function returns,
+ m_curr_row_end should point at the next row (one byte after the end
+ of the current row).
+
+ RETURN VALUE
+ 0 if execution succeeded, 1 if execution failed.
+
+ */
+ virtual int do_exec_row(const Relay_log_info *const rli) = 0;
+
+ /**
+ Private member function called while handling idempotent errors.
+
+ @param rli Pointer to relay log info structure.
+ @param [in,out] err the error to handle. If it is listed as
+ idempotent/ignored related error, then it is cleared.
+ @returns true if the slave should stop executing rows.
+ */
+ int handle_idempotent_and_ignored_errors(Relay_log_info const *rli, int *err);
+
+ /**
+ Private member function called after updating/deleting a row. It
+ performs some assertions and more importantly, it updates
+ m_curr_row so that the next row is processed during the row
+ execution main loop (@c Rows_log_event::do_apply_event()).
+
+ @param rli Pointer to relay log info structure.
+ @param err the current error code.
+ */
+ void do_post_row_operations(Relay_log_info const *rli, int err);
+
+ /**
+ Commodity wrapper around do_exec_row(), that deals with resetting
+ the thd reference in the table.
+ */
+ int do_apply_row(Relay_log_info const *rli);
+
+ /**
+ Implementation of the index scan and update algorithm. It uses
+ PK, UK or regular Key to search for the record to update. When
+ found it updates it.
+ */
+ int do_index_scan_and_update(Relay_log_info const *rli);
+
+ /**
+ Implementation of the hash_scan and update algorithm. It collects
+ rows positions in a hashtable until the last row is
+ unpacked. Then it scans the table to update and when a record in
+ the table matches the one in the hashtable, the update/delete is
+ performed.
+ */
+ int do_hash_scan_and_update(Relay_log_info const *rli);
+
+ /**
+ Implementation of the legacy table_scan and update algorithm. For
+ each unpacked row it scans the storage engine table for a
+ match. When a match is found, the update/delete operations are
+ performed.
+ */
+ int do_table_scan_and_update(Relay_log_info const *rli);
+
+ /**
+ Seek past the after-image of an update event, in case a row was processed
+ without reading the after-image.
+
+ An update event may process a row without reading the after-image,
+ e.g. in case of ignored or idempotent errors. To ensure that the
+ read position for the next row is correct, we need to seek past
+ the after-image.
+
+ @param rli The applier context
+
+ @param curr_bi_start The read position of the beginning of the
+ before-image. (The function compares this with m_curr_row to know
+ if the after-image has been read or not.)
+
+ @retval 0 Success
+ @retval ER_* Error code returned by unpack_current_row
+ */
+ virtual int skip_after_image_for_update_event(
+ const Relay_log_info *rli MY_ATTRIBUTE((unused)),
+ const uchar *curr_bi_start MY_ATTRIBUTE((unused))) {
+ return 0;
+ }
+
+ /**
+ Initializes scanning of rows. Opens an index and initailizes an iterator
+ over a list of distinct keys (m_distinct_keys) if it is a HASH_SCAN
+ over an index or the table if its a HASH_SCAN over the table.
+ */
+ int open_record_scan();
+
+ /**
+ Does the cleanup
+ - closes the index if opened by open_record_scan
+ - closes the table if opened for scanning.
+ */
+ int close_record_scan();
+
+ /**
+ Fetches next row. If it is a HASH_SCAN over an index, it populates
+ table->record[0] with the next row corresponding to the index. If
+ the indexes are in non-contigous ranges it fetches record corresponding
+ to the key value in the next range.
+
+ @param first_read signifying if this is the first time we are reading a row
+ over an index.
+ @return error code when there are no more records to be fetched or some
+ other error occurred,
+ - 0 otherwise.
+ */
+ int next_record_scan(bool first_read);
+
+ /**
+ Populates the m_distinct_keys with unique keys to be modified
+ during HASH_SCAN over keys.
+ @returns 0 success, or the error code.
+ */
+ int add_key_to_distinct_keyset();
+
+ /**
+ Populates the m_hash when using HASH_SCAN. Thence, it:
+ - unpacks the before image (BI)
+ - saves the positions
+ - saves the positions into the hash map, using the
+ BI checksum as key
+ - unpacks the after image (AI) if needed, so that
+ m_curr_row_end gets updated correctly.
+
+ @param rli The reference to the relay log info object.
+ @returns 0 on success. Otherwise, the error code.
+ */
+ int do_hash_row(Relay_log_info const *rli);
+
+ /**
+ This member function scans the table and applies the changes
+ that had been previously hashed. As such, m_hash MUST be filled
+ by do_hash_row before calling this member function.
+
+ @param rli The reference to the relay log info object.
+ @returns 0 on success. Otherwise, the error code.
+ */
+ int do_scan_and_update(Relay_log_info const *rli);
+#endif /* defined(MYSQL_SERVER) */
+
+ friend class Old_rows_log_event;
+
+ /**
+ This bitmap is used as a backup for the write set while we calculate
+ the values for any hidden generated columns (functional indexes). In order
+ to calculate the values, the columns must be marked in the write set. After
+ the values are caluclated, we set the write set back to it's original value.
+ */
+ MY_BITMAP write_set_backup;
+};
+
+/**
+ @class Write_rows_log_event
+
+ Log row insertions and updates. The event contain several
+ insert/update rows for a table. Note that each event contains only
+ rows for one table.
+
+ @internal
+ The inheritance structure is as follows
+
+ Binary_log_event
+ ^
+ |
+ |
+ |
+ Log_event B_l:Rows_event
+ ^ /\
+ | / \
+ | <<vir>>/ \ <<vir>>
+ | / \
+ | / \
+ | / \
+ Rows_log_event B_l:W_R_E
+ \ /
+ \ /
+ \ /
+ \ /
+ \ /
+ \/
+ Write_rows_log_event
+
+ B_l: Namespace Binary_log
+ W_R_E: class Write_rows_event
+ @endinternal
+
+*/
+class Write_rows_log_event : public Rows_log_event,
+ public binary_log::Write_rows_event {
+ public:
+ enum {
+ /* Support interface to THD::binlog_prepare_pending_rows_event */
+ TYPE_CODE = binary_log::WRITE_ROWS_EVENT
+ };
+
+#if defined(MYSQL_SERVER)
+ Write_rows_log_event(THD *, TABLE *, const Table_id &table_id,
+ bool is_transactional,
+ const unsigned char *extra_row_ndb_info);
+#endif
+ Write_rows_log_event(const char *buf,
+ const Format_description_event *description_event);
+#if defined(MYSQL_SERVER)
+ static bool binlog_row_logging_function(
+ THD *thd, TABLE *table, bool is_transactional,
+ const uchar *before_record MY_ATTRIBUTE((unused)),
+ const uchar *after_record);
+ bool read_write_bitmaps_cmp(const TABLE *table) const override {
+ return bitmap_cmp(get_cols(), table->write_set);
+ }
+#endif
+
+ protected:
+ int write_row(const Relay_log_info *const, const bool);
+
+ private:
+ virtual Log_event_type get_general_type_code() override {
+ return (Log_event_type)TYPE_CODE;
+ }
+
+#ifndef MYSQL_SERVER
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+
+#if defined(MYSQL_SERVER)
+ virtual int do_before_row_operations(
+ const Slave_reporting_capability *const) override;
+ virtual int do_after_row_operations(const Slave_reporting_capability *const,
+ int) override;
+ virtual int do_exec_row(const Relay_log_info *const) override;
+#endif
+};
+
+/**
+ @class Update_rows_log_event
+
+ Log row updates with a before image. The event contain several
+ update rows for a table. Note that each event contains only rows for
+ one table.
+
+ Also note that the row data consists of pairs of row data: one row
+ for the old data and one row for the new data.
+
+ @internal
+ The inheritance structure is as follows
+
+ Binary_log_event
+ ^
+ |
+ |
+ |
+ Log_event B_l:Rows_event
+ ^ /\
+ | / \
+ | <<vir>>/ \ <<vir>>
+ | / \
+ | / \
+ | / \
+ Rows_log_event B_l:U_R_E
+ \ /
+ \ /
+ \ /
+ \ /
+ \ /
+ \/
+ Update_rows_log_event
+
+
+ B_l: Namespace Binary_log
+ U_R_E: class Update_rows_event
+ @eninternal
+
+*/
+class Update_rows_log_event : public Rows_log_event,
+ public binary_log::Update_rows_event {
+ public:
+ enum {
+ /* Support interface to THD::binlog_prepare_pending_rows_event */
+ TYPE_CODE = binary_log::UPDATE_ROWS_EVENT
+ };
+
+#ifdef MYSQL_SERVER
+ Update_rows_log_event(THD *, TABLE *, const Table_id &table_id,
+ MY_BITMAP const *cols_bi, MY_BITMAP const *cols_ai,
+ bool is_transactional,
+ const unsigned char *extra_row_ndb_info);
+
+ Update_rows_log_event(THD *, TABLE *, const Table_id &table_id,
+ bool is_transactional,
+ const unsigned char *extra_row_ndb_info);
+
+ void init(MY_BITMAP const *cols, const MY_BITMAP &cols_to_subtract);
+#endif
+
+ ~Update_rows_log_event() override;
+
+ Update_rows_log_event(const char *buf,
+ const Format_description_event *description_event);
+
+#ifdef MYSQL_SERVER
+ static bool binlog_row_logging_function(THD *thd, TABLE *table,
+ bool is_transactional,
+ const uchar *before_record,
+ const uchar *after_record);
+ bool read_write_bitmaps_cmp(const TABLE *table) const override {
+ return (bitmap_cmp(get_cols(), table->read_set) &&
+ bitmap_cmp(get_cols_ai(), table->write_set));
+ }
+#endif
+
+ protected:
+ virtual Log_event_type get_general_type_code() override {
+ return (Log_event_type)TYPE_CODE;
+ }
+
+#ifndef MYSQL_SERVER
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+
+#if defined(MYSQL_SERVER)
+ virtual int do_before_row_operations(
+ const Slave_reporting_capability *const) override;
+ virtual int do_after_row_operations(const Slave_reporting_capability *const,
+ int) override;
+ virtual int do_exec_row(const Relay_log_info *const) override;
+
+ virtual int skip_after_image_for_update_event(
+ const Relay_log_info *rli, const uchar *curr_bi_start) override;
+
+ private:
+ /**
+ Auxiliary function used in the (THD*, ...) constructor to
+ determine the type code based on configuration options.
+
+ @param thd_arg The THD object for the session.
+
+ @return One of UPDATE_ROWS_EVENT_V1, PARTIAL_UPDATE_ROWS_EVENT, or
+ UPDATE_ROWS_EVENT.
+ */
+ static binary_log::Log_event_type get_update_rows_event_type(
+ const THD *thd_arg);
+#endif /* defined(MYSQL_SERVER) */
+};
+
+/**
+ @class Delete_rows_log_event
+
+ Log row deletions. The event contain several delete rows for a
+ table. Note that each event contains only rows for one table.
+
+ RESPONSIBILITIES
+
+ - Act as a container for rows that has been deleted on the master
+ and should be deleted on the slave.
+
+ COLLABORATION
+
+ Row_writer
+ Create the event and add rows to the event.
+ Row_reader
+ Extract the rows from the event.
+
+ @internal
+ The inheritance structure is as follows
+
+ Binary_log_event
+ ^
+ |
+ |
+ |
+ Log_event B_l:Rows_event
+ ^ /\
+ | / \
+ | <<vir>>/ \ <<vir>>
+ | / \
+ | / \
+ | / \
+ Rows_log_event B_l:D_R_E
+ \ /
+ \ /
+ \ /
+ \ /
+ \ /
+ \/
+ Delete_rows_log_event
+
+ B_l: Namespace Binary_log
+ D_R_E: class Delete_rows_event
+ @endinternal
+
+*/
+class Delete_rows_log_event : public Rows_log_event,
+ public binary_log::Delete_rows_event {
+ public:
+ enum {
+ /* Support interface to THD::binlog_prepare_pending_rows_event */
+ TYPE_CODE = binary_log::DELETE_ROWS_EVENT
+ };
+
+#ifdef MYSQL_SERVER
+ Delete_rows_log_event(THD *, TABLE *, const Table_id &, bool is_transactional,
+ const unsigned char *extra_row_ndb_info);
+#endif
+ Delete_rows_log_event(const char *buf,
+ const Format_description_event *description_event);
+#ifdef MYSQL_SERVER
+ static bool binlog_row_logging_function(
+ THD *thd, TABLE *table, bool is_transactional, const uchar *before_record,
+ const uchar *after_record MY_ATTRIBUTE((unused)));
+ bool read_write_bitmaps_cmp(const TABLE *table) const override {
+ return bitmap_cmp(get_cols(), table->read_set);
+ }
+#endif
+
+ protected:
+ virtual Log_event_type get_general_type_code() override {
+ return (Log_event_type)TYPE_CODE;
+ }
+
+#ifndef MYSQL_SERVER
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+
+#if defined(MYSQL_SERVER)
+ virtual int do_before_row_operations(
+ const Slave_reporting_capability *const) override;
+ virtual int do_after_row_operations(const Slave_reporting_capability *const,
+ int) override;
+ virtual int do_exec_row(const Relay_log_info *const) override;
+#endif
+};
+
+/**
+ @class Incident_log_event
+
+ Class representing an incident, an occurance out of the ordinary,
+ that happened on the master.
+
+ The event is used to inform the slave that something out of the
+ ordinary happened on the master that might cause the database to be
+ in an inconsistent state.
+ Its the derived class of Incident_event
+
+ @internal
+ The inheritance structure is as follows
+
+ Binary_log_event
+ ^
+ |
+ |
+ B_l:Incident_event Log_event
+ \ /
+ \ /
+ \ /
+ \ /
+ Incident_log_event
+
+ B_l: Namespace Binary_log
+ @endinternal
+
+*/
+class Incident_log_event : public binary_log::Incident_event, public Log_event {
+ public:
+#ifdef MYSQL_SERVER
+ Incident_log_event(THD *thd_arg, enum_incident incident_arg)
+ : binary_log::Incident_event(incident_arg),
+ Log_event(thd_arg, LOG_EVENT_NO_FILTER_F, Log_event::EVENT_NO_CACHE,
+ Log_event::EVENT_IMMEDIATE_LOGGING, header(), footer()) {
+ DBUG_ENTER("Incident_log_event::Incident_log_event");
+ DBUG_PRINT("enter", ("incident: %d", incident_arg));
+ common_header->set_is_valid(incident_arg > INCIDENT_NONE &&
+ incident_arg < INCIDENT_COUNT);
+ DBUG_ASSERT(message == nullptr && message_length == 0);
+ DBUG_VOID_RETURN;
+ }
+
+ Incident_log_event(THD *thd_arg, enum_incident incident_arg,
+ LEX_CSTRING const msg)
+ : binary_log::Incident_event(incident_arg),
+ Log_event(thd_arg, LOG_EVENT_NO_FILTER_F, Log_event::EVENT_NO_CACHE,
+ Log_event::EVENT_IMMEDIATE_LOGGING, header(), footer()) {
+ DBUG_ENTER("Incident_log_event::Incident_log_event");
+ DBUG_PRINT("enter", ("incident: %d", incident_arg));
+ common_header->set_is_valid(incident_arg > INCIDENT_NONE &&
+ incident_arg < INCIDENT_COUNT);
+ DBUG_ASSERT(message == nullptr && message_length == 0);
+ if (!(message = (char *)my_malloc(key_memory_Incident_log_event_message,
+ msg.length + 1, MYF(MY_WME)))) {
+ // The allocation failed. Mark this binlog event as invalid.
+ common_header->set_is_valid(false);
+ DBUG_VOID_RETURN;
+ }
+ strmake(message, msg.str, msg.length);
+ message_length = msg.length;
+ DBUG_VOID_RETURN;
+ }
+#endif
+
+#ifdef MYSQL_SERVER
+ int pack_info(Protocol *) override;
+#endif
+
+ Incident_log_event(const char *buf,
+ const Format_description_event *description_event);
+
+ ~Incident_log_event() override;
+
+#ifndef MYSQL_SERVER
+ virtual void print(FILE *file,
+ PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+
+#if defined(MYSQL_SERVER)
+ virtual int do_apply_event(Relay_log_info const *rli) override;
+ virtual bool write_data_header(Basic_ostream *ostream) override;
+ virtual bool write_data_body(Basic_ostream *ostream) override;
+#endif
+
+ virtual size_t get_data_size() override {
+ return Binary_log_event::INCIDENT_HEADER_LEN + 1 + message_length;
+ }
+
+ virtual bool ends_group() const override { return true; }
+
+ private:
+ const char *description() const;
+};
+
+/**
+ @class Ignorable_log_event
+
+ Base class for ignorable log events is Ignorable_event.
+ Events deriving from this class can be safely ignored
+ by slaves that cannot recognize them.
+
+ Its the derived class of Ignorable_event
+
+ @internal
+ The inheritance structure is as follows
+
+ Binary_log_event
+ ^
+ |
+ |
+ B_l:Ignorable_event Log_event
+ \ /
+ <<virtual>>\ /
+ \ /
+ Ignorable_log_event
+
+ B_l: Namespace Binary_log
+ @endinternal
+*/
+class Ignorable_log_event : public virtual binary_log::Ignorable_event,
+ public Log_event {
+ public:
+#ifdef MYSQL_SERVER
+ Ignorable_log_event(THD *thd_arg)
+ : Log_event(thd_arg, LOG_EVENT_IGNORABLE_F, Log_event::EVENT_STMT_CACHE,
+ Log_event::EVENT_NORMAL_LOGGING, header(), footer()) {
+ DBUG_ENTER("Ignorable_log_event::Ignorable_log_event");
+ common_header->set_is_valid(true);
+ DBUG_VOID_RETURN;
+ }
+#endif
+
+ Ignorable_log_event(const char *buf,
+ const Format_description_event *descr_event);
+ ~Ignorable_log_event() override;
+
+#ifdef MYSQL_SERVER
+ int pack_info(Protocol *) override;
+#endif
+
+#ifndef MYSQL_SERVER
+ virtual void print(FILE *file,
+ PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+
+ virtual size_t get_data_size() override {
+ return Binary_log_event::IGNORABLE_HEADER_LEN;
+ }
+};
+
+/**
+ @class Rows_query_log_event
+ It is used to record the original query for the rows
+ events in RBR.
+ It is the subclass of Ignorable_log_event and Rows_query_event
+
+ @internal
+ The inheritance structure in the current design for the classes is
+ as follows:
+ Binary_log_event
+ ^
+ |
+ |
+ |
+ Log_event B_l:Ignorable_event
+ ^ /\
+ | / \
+ | <<vir>>/ \ <<vir>>
+ | / \
+ | / \
+ | / \
+ Ignorable_log_event B_l:Rows_query_event
+ \ /
+ \ /
+ \ /
+ \ /
+ \ /
+ \/
+ Rows_query_log_event
+
+ B_l : namespace binary_log
+ @endinternal
+*/
+class Rows_query_log_event : public Ignorable_log_event,
+ public binary_log::Rows_query_event {
+ public:
+#ifdef MYSQL_SERVER
+ Rows_query_log_event(THD *thd_arg, const char *query, size_t query_len)
+ : Ignorable_log_event(thd_arg) {
+ DBUG_ENTER("Rows_query_log_event::Rows_query_log_event");
+ common_header->type_code = binary_log::ROWS_QUERY_LOG_EVENT;
+ if (!(m_rows_query =
+ (char *)my_malloc(key_memory_Rows_query_log_event_rows_query,
+ query_len + 1, MYF(MY_WME))))
+ return;
+ snprintf(m_rows_query, query_len + 1, "%s", query);
+ DBUG_PRINT("enter", ("%s", m_rows_query));
+ DBUG_VOID_RETURN;
+ }
+#endif
+
+#ifdef MYSQL_SERVER
+ int pack_info(Protocol *) override;
+ virtual int do_apply_event(Relay_log_info const *rli) override;
+ virtual bool write_data_body(Basic_ostream *ostream) override;
+#endif
+
+ Rows_query_log_event(const char *buf,
+ const Format_description_event *descr_event);
+
+ ~Rows_query_log_event() override {
+ if (m_rows_query) my_free(m_rows_query);
+ m_rows_query = nullptr;
+ }
+#ifndef MYSQL_SERVER
+ virtual void print(FILE *file,
+ PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+ virtual size_t get_data_size() override {
+ return Binary_log_event::IGNORABLE_HEADER_LEN + 1 + strlen(m_rows_query);
+ }
+};
+
+static inline bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache,
+ FILE *file,
+ bool flush_stream) {
+ return my_b_copy_to_file(cache, file) ||
+ (flush_stream ? (fflush(file) || ferror(file)) : 0) ||
+ reinit_io_cache(cache, WRITE_CACHE, 0, false, true);
+}
+
+#ifdef MYSQL_SERVER
+/*****************************************************************************
+
+ Heartbeat Log Event class
+
+ The class is not logged to a binary log, and is not applied on to the slave.
+ The decoding of the event on the slave side is done by its superclass,
+ binary_log::Heartbeat_event.
+
+ ****************************************************************************/
+class Heartbeat_log_event : public binary_log::Heartbeat_event,
+ public Log_event {
+ public:
+ Heartbeat_log_event(const char *buf,
+ const Format_description_event *description_event);
+};
+
+/**
+ The function is called by slave applier in case there are
+ active table filtering rules to force gathering events associated
+ with Query-log-event into an array to execute
+ them once the fate of the Query is determined for execution.
+*/
+bool slave_execute_deferred_events(THD *thd);
+#endif
+
+int append_query_string(const THD *thd, const CHARSET_INFO *csinfo,
+ String const *from, String *to);
+extern TYPELIB binlog_checksum_typelib;
+
+/**
+ @class Gtid_log_event
+
+ This is a subclass if Gtid_event and Log_event. It contains
+ per-transaction fields, including the GTID and logical timestamps
+ used by MTS.
+
+ @internal
+ The inheritance structure is as follows
+
+ Binary_log_event
+ ^
+ |
+ |
+ B_l:Gtid_event Log_event
+ \ /
+ \ /
+ \ /
+ \ /
+ Gtid_log_event
+
+ B_l: Namespace Binary_log
+ @endinternal
+*/
+class Gtid_log_event : public binary_log::Gtid_event, public Log_event {
+ public:
+#ifdef MYSQL_SERVER
+ /**
+ Create a new event using the GTID owned by the given thread.
+ */
+ Gtid_log_event(THD *thd_arg, bool using_trans, int64 last_committed_arg,
+ int64 sequence_number_arg, bool may_have_sbr_stmts_arg,
+ ulonglong original_commit_timestamp_arg,
+ ulonglong immediate_commit_timestamp_arg,
+ uint32_t original_server_version_arg,
+ uint32_t immediate_server_version_arg);
+
+ /**
+ Create a new event using the GTID from the given Gtid_specification
+ without a THD object.
+ */
+ Gtid_log_event(uint32 server_id_arg, bool using_trans,
+ int64 last_committed_arg, int64 sequence_number_arg,
+ bool may_have_sbr_stmts_arg,
+ ulonglong original_commit_timestamp_arg,
+ ulonglong immediate_commit_timestamp_arg,
+ const Gtid_specification spec_arg,
+ uint32_t original_server_version_arg,
+ uint32_t immediate_server_version_arg);
+#endif
+
+#ifdef MYSQL_SERVER
+ int pack_info(Protocol *) override;
+#endif
+ Gtid_log_event(const char *buffer,
+ const Format_description_event *description_event);
+
+ ~Gtid_log_event() override {}
+
+ size_t get_data_size() override {
+ DBUG_EXECUTE_IF("do_not_write_rpl_timestamps", return POST_HEADER_LENGTH;);
+ return POST_HEADER_LENGTH + get_commit_timestamp_length() +
+ net_length_size(transaction_length) + get_server_version_length();
+ }
+
+ size_t get_event_length() { return LOG_EVENT_HEADER_LEN + get_data_size(); }
+
+ private:
+ /// Used internally by both print() and pack_info().
+ size_t to_string(char *buf) const;
+
+#ifdef MYSQL_SERVER
+ /**
+ Writes the post-header to the given output stream.
+
+ This is an auxiliary function typically used by the write() member
+ function.
+
+ @param ostream The output stream to write to.
+
+ @retval true Error.
+ @retval false Success.
+ */
+ bool write_data_header(Basic_ostream *ostream) override;
+ bool write_data_body(Basic_ostream *ostream) override;
+ /**
+ Writes the post-header to the given memory buffer.
+
+ This is an auxiliary function used by write_to_memory.
+
+ @param[in,out] buffer Buffer to which the post-header will be written.
+
+ @return The number of bytes written, i.e., always
+ Gtid_log_event::POST_HEADER_LENGTH.
+ */
+ uint32 write_post_header_to_memory(uchar *buffer);
+
+ /**
+ Writes the body to the given memory buffer.
+
+ This is an auxiliary function used by write_to_memory.
+
+ @param [in,out] buff Buffer to which the data will be written.
+
+ @return The number of bytes written, i.e.,
+ If the transaction did not originated on this server
+ Gtid_event::IMMEDIATE_COMMIT_TIMESTAMP_LENGTH.
+ else
+ FULL_COMMIT_TIMESTAMP_LENGTH.
+ */
+ uint32 write_body_to_memory(uchar *buff);
+#endif
+
+ public:
+#ifndef MYSQL_SERVER
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+#ifdef MYSQL_SERVER
+ /**
+ Writes this event to a memory buffer.
+
+ @param buf The event will be written to this buffer.
+
+ @return the number of bytes written, i.e., always
+ LOG_EVENT_HEADER_LEN + Gtid_log_event::POST_HEADEr_LENGTH.
+ */
+ uint32 write_to_memory(uchar *buf) {
+ common_header->data_written = LOG_EVENT_HEADER_LEN + get_data_size();
+ uint32 len = write_header_to_memory(buf);
+ len += write_post_header_to_memory(buf + len);
+ len += write_body_to_memory(buf + len);
+ return len;
+ }
+#endif
+
+#if defined(MYSQL_SERVER)
+ int do_apply_event(Relay_log_info const *rli) override;
+ int do_update_pos(Relay_log_info *rli) override;
+ enum_skip_reason do_shall_skip(Relay_log_info *rli) override;
+#endif
+
+ /**
+ Return the gtid type for this Gtid_log_event: this can be
+ either ANONYMOUS_GTID, AUTOMATIC_GTID, or ASSIGNED_GTID.
+ */
+ enum_gtid_type get_type() const { return spec.type; }
+
+ /**
+ Return the SID for this GTID. The SID is shared with the
+ Log_event so it should not be modified.
+ */
+ const rpl_sid *get_sid() const { return &sid; }
+ /**
+ Return the SIDNO relative to the global sid_map for this GTID.
+
+ This requires a lookup and possibly even update of global_sid_map,
+ hence global_sid_lock must be held. If global_sid_lock is not
+ held, the caller must pass need_lock=true. If there is an error
+ (e.g. out of memory) while updating global_sid_map, this function
+ returns a negative number.
+
+ @param need_lock If true, the read lock on global_sid_lock is
+ acquired and released inside this function; if false, the read
+ lock or write lock must be held prior to calling this function.
+ @retval SIDNO if successful
+ @retval negative if adding SID to global_sid_map causes an error.
+ */
+ rpl_sidno get_sidno(bool need_lock);
+
+ /**
+ Return the SIDNO relative to the given Sid_map for this GTID.
+
+ This assumes that the Sid_map is local to the thread, and thus
+ does not use locks.
+
+ @param sid_map The sid_map to use.
+ @retval SIDNO if successful.
+ @retval negative if adding SID to sid_map causes an error.
+ */
+ rpl_sidno get_sidno(Sid_map *sid_map) { return sid_map->add_sid(sid); }
+ /// Return the GNO for this GTID.
+ rpl_gno get_gno() const { return spec.gtid.gno; }
+
+ /// string holding the text "SET @@GLOBAL.GTID_NEXT = '"
+ static const char *SET_STRING_PREFIX;
+
+ private:
+ /// Length of SET_STRING_PREFIX
+ static const size_t SET_STRING_PREFIX_LENGTH = 26;
+ /// The maximal length of the entire "SET ..." query.
+ static const size_t MAX_SET_STRING_LENGTH = SET_STRING_PREFIX_LENGTH +
+ binary_log::Uuid::TEXT_LENGTH +
+ 1 + MAX_GNO_TEXT_LENGTH + 1;
+
+ private:
+ /**
+ Internal representation of the GTID. The SIDNO will be
+ uninitialized (value -1) until the first call to get_sidno(bool).
+ */
+ Gtid_specification spec;
+ /// SID for this GTID.
+ rpl_sid sid;
+
+ public:
+ /**
+ Set the transaction length information based on binlog cache size.
+
+ Note that is_checksum_enabled and event_counter are optional parameters.
+ When not specified, the function will assume that no checksum will be used
+ and the informed cache_size is the final transaction size without
+ considering the GTID event size.
+
+ The high level formula that will be used by the function is:
+
+ trx_length = cache_size +
+ cache_checksum_active * cache_events * CRC32_payload +
+ gtid_length +
+ cache_checksum_active * CRC32_payload; // For the GTID.
+
+ @param cache_size The size of the binlog cache in bytes.
+ @param is_checksum_enabled If checksum will be added to events on flush.
+ @param event_counter The amount of events in the cache.
+ */
+ void set_trx_length_by_cache_size(ulonglong cache_size,
+ bool is_checksum_enabled = false,
+ int event_counter = 0);
+};
+
+/**
+ @class Previous_gtids_log_event
+
+ This is the subclass of Previous_gtids_event and Log_event
+ It is used to record the gtid_executed in the last binary log file,
+ for ex after flush logs, or at the starting of the binary log file
+
+ @internal
+ The inheritance structure is as follows
+
+ Binary_log_event
+ ^
+ |
+ |
+B_l:Previous_gtids_event Log_event
+ \ /
+ \ /
+ \ /
+ \ /
+ Previous_gtids_log_event
+
+ B_l: Namespace Binary_log
+ @endinternal
+*/
+class Previous_gtids_log_event : public binary_log::Previous_gtids_event,
+ public Log_event {
+ public:
+#ifdef MYSQL_SERVER
+ Previous_gtids_log_event(const Gtid_set *set);
+#endif
+
+#ifdef MYSQL_SERVER
+ int pack_info(Protocol *) override;
+#endif
+
+ Previous_gtids_log_event(const char *buf,
+ const Format_description_event *description_event);
+ ~Previous_gtids_log_event() override {}
+
+ size_t get_data_size() override { return buf_size; }
+
+#ifndef MYSQL_SERVER
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+#ifdef MYSQL_SERVER
+ bool write(Basic_ostream *ostream) override {
+ if (DBUG_EVALUATE_IF("skip_writing_previous_gtids_log_event", 1, 0) &&
+ /*
+ The skip_writing_previous_gtids_log_event debug point was designed
+ for skipping the writing of the previous_gtids_log_event on binlog
+ files only.
+ */
+ !is_relay_log_event()) {
+ DBUG_PRINT("info",
+ ("skip writing Previous_gtids_log_event because of"
+ "debug option 'skip_writing_previous_gtids_log_event'"));
+ return false;
+ }
+
+ if (DBUG_EVALUATE_IF("write_partial_previous_gtids_log_event", 1, 0) &&
+ /*
+ The write_partial_previous_gtids_log_event debug point was designed
+ for writing a partial previous_gtids_log_event on binlog files only.
+ */
+ !is_relay_log_event()) {
+ DBUG_PRINT("info",
+ ("writing partial Previous_gtids_log_event because of"
+ "debug option 'write_partial_previous_gtids_log_event'"));
+ return (Log_event::write_header(ostream, get_data_size()) ||
+ Log_event::write_data_header(ostream));
+ }
+
+ return (Log_event::write_header(ostream, get_data_size()) ||
+ Log_event::write_data_header(ostream) || write_data_body(ostream) ||
+ Log_event::write_footer(ostream));
+ }
+ bool write_data_body(Basic_ostream *ostream) override;
+#endif
+
+ /// Return the encoded buffer, or NULL on error.
+ const uchar *get_buf() { return buf; }
+ /**
+ Return the formatted string, or NULL on error.
+ The string is allocated using my_malloc and it is the
+ responsibility of the caller to free it.
+ */
+ char *get_str(size_t *length,
+ const Gtid_set::String_format *string_format) const;
+ /// Add all GTIDs from this event to the given Gtid_set.
+ int add_to_set(Gtid_set *gtid_set) const;
+ /*
+ Previous Gtid Log events should always be skipped
+ there is nothing to apply there, whether it is
+ relay log's (generated on Slave) or it is binary log's
+ (generated on Master, copied to slave as relay log).
+ Also, we should not increment slave_skip_counter
+ for this event, hence return EVENT_SKIP_IGNORE.
+ */
+#if defined(MYSQL_SERVER)
+ enum_skip_reason do_shall_skip(Relay_log_info *) override // 1358
+ {
+ return EVENT_SKIP_IGNORE;
+ }
+
+ int do_apply_event(Relay_log_info const *) override { return 0; }
+ int do_update_pos(Relay_log_info *rli) override;
+#endif
+};
+
+/**
+ @class Transaction_context_log_event
+
+ This is the subclass of Transaction_context_event and Log_event
+ This class encodes the transaction_context_log_event.
+
+ @internal
+ The inheritance structure is as follows
+
+ Binary_log_event
+ ^
+ |
+ |
+B_l:Transaction_context_event Log_event
+ \ /
+ \ /
+ \ /
+ \ /
+ Transaction_context_log_event
+
+ B_l: Namespace Binary_log
+ @endinternal
+*/
+class Transaction_context_log_event
+ : public binary_log::Transaction_context_event,
+ public Log_event {
+ private:
+ /// The Sid_map to use for creating the Gtid_set.
+ Sid_map *sid_map;
+ /// A gtid_set which is used to store the transaction set used for
+ /// conflict detection.
+ Gtid_set *snapshot_version;
+
+#ifdef MYSQL_SERVER
+ bool write_data_header(Basic_ostream *ostream) override;
+
+ bool write_data_body(Basic_ostream *ostream) override;
+
+ bool write_snapshot_version(Basic_ostream *ostream);
+
+ bool write_data_set(Basic_ostream *ostream, std::list<const char *> *set);
+#endif
+
+ size_t get_snapshot_version_size();
+
+ static int get_data_set_size(std::list<const char *> *set);
+
+ size_t to_string(char *buf, ulong len) const;
+
+ public:
+#ifdef MYSQL_SERVER
+ Transaction_context_log_event(const char *server_uuid_arg, bool using_trans,
+ my_thread_id thread_id_arg,
+ bool is_gtid_specified_arg);
+#endif
+
+ Transaction_context_log_event(const char *buffer,
+ const Format_description_event *descr_event);
+
+ ~Transaction_context_log_event() override;
+
+ size_t get_data_size() override;
+
+#ifdef MYSQL_SERVER
+ int pack_info(Protocol *protocol) override;
+#endif
+
+#ifndef MYSQL_SERVER
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+
+#if defined(MYSQL_SERVER)
+ int do_apply_event(Relay_log_info const *) override { return 0; }
+ int do_update_pos(Relay_log_info *rli) override;
+#endif
+
+ /**
+ Add a hash which identifies a inserted/updated/deleted row on the
+ ongoing transaction.
+
+ @param[in] hash row identifier
+ */
+ void add_write_set(const char *hash);
+
+ /**
+ Return a pointer to write-set list.
+ */
+ std::list<const char *> *get_write_set() { return &write_set; }
+
+ /**
+ Add a hash which identifies a read row on the ongoing transaction.
+
+ @param[in] hash row identifier
+ */
+ void add_read_set(const char *hash);
+
+ /**
+ Return a pointer to read-set list.
+ */
+ std::list<const char *> *get_read_set() { return &read_set; }
+
+ /**
+ Read snapshot version from encoded buffers.
+ Cannot be executed during data read from file (event constructor),
+ since its required locks will collide with the server gtid state
+ initialization procedure.
+ */
+ bool read_snapshot_version();
+
+ /**
+ Return the transaction snapshot timestamp.
+ */
+ Gtid_set *get_snapshot_version() { return snapshot_version; }
+
+ /**
+ Return the server uuid.
+ */
+ const char *get_server_uuid() { return server_uuid; }
+
+ /**
+ Return the id of the committing thread.
+ */
+ my_thread_id get_thread_id() { return static_cast<my_thread_id>(thread_id); }
+
+ /**
+ Return true if transaction has GTID_NEXT specified, false otherwise.
+ */
+ bool is_gtid_specified() { return gtid_specified == true; }
+};
+
+/**
+ @class View_change_log_event
+
+ This is the subclass of View_change_log_event and Log_event
+ This class created the view_change_log_event which is used as a marker in
+ case a new node joins or leaves the group.
+
+ @internal
+ The inheritance structure is as follows
+
+ Binary_log_event
+ ^
+ |
+ |
+B_l: View_change_event Log_event
+ \ /
+ \ /
+ \ /
+ \ /
+ View_change_log_event
+
+ B_l: Namespace Binary_log
+ @endinternal
+*/
+
+class View_change_log_event : public binary_log::View_change_event,
+ public Log_event {
+ private:
+ size_t to_string(char *buf, ulong len) const;
+
+#ifdef MYSQL_SERVER
+ bool write_data_header(Basic_ostream *ostream) override;
+
+ bool write_data_body(Basic_ostream *ostream) override;
+
+ bool write_data_map(Basic_ostream *ostream,
+ std::map<std::string, std::string> *map);
+#endif
+
+ size_t get_size_data_map(std::map<std::string, std::string> *map);
+
+ public:
+ View_change_log_event(const char *view_id);
+
+ View_change_log_event(const char *buffer,
+ const Format_description_event *descr_event);
+
+ ~View_change_log_event() override;
+
+ size_t get_data_size() override;
+
+#ifdef MYSQL_SERVER
+ int pack_info(Protocol *protocol) override;
+#endif
+
+#ifndef MYSQL_SERVER
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info) const override;
+#endif
+
+#if defined(MYSQL_SERVER)
+ int do_apply_event(Relay_log_info const *rli) override;
+ int do_update_pos(Relay_log_info *rli) override;
+#endif
+
+ /**
+ Returns the view id.
+ */
+ char *get_view_id() { return view_id; }
+
+ /**
+ Sets the certification info in the event
+
+ @note size is calculated on this method as the size of the data
+ might render the log even invalid. Also due to its size doing it
+ here avoid looping over the data multiple times.
+
+ @param[in] info certification info to be written
+ @param[out] event_size the event size after this operation
+ */
+ void set_certification_info(std::map<std::string, std::string> *info,
+ size_t *event_size);
+
+ /**
+ Returns the certification info
+ */
+ std::map<std::string, std::string> *get_certification_info() {
+ return &certification_info;
+ }
+
+ /**
+ Set the certification sequence number
+
+ @param number the sequence number
+ */
+ void set_seq_number(rpl_gno number) { seq_number = number; }
+
+ /**
+ Returns the certification sequence number
+ */
+ rpl_gno get_seq_number() { return seq_number; }
+};
+
+inline bool is_gtid_event(Log_event *evt) {
+ return (evt->get_type_code() == binary_log::GTID_LOG_EVENT ||
+ evt->get_type_code() == binary_log::ANONYMOUS_GTID_LOG_EVENT);
+}
+
+/**
+ The function checks the argument event properties to deduce whether
+ it represents an atomic DDL.
+
+ @param evt a reference to Log_event
+ @return true when the DDL properties are found,
+ false otherwise
+*/
+inline bool is_atomic_ddl_event(Log_event *evt) {
+ return evt != nullptr && evt->get_type_code() == binary_log::QUERY_EVENT &&
+ static_cast<Query_log_event *>(evt)->ddl_xid !=
+ binary_log::INVALID_XID;
+}
+
+/**
+ The function lists all DDL instances that are supported
+ for crash-recovery (WL9175).
+ todo: the supported feature list is supposed to grow. Once
+ a feature has been readied for 2pc through WL7743,9536(7141/7016) etc
+ it needs registering in the function.
+
+ @param thd an Query-log-event creator thread handle
+ @param using_trans
+ The caller must specify the value accoding to the following
+ rules:
+ @c true when
+ - on master the current statement is not processing
+ a table in SE which does not support atomic DDL
+ - on slave the relay-log repository is transactional.
+ @c false otherwise.
+ @return true when the being created (master) or handled (slave) event
+ is 2pc-capable, @c false otherwise.
+*/
+bool is_atomic_ddl(THD *thd, bool using_trans);
+
+#ifdef MYSQL_SERVER
+/**
+ Serialize an binary event to the given output stream. It is more general
+ than call ev->write() directly. The caller will not be affected if any
+ change happens in serialization process. For example, serializing the
+ event in different format.
+ */
+template <class EVENT>
+bool binary_event_serialize(EVENT *ev, Basic_ostream *ostream) {
+ return ev->write(ostream);
+}
+
+/*
+ This is an utility function that adds a quoted identifier into the a buffer.
+ This also escapes any existance of the quote string inside the identifier.
+ */
+size_t my_strmov_quoted_identifier(THD *thd, char *buffer,
+ const char *identifier, size_t length);
+#else
+size_t my_strmov_quoted_identifier(char *buffer, const char *identifier);
+#endif
+size_t my_strmov_quoted_identifier_helper(int q, char *buffer,
+ const char *identifier,
+ size_t length);
+
+/**
+ Read an integer in net_field_length format, guarding against read out of
+ bounds and advancing the position.
+
+ @param[in,out] packet Pointer to buffer to read from. On successful
+ return, the buffer position will be incremented to point to the next
+ byte after what was read.
+
+ @param[in,out] max_length Pointer to the number of bytes in the
+ buffer. If the function would need to look at more than *max_length
+ bytes in order to decode the number, the function will do nothing
+ and return true.
+
+ @param[out] out Pointer where the value will be stored.
+
+ @retval false Success.
+ @retval true Failure, i.e., reached end of buffer.
+*/
+template <typename T>
+bool net_field_length_checked(const uchar **packet, size_t *max_length, T *out);
+
+/**
+ @} (end of group Replication)
+*/
+
+#endif /* _log_event_h */
diff --git a/contrib/libs/libmysql_r/sql/malloc_allocator.h b/contrib/libs/libmysql_r/sql/malloc_allocator.h
new file mode 100644
index 0000000000..9a8abbc8fb
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/malloc_allocator.h
@@ -0,0 +1,149 @@
+/* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef MALLOC_ALLOCATOR_INCLUDED
+#define MALLOC_ALLOCATOR_INCLUDED
+
+#include <limits>
+#include <new>
+#include <utility> // std::forward
+
+#include "my_dbug.h"
+#include "my_sys.h"
+#include "mysql/service_mysql_alloc.h"
+#include "sql/psi_memory_key.h"
+
+/**
+ Malloc_allocator is a C++ STL memory allocator based on my_malloc/my_free.
+
+ This allows for P_S instrumentation of memory allocation done by
+ internally by STL container classes.
+
+ Example usage:
+ vector<int, Malloc_allocator<int>>
+ v((Malloc_allocator<int>(PSI_NOT_INSTRUMENTED)));
+
+ If the type is complicated, you can just write Malloc_allocator<>(psi_key)
+ as a shorthand for Malloc_allocator<My_complicated_type>(psi_key), as all
+ Malloc_allocator instances are implicitly convertible to each other
+ and there is a default template parameter.
+
+ @note allocate() throws std::bad_alloc() similarly to the default
+ STL memory allocator. This is necessary - STL functions which allocates
+ memory expects it. Otherwise these functions will try to use the memory,
+ leading to segfaults if memory allocation was not successful.
+
+ @note This allocator cannot be used for std::basic_string with RHEL 6/7
+ because of this bug:
+ https://bugzilla.redhat.com/show_bug.cgi?id=1546704
+ "Define _GLIBCXX_USE_CXX11_ABI gets ignored by gcc in devtoolset-7"
+*/
+
+template <class T = void *>
+class Malloc_allocator {
+ // This cannot be const if we want to be able to swap.
+ PSI_memory_key m_key;
+
+ public:
+ typedef T value_type;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+
+ typedef T *pointer;
+ typedef const T *const_pointer;
+
+ typedef T &reference;
+ typedef const T &const_reference;
+
+ pointer address(reference r) const { return &r; }
+ const_pointer address(const_reference r) const { return &r; }
+
+#ifdef _MSC_VER
+ Malloc_allocator() : m_key(PSI_NOT_INSTRUMENTED) {}
+#endif
+
+ explicit Malloc_allocator(PSI_memory_key key) : m_key(key) {}
+
+ template <class U>
+ Malloc_allocator(const Malloc_allocator<U> &other MY_ATTRIBUTE((unused)))
+ : m_key(other.psi_key()) {}
+
+ template <class U>
+ Malloc_allocator &operator=(
+ const Malloc_allocator<U> &other MY_ATTRIBUTE((unused))) {
+ DBUG_ASSERT(m_key == other.psi_key()); // Don't swap key.
+ }
+
+ pointer allocate(size_type n, const_pointer hint MY_ATTRIBUTE((unused)) = 0) {
+ if (n == 0) return NULL;
+ if (n > max_size()) throw std::bad_alloc();
+
+ pointer p = static_cast<pointer>(
+ my_malloc(m_key, n * sizeof(T), MYF(MY_WME | ME_FATALERROR)));
+ if (p == NULL) throw std::bad_alloc();
+ return p;
+ }
+
+ void deallocate(pointer p, size_type) { my_free(p); }
+
+ template <class U, class... Args>
+ void construct(U *p, Args &&... args) {
+ DBUG_ASSERT(p != NULL);
+ try {
+ ::new ((void *)p) U(std::forward<Args>(args)...);
+ } catch (...) {
+ DBUG_ASSERT(false); // Constructor should not throw an exception.
+ }
+ }
+
+ void destroy(pointer p) {
+ DBUG_ASSERT(p != NULL);
+ try {
+ p->~T();
+ } catch (...) {
+ DBUG_ASSERT(false); // Destructor should not throw an exception
+ }
+ }
+
+ size_type max_size() const {
+ return std::numeric_limits<size_t>::max() / sizeof(T);
+ }
+
+ template <class U>
+ struct rebind {
+ typedef Malloc_allocator<U> other;
+ };
+
+ PSI_memory_key psi_key() const { return m_key; }
+};
+
+template <class T>
+bool operator==(const Malloc_allocator<T> &a1, const Malloc_allocator<T> &a2) {
+ return a1.psi_key() == a2.psi_key();
+}
+
+template <class T>
+bool operator!=(const Malloc_allocator<T> &a1, const Malloc_allocator<T> &a2) {
+ return a1.psi_key() != a2.psi_key();
+}
+
+#endif // MALLOC_ALLOCATOR_INCLUDED
diff --git a/contrib/libs/libmysql_r/sql/mdl.h b/contrib/libs/libmysql_r/sql/mdl.h
new file mode 100644
index 0000000000..06e7edc25d
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/mdl.h
@@ -0,0 +1,1756 @@
+#ifndef MDL_H
+#define MDL_H
+/* Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <string.h>
+#include <sys/types.h>
+#include <algorithm>
+#include <new>
+#include <unordered_map>
+
+#include "m_string.h"
+#include "my_alloc.h"
+#include "my_compiler.h"
+#include "my_dbug.h"
+#include "my_inttypes.h"
+#include "my_psi_config.h"
+#include "my_sys.h"
+#include "my_systime.h" // Timout_type
+#include "mysql/components/services/mysql_cond_bits.h"
+#include "mysql/components/services/mysql_mutex_bits.h"
+#include "mysql/components/services/mysql_rwlock_bits.h"
+#include "mysql/components/services/psi_mdl_bits.h"
+#include "mysql/components/services/psi_stage_bits.h"
+#include "mysql/psi/mysql_rwlock.h"
+#include "mysql_com.h"
+#include "sql/sql_plist.h"
+#include "template_utils.h"
+
+class MDL_context;
+class MDL_lock;
+class MDL_ticket;
+class THD;
+struct LF_PINS;
+struct MDL_key;
+struct MEM_ROOT;
+
+/**
+ @def ENTER_COND(C, M, S, O)
+ Start a wait on a condition.
+ @param C the condition to wait on
+ @param M the associated mutex
+ @param S the new stage to enter
+ @param O the previous stage
+ @sa EXIT_COND().
+*/
+#define ENTER_COND(C, M, S, O) \
+ enter_cond(C, M, S, O, __func__, __FILE__, __LINE__)
+
+/**
+ @def EXIT_COND(S)
+ End a wait on a condition
+ @param S the new stage to enter
+*/
+#define EXIT_COND(S) exit_cond(S, __func__, __FILE__, __LINE__)
+
+/**
+ An interface to separate the MDL module from the THD, and the rest of the
+ server code.
+ */
+
+class MDL_context_owner {
+ public:
+ virtual ~MDL_context_owner() {}
+
+ /**
+ Enter a condition wait.
+ For @c enter_cond() / @c exit_cond() to work the mutex must be held before
+ @c enter_cond(); this mutex must then be released before @c exit_cond().
+ Usage must be: lock mutex; enter_cond(); your code; unlock mutex;
+ exit_cond().
+ @param cond the condition to wait on
+ @param mutex the associated mutex
+ @param [in] stage the stage to enter, or NULL
+ @param [out] old_stage the previous stage, or NULL
+ @param src_function function name of the caller
+ @param src_file file name of the caller
+ @param src_line line number of the caller
+ @sa ENTER_COND(), THD::enter_cond()
+ @sa EXIT_COND(), THD::exit_cond()
+ */
+ virtual void enter_cond(mysql_cond_t *cond, mysql_mutex_t *mutex,
+ const PSI_stage_info *stage,
+ PSI_stage_info *old_stage, const char *src_function,
+ const char *src_file, int src_line) = 0;
+
+ /**
+ End a wait on a condition
+ @param [in] stage the new stage to enter
+ @param src_function function name of the caller
+ @param src_file file name of the caller
+ @param src_line line number of the caller
+ @sa ENTER_COND(), THD::enter_cond()
+ @sa EXIT_COND(), THD::exit_cond()
+ */
+ virtual void exit_cond(const PSI_stage_info *stage, const char *src_function,
+ const char *src_file, int src_line) = 0;
+ /**
+ Has the owner thread been killed?
+ */
+ virtual int is_killed() const = 0;
+
+ /**
+ Does the owner still have connection to the client?
+ */
+ virtual bool is_connected() = 0;
+
+ /**
+ Within MDL subsystem this one is only used for DEBUG_SYNC.
+ Do not use it to peek/poke into other parts of THD from MDL.
+ However it is OK to use this method in callbacks provided
+ by SQL-layer to MDL subsystem (since SQL-layer has full
+ access to THD anyway).
+
+ @warning For some derived classes implementation of this method
+ can return nullptr. Calling side must be ready to handle
+ this case.
+ */
+ virtual THD *get_thd() = 0;
+
+ /**
+ @see THD::notify_shared_lock()
+ */
+ virtual void notify_shared_lock(MDL_context_owner *in_use,
+ bool needs_thr_lock_abort) = 0;
+
+ /**
+ Notify/get permission from interested storage engines before acquiring
+ exclusive lock for the key.
+
+ The returned argument 'victimized' specify reason for lock
+ not granted. If 'true', lock was refused in an attempt to
+ resolve a possible MDL->GSL deadlock. Locking may then be retried.
+
+ @return False if notification was successful and it is OK to acquire lock,
+ True if one of SEs asks to abort lock acquisition.
+ */
+ virtual bool notify_hton_pre_acquire_exclusive(const MDL_key *mdl_key,
+ bool *victimized) = 0;
+ /**
+ Notify interested storage engines that we have just released exclusive
+ lock for the key.
+ */
+ virtual void notify_hton_post_release_exclusive(const MDL_key *mdl_key) = 0;
+
+ /**
+ Get random seed specific to this THD to be used for initialization
+ of PRNG for the MDL_context.
+ */
+ virtual uint get_rand_seed() const = 0;
+};
+
+/**
+ Type of metadata lock request.
+
+ @sa Comments for MDL_object_lock::can_grant_lock() and
+ MDL_scoped_lock::can_grant_lock() for details.
+*/
+
+enum enum_mdl_type {
+ /*
+ An intention exclusive metadata lock. Used only for scoped locks.
+ Owner of this type of lock can acquire upgradable exclusive locks on
+ individual objects.
+ This lock type is also used when doing lookups in the dictionary
+ cache. When acquiring objects in a schema, we lock the schema with IX
+ to prevent the schema from being deleted. This should conceptually
+ be an IS lock, but it would have the same behavior as the current IX.
+ Compatible with other IX locks, but is incompatible with scoped S and
+ X locks.
+ */
+ MDL_INTENTION_EXCLUSIVE = 0,
+ /*
+ A shared metadata lock.
+ To be used in cases when we are interested in object metadata only
+ and there is no intention to access object data (e.g. for stored
+ routines or during preparing prepared statements).
+ We also mis-use this type of lock for open HANDLERs, since lock
+ acquired by this statement has to be compatible with lock acquired
+ by LOCK TABLES ... WRITE statement, i.e. SNRW (We can't get by by
+ acquiring S lock at HANDLER ... OPEN time and upgrading it to SR
+ lock for HANDLER ... READ as it doesn't solve problem with need
+ to abort DML statements which wait on table level lock while having
+ open HANDLER in the same connection).
+ To avoid deadlock which may occur when SNRW lock is being upgraded to
+ X lock for table on which there is an active S lock which is owned by
+ thread which waits in its turn for table-level lock owned by thread
+ performing upgrade we have to use thr_abort_locks_for_thread()
+ facility in such situation.
+ This problem does not arise for locks on stored routines as we don't
+ use SNRW locks for them. It also does not arise when S locks are used
+ during PREPARE calls as table-level locks are not acquired in this
+ case.
+ */
+ MDL_SHARED,
+ /*
+ A high priority shared metadata lock.
+ Used for cases when there is no intention to access object data (i.e.
+ data in the table).
+ "High priority" means that, unlike other shared locks, it is granted
+ ignoring pending requests for exclusive locks. Intended for use in
+ cases when we only need to access metadata and not data, e.g. when
+ filling an INFORMATION_SCHEMA table.
+ Since SH lock is compatible with SNRW lock, the connection that
+ holds SH lock lock should not try to acquire any kind of table-level
+ or row-level lock, as this can lead to a deadlock. Moreover, after
+ acquiring SH lock, the connection should not wait for any other
+ resource, as it might cause starvation for X locks and a potential
+ deadlock during upgrade of SNW or SNRW to X lock (e.g. if the
+ upgrading connection holds the resource that is being waited for).
+ */
+ MDL_SHARED_HIGH_PRIO,
+ /*
+ A shared metadata lock for cases when there is an intention to read data
+ from table.
+ A connection holding this kind of lock can read table metadata and read
+ table data (after acquiring appropriate table and row-level locks).
+ This means that one can only acquire TL_READ, TL_READ_NO_INSERT, and
+ similar table-level locks on table if one holds SR MDL lock on it.
+ To be used for tables in SELECTs, subqueries, and LOCK TABLE ... READ
+ statements.
+ */
+ MDL_SHARED_READ,
+ /*
+ A shared metadata lock for cases when there is an intention to modify
+ (and not just read) data in the table.
+ A connection holding SW lock can read table metadata and modify or read
+ table data (after acquiring appropriate table and row-level locks).
+ To be used for tables to be modified by INSERT, UPDATE, DELETE
+ statements, but not LOCK TABLE ... WRITE or DDL). Also taken by
+ SELECT ... FOR UPDATE.
+ */
+ MDL_SHARED_WRITE,
+ /*
+ A version of MDL_SHARED_WRITE lock which has lower priority than
+ MDL_SHARED_READ_ONLY locks. Used by DML statements modifying
+ tables and using the LOW_PRIORITY clause.
+ */
+ MDL_SHARED_WRITE_LOW_PRIO,
+ /*
+ An upgradable shared metadata lock which allows concurrent updates and
+ reads of table data.
+ A connection holding this kind of lock can read table metadata and read
+ table data. It should not modify data as this lock is compatible with
+ SRO locks.
+ Can be upgraded to SNW, SNRW and X locks. Once SU lock is upgraded to X
+ or SNRW lock data modification can happen freely.
+ To be used for the first phase of ALTER TABLE.
+ */
+ MDL_SHARED_UPGRADABLE,
+ /*
+ A shared metadata lock for cases when we need to read data from table
+ and block all concurrent modifications to it (for both data and metadata).
+ Used by LOCK TABLES READ statement.
+ */
+ MDL_SHARED_READ_ONLY,
+ /*
+ An upgradable shared metadata lock which blocks all attempts to update
+ table data, allowing reads.
+ A connection holding this kind of lock can read table metadata and read
+ table data.
+ Can be upgraded to X metadata lock.
+ Note, that since this type of lock is not compatible with SNRW or SW
+ lock types, acquiring appropriate engine-level locks for reading
+ (TL_READ* for MyISAM, shared row locks in InnoDB) should be
+ contention-free.
+ To be used for the first phase of ALTER TABLE, when copying data between
+ tables, to allow concurrent SELECTs from the table, but not UPDATEs.
+ */
+ MDL_SHARED_NO_WRITE,
+ /*
+ An upgradable shared metadata lock which allows other connections
+ to access table metadata, but not data.
+ It blocks all attempts to read or update table data, while allowing
+ INFORMATION_SCHEMA and SHOW queries.
+ A connection holding this kind of lock can read table metadata modify and
+ read table data.
+ Can be upgraded to X metadata lock.
+ To be used for LOCK TABLES WRITE statement.
+ Not compatible with any other lock type except S and SH.
+ */
+ MDL_SHARED_NO_READ_WRITE,
+ /*
+ An exclusive metadata lock.
+ A connection holding this lock can modify both table's metadata and data.
+ No other type of metadata lock can be granted while this lock is held.
+ To be used for CREATE/DROP/RENAME TABLE statements and for execution of
+ certain phases of other DDL statements.
+ */
+ MDL_EXCLUSIVE,
+ /* This should be the last !!! */
+ MDL_TYPE_END
+};
+
+/** Duration of metadata lock. */
+
+enum enum_mdl_duration {
+ /**
+ Locks with statement duration are automatically released at the end
+ of statement or transaction.
+ */
+ MDL_STATEMENT = 0,
+ /**
+ Locks with transaction duration are automatically released at the end
+ of transaction.
+ */
+ MDL_TRANSACTION,
+ /**
+ Locks with explicit duration survive the end of statement and transaction.
+ They have to be released explicitly by calling MDL_context::release_lock().
+ */
+ MDL_EXPLICIT,
+ /* This should be the last ! */
+ MDL_DURATION_END
+};
+
+/** Maximal length of key for metadata locking subsystem. */
+#define MAX_MDLKEY_LENGTH (1 + NAME_LEN + 1 + NAME_LEN + 1)
+
+/**
+ Metadata lock object key.
+
+ A lock is requested or granted based on a fully qualified name and type.
+ E.g. They key for a table consists of @<0 (=table)@>+@<database@>+@<table
+ name@>. Elsewhere in the comments this triple will be referred to simply as
+ "key" or "name".
+*/
+
+struct MDL_key {
+ public:
+#ifdef HAVE_PSI_INTERFACE
+ static void init_psi_keys();
+#endif
+
+ /**
+ Object namespaces.
+ Sic: when adding a new member to this enum make sure to
+ update m_namespace_to_wait_state_name array in mdl.cc!
+
+ Different types of objects exist in different namespaces
+ - GLOBAL is used for the global read lock.
+ - TABLESPACE is for tablespaces.
+ - SCHEMA is for schemas (aka databases).
+ - TABLE is for tables and views.
+ - FUNCTION is for stored functions.
+ - PROCEDURE is for stored procedures.
+ - TRIGGER is for triggers.
+ - EVENT is for event scheduler events.
+ - COMMIT is for enabling the global read lock to block commits.
+ - USER_LEVEL_LOCK is for user-level locks.
+ - LOCKING_SERVICE is for the name plugin RW-lock service
+ - SRID is for spatial reference systems
+ - ACL_CACHE is for ACL caches
+ - COLUMN_STATISTICS is for column statistics, such as histograms
+ - BACKUP_LOCK is to block any operations that could cause
+ inconsistent backup. Such operations are most DDL statements,
+ and some administrative statements.
+ - RESOURCE_GROUPS is for resource groups.
+ - FOREIGN_KEY is for foreign key names.
+ - CHECK_CONSTRAINT is for check constraint names.
+ Note that requests waiting for user-level locks get special
+ treatment - waiting is aborted if connection to client is lost.
+ */
+ enum enum_mdl_namespace {
+ GLOBAL = 0,
+ TABLESPACE,
+ SCHEMA,
+ TABLE,
+ FUNCTION,
+ PROCEDURE,
+ TRIGGER,
+ EVENT,
+ COMMIT,
+ USER_LEVEL_LOCK,
+ LOCKING_SERVICE,
+ SRID,
+ ACL_CACHE,
+ COLUMN_STATISTICS,
+ BACKUP_LOCK,
+ RESOURCE_GROUPS,
+ FOREIGN_KEY,
+ CHECK_CONSTRAINT,
+ /* This should be the last ! */
+ NAMESPACE_END
+ };
+
+ const uchar *ptr() const { return pointer_cast<const uchar *>(m_ptr); }
+ uint length() const { return m_length; }
+
+ const char *db_name() const { return m_ptr + 1; }
+ uint db_name_length() const { return m_db_name_length; }
+
+ const char *name() const {
+ return (use_normalized_object_name() ? m_ptr + m_length
+ : m_ptr + m_db_name_length + 2);
+ }
+ uint name_length() const { return m_object_name_length; }
+
+ const char *col_name() const {
+ DBUG_ASSERT(!use_normalized_object_name());
+
+ if (m_db_name_length + m_object_name_length + 3 < m_length) {
+ /* A column name was stored in the key buffer. */
+ return m_ptr + m_db_name_length + m_object_name_length + 3;
+ }
+
+ /* No column name stored. */
+ return NULL;
+ }
+
+ uint col_name_length() const {
+ DBUG_ASSERT(!use_normalized_object_name());
+
+ if (m_db_name_length + m_object_name_length + 3 < m_length) {
+ /* A column name was stored in the key buffer. */
+ return m_length - m_db_name_length - m_object_name_length - 4;
+ }
+
+ /* No column name stored. */
+ return 0;
+ }
+
+ enum_mdl_namespace mdl_namespace() const {
+ return (enum_mdl_namespace)(m_ptr[0]);
+ }
+
+ /**
+ Construct a metadata lock key from a triplet (mdl_namespace,
+ database and name).
+
+ @remark The key for a table is @<mdl_namespace@>+@<database name@>+@<table
+ name@>
+
+ @param mdl_namespace Id of namespace of object to be locked
+ @param db Name of database to which the object belongs
+ @param name Name of of the object
+ */
+ void mdl_key_init(enum_mdl_namespace mdl_namespace, const char *db,
+ const char *name) {
+ m_ptr[0] = (char)mdl_namespace;
+
+ DBUG_ASSERT(!use_normalized_object_name());
+
+ /*
+ It is responsibility of caller to ensure that db and object names
+ are not longer than NAME_LEN. Still we play safe and try to avoid
+ buffer overruns.
+
+ Implicit tablespace names in InnoDB may be longer than NAME_LEN.
+ We will lock based on the first NAME_LEN characters.
+
+ TODO: The patch acquires metadata locks on the NAME_LEN
+ first bytest of the tablespace names. For long names,
+ the consequence of locking on this prefix is
+ that locking a single implicit tablespace might end up
+ effectively lock all implicit tablespaces in the same
+ schema. A possible fix is to lock on a prefix of length
+ NAME_LEN * 2, since this is the real buffer size of
+ the metadata lock key. Dependencies from the PFS
+ implementation, possibly relying on the key format,
+ must be investigated first, though.
+ */
+ DBUG_ASSERT(strlen(db) <= NAME_LEN);
+ DBUG_ASSERT((mdl_namespace == TABLESPACE) || (strlen(name) <= NAME_LEN));
+ m_db_name_length =
+ static_cast<uint16>(strmake(m_ptr + 1, db, NAME_LEN) - m_ptr - 1);
+ m_object_name_length = static_cast<uint16>(
+ strmake(m_ptr + m_db_name_length + 2, name, NAME_LEN) - m_ptr -
+ m_db_name_length - 2);
+ m_length = m_db_name_length + m_object_name_length + 3;
+ }
+
+ /**
+ Construct a metadata lock key from a quadruplet (mdl_namespace,
+ database, table and column name).
+
+ @remark The key for a column is
+ @<mdl_namespace@>+@<database name@>+@<table name@>+@<column name@>
+
+ @param mdl_namespace Id of namespace of object to be locked
+ @param db Name of database to which the object belongs
+ @param name Name of of the object
+ @param column_name Name of of the column
+ */
+ void mdl_key_init(enum_mdl_namespace mdl_namespace, const char *db,
+ const char *name, const char *column_name) {
+ m_ptr[0] = (char)mdl_namespace;
+ char *start;
+ char *end;
+
+ DBUG_ASSERT(!use_normalized_object_name());
+
+ DBUG_ASSERT(strlen(db) <= NAME_LEN);
+ start = m_ptr + 1;
+ end = strmake(start, db, NAME_LEN);
+ m_db_name_length = static_cast<uint16>(end - start);
+
+ DBUG_ASSERT(strlen(name) <= NAME_LEN);
+ start = end + 1;
+ end = strmake(start, name, NAME_LEN);
+ m_object_name_length = static_cast<uint16>(end - start);
+
+ size_t col_len = strlen(column_name);
+ DBUG_ASSERT(col_len <= NAME_LEN);
+ start = end + 1;
+ size_t remaining =
+ MAX_MDLKEY_LENGTH - m_db_name_length - m_object_name_length - 3;
+ uint16 extra_length = 0;
+
+ /*
+ In theory:
+ - schema name is up to NAME_LEN characters
+ - object name is up to NAME_LEN characters
+ - column name is up to NAME_LEN characters
+ - NAME_LEN is 64 characters
+ - 1 character is up to 3 bytes (UTF8MB3),
+ and when moving to UTF8MB4, up to 4 bytes.
+ - Storing a SCHEMA + OBJECT MDL key
+ can take up to 387 bytes
+ - Storing a SCHEMA + OBJECT + COLUMN MDL key
+ can take up to 580 bytes.
+
+ In practice:
+ - full storage is allocated for SCHEMA + OBJECT only,
+ storage for COLUMN is **NOT** reserved.
+ - SCHEMA and OBJECT names are typically shorter,
+ and are not using systematically multi-bytes characters
+ for each character, so that less space is required.
+ - MDL keys that are not COLUMN_STATISTICS
+ are stored in full, without truncation.
+
+ For the COLUMN_STATISTICS name space:
+ - either the full SCHEMA + OBJECT + COLUMN key fits
+ within 387 bytes, in which case the fully qualified
+ column name is stored,
+ leading to MDL locks per column (as intended)
+ - or the SCHEMA and OBJECT names are very long,
+ so that not enough room is left to store a column name,
+ in which case the MDL key is truncated to be
+ COLUMN_STATISTICS + SCHEMA + NAME.
+ In this case, MDL locks for columns col_X and col_Y
+ in table LONG_FOO.LONG_BAR will both share the same
+ key LONG_FOO.LONG_BAR, in effect providing a lock
+ granularity not per column but per table.
+ This is a degraded mode of operation,
+ which serializes MDL access to columns
+ (for tables with a very long fully qualified name),
+ to reduce the memory footprint for all MDL access.
+
+ To be revised if the MDL key buffer is allocated dynamically
+ instead.
+ */
+
+ static_assert(MAX_MDLKEY_LENGTH == 387, "UTF8MB3");
+
+ /*
+ Check if there is room to store the whole column name.
+ This code is not trying to store truncated column names,
+ to avoid cutting column_name in the middle of a
+ multi-byte character.
+ */
+ if (remaining >= col_len + 1) {
+ end = strmake(start, column_name, remaining);
+ extra_length = static_cast<uint16>(end - start) + 1; // With \0
+ }
+ m_length = m_db_name_length + m_object_name_length + 3 + extra_length;
+ DBUG_ASSERT(m_length <= MAX_MDLKEY_LENGTH);
+ }
+
+ /**
+ Construct a metadata lock key from a quadruplet (mdl_namespace, database,
+ normalized object name buffer and the object name).
+
+ @remark The key for a routine/event/resource group/trigger is
+ @<mdl_namespace@>+@<database name@>+@<normalized object name@>
+ additionaly @<object name@> is stored in the same buffer for information
+ purpose if buffer has sufficent space.
+
+ Routine, Event and Resource group names are case sensitive and accent
+ sensitive. So normalized object name is used to form a MDL_key.
+
+ With the UTF8MB3 charset space reserved for the db name/object name is
+ 64 * 3 bytes. utf8_general_ci collation is used for the Routine, Event and
+ Resource group names. With this collation, the normalized object name uses
+ just 2 bytes for each character (max length = 64 * 2 bytes). MDL_key has
+ still some space to store the object names. If there is a sufficient space
+ for the object name in the MDL_key then it is stored in the MDL_key (similar
+ to the column names in the MDL_key). Actual object name is used by the PFS.
+ Not listing actual object name from the PFS should be OK when there is no
+ space to store it (instead of increasing the MDL_key size). Object name is
+ not used in the key comparisons. So only (mdl_namespace + strlen(db) + 1 +
+ normalized_name_len + 1) value is stored in the m_length member.
+
+ @param mdl_namespace Id of namespace of object to be locked.
+ @param db Name of database to which the object belongs.
+ @param normalized_name Normalized name of the object.
+ @param normalized_name_len Length of the normalized object name.
+ @param name Name of the object.
+ */
+ void mdl_key_init(enum_mdl_namespace mdl_namespace, const char *db,
+ const char *normalized_name, size_t normalized_name_len,
+ const char *name) {
+ m_ptr[0] = (char)mdl_namespace;
+
+ /*
+ FUNCTION, PROCEDURE, EVENT and RESOURCE_GROUPS names are case and accent
+ insensitive. For other objects key should not be formed from this method.
+ */
+ DBUG_ASSERT(use_normalized_object_name());
+
+ DBUG_ASSERT(strlen(db) <= NAME_LEN && strlen(name) <= NAME_LEN &&
+ normalized_name_len <= NAME_CHAR_LEN * 2);
+
+ // Database name.
+ m_db_name_length =
+ static_cast<uint16>(strmake(m_ptr + 1, db, NAME_LEN) - m_ptr - 1);
+
+ // Normalized object name.
+ m_length = static_cast<uint16>(m_db_name_length + normalized_name_len + 3);
+ memcpy(m_ptr + m_db_name_length + 2, normalized_name, normalized_name_len);
+ *(m_ptr + m_length - 1) = 0;
+
+ /*
+ Copy name of the object if there is a sufficient space to store the name
+ in the MDL key. This code is not trying to store truncated object names,
+ to avoid cutting object_name in the middle of a multi-byte character.
+ */
+ if (strlen(name) < static_cast<size_t>(MAX_MDLKEY_LENGTH - m_length)) {
+ m_object_name_length = static_cast<uint16>(
+ (strmake(m_ptr + m_length, name, MAX_MDLKEY_LENGTH - m_length - 1) -
+ m_ptr - m_length));
+ } else {
+ m_object_name_length = 0;
+ *(m_ptr + m_length) = 0;
+ }
+
+ DBUG_ASSERT(m_length + m_object_name_length < MAX_MDLKEY_LENGTH);
+ }
+
+ /**
+ Construct a metadata lock key from namespace and partial key, which
+ contains info about object database and name.
+
+ @remark The partial key must be "<database>\0<name>\0".
+
+ @param mdl_namespace Id of namespace of object to be locked
+ @param part_key Partial key.
+ @param part_key_length Partial key length
+ @param db_length Database name length.
+ */
+ void mdl_key_init(enum_mdl_namespace mdl_namespace, const char *part_key,
+ size_t part_key_length, size_t db_length) {
+ /*
+ Key suffix provided should be in compatible format and
+ its components should adhere to length restrictions.
+ */
+ DBUG_ASSERT(strlen(part_key) == db_length);
+ DBUG_ASSERT(db_length + 1 + strlen(part_key + db_length + 1) + 1 ==
+ part_key_length);
+ DBUG_ASSERT(db_length <= NAME_LEN);
+ DBUG_ASSERT(part_key_length <= NAME_LEN + 1 + NAME_LEN + 1);
+
+ m_ptr[0] = (char)mdl_namespace;
+ /*
+ Partial key of objects with normalized object name can not be used to
+ initialize MDL key.
+ */
+ DBUG_ASSERT(!use_normalized_object_name());
+
+ memcpy(m_ptr + 1, part_key, part_key_length);
+ m_length = static_cast<uint16>(part_key_length + 1);
+ m_db_name_length = static_cast<uint16>(db_length);
+ m_object_name_length = m_length - m_db_name_length - 3;
+ }
+ void mdl_key_init(const MDL_key *rhs) {
+ uint16 copy_length = rhs->use_normalized_object_name()
+ ? rhs->m_length + rhs->m_object_name_length + 1
+ : rhs->m_length;
+ memcpy(m_ptr, rhs->m_ptr, copy_length);
+ m_length = rhs->m_length;
+ m_db_name_length = rhs->m_db_name_length;
+ m_object_name_length = rhs->m_object_name_length;
+ }
+ void reset() {
+ m_ptr[0] = NAMESPACE_END;
+ m_db_name_length = 0;
+ m_object_name_length = 0;
+ m_length = 0;
+ }
+ bool is_equal(const MDL_key *rhs) const {
+ return (m_length == rhs->m_length &&
+ memcmp(m_ptr, rhs->m_ptr, m_length) == 0);
+ }
+ /**
+ Compare two MDL keys lexicographically.
+ */
+ int cmp(const MDL_key *rhs) const {
+ /*
+ For the keys with the normalized names, there is a possibility of getting
+ '\0' in its middle. So only key content comparison would yield incorrect
+ result. Hence comparing key length too when keys are equal.
+ For other keys, key buffer is always '\0'-terminated. Since key character
+ set is utf-8, we can safely assume that no character starts with a zero
+ byte.
+ */
+ int res = memcmp(m_ptr, rhs->m_ptr, std::min(m_length, rhs->m_length));
+ if (res == 0) res = m_length - rhs->m_length;
+ return res;
+ }
+
+ MDL_key(const MDL_key &rhs) { mdl_key_init(&rhs); }
+
+ MDL_key &operator=(const MDL_key &rhs) {
+ mdl_key_init(&rhs);
+ return *this;
+ }
+
+ MDL_key(enum_mdl_namespace namespace_arg, const char *db_arg,
+ const char *name_arg) {
+ mdl_key_init(namespace_arg, db_arg, name_arg);
+ }
+ MDL_key() {} /* To use when part of MDL_request. */
+
+ /**
+ Get thread state name to be used in case when we have to
+ wait on resource identified by key.
+ */
+ const PSI_stage_info *get_wait_state_name() const {
+ return &m_namespace_to_wait_state_name[(int)mdl_namespace()];
+ }
+
+ private:
+ /**
+ Check if normalized object name should be used.
+
+ @return true if normlized object name should be used, false
+ otherwise.
+ */
+ bool use_normalized_object_name() const {
+ return (mdl_namespace() == FUNCTION || mdl_namespace() == PROCEDURE ||
+ mdl_namespace() == EVENT || mdl_namespace() == RESOURCE_GROUPS ||
+ mdl_namespace() == TRIGGER);
+ }
+
+ private:
+ uint16 m_length{0};
+ uint16 m_db_name_length{0};
+ uint16 m_object_name_length{0};
+ char m_ptr[MAX_MDLKEY_LENGTH]{0};
+ static PSI_stage_info m_namespace_to_wait_state_name[NAMESPACE_END];
+};
+
+/**
+ A pending metadata lock request.
+
+ A lock request and a granted metadata lock are represented by
+ different classes because they have different allocation
+ sites and hence different lifetimes. The allocation of lock requests is
+ controlled from outside of the MDL subsystem, while allocation of granted
+ locks (tickets) is controlled within the MDL subsystem.
+*/
+
+class MDL_request {
+ public:
+ /** Type of metadata lock. */
+ enum_mdl_type type{MDL_INTENTION_EXCLUSIVE};
+ /** Duration for requested lock. */
+ enum_mdl_duration duration{MDL_STATEMENT};
+
+ /**
+ Pointers for participating in the list of lock requests for this context.
+ */
+ MDL_request *next_in_list{nullptr};
+ MDL_request **prev_in_list{nullptr};
+ /**
+ Pointer to the lock ticket object for this lock request.
+ Valid only if this lock request is satisfied.
+ */
+ MDL_ticket *ticket{nullptr};
+
+ /** A lock is requested based on a fully qualified name and type. */
+ MDL_key key;
+
+ const char *m_src_file{nullptr};
+ uint m_src_line{0};
+
+ public:
+ static void *operator new(size_t size, MEM_ROOT *mem_root,
+ const std::nothrow_t &arg MY_ATTRIBUTE((unused)) =
+ std::nothrow) noexcept {
+ return mem_root->Alloc(size);
+ }
+
+ static void operator delete(void *, MEM_ROOT *,
+ const std::nothrow_t &)noexcept {}
+
+ void init_with_source(MDL_key::enum_mdl_namespace namespace_arg,
+ const char *db_arg, const char *name_arg,
+ enum_mdl_type mdl_type_arg,
+ enum_mdl_duration mdl_duration_arg,
+ const char *src_file, uint src_line);
+ void init_by_key_with_source(const MDL_key *key_arg,
+ enum_mdl_type mdl_type_arg,
+ enum_mdl_duration mdl_duration_arg,
+ const char *src_file, uint src_line);
+ void init_by_part_key_with_source(MDL_key::enum_mdl_namespace namespace_arg,
+ const char *part_key_arg,
+ size_t part_key_length_arg,
+ size_t db_length_arg,
+ enum_mdl_type mdl_type_arg,
+ enum_mdl_duration mdl_duration_arg,
+ const char *src_file, uint src_line);
+ /** Set type of lock request. Can be only applied to pending locks. */
+ inline void set_type(enum_mdl_type type_arg) {
+ DBUG_ASSERT(ticket == NULL);
+ type = type_arg;
+ }
+
+ /**
+ Is this a request for a lock which allow data to be updated?
+
+ @note This method returns true for MDL_SHARED_UPGRADABLE type of
+ lock. Even though this type of lock doesn't allow updates
+ it will always be upgraded to one that does.
+ */
+ bool is_write_lock_request() const {
+ return (type >= MDL_SHARED_WRITE && type != MDL_SHARED_READ_ONLY);
+ }
+
+ /** Is this a request for a strong, DDL/LOCK TABLES-type, of lock? */
+ bool is_ddl_or_lock_tables_lock_request() const {
+ return type >= MDL_SHARED_UPGRADABLE;
+ }
+
+ /**
+ This constructor exists for two reasons:
+
+ - TABLE_LIST objects are sometimes default-constructed. We plan to remove
+ this as there is no practical reason, the call to the default
+ constructor is always followed by either a call to
+ TABLE_LIST::init_one_table() or memberwise assignments.
+
+ - In some legacy cases TABLE_LIST objects are copy-assigned without
+ intention to copy the TABLE_LIST::mdl_request member. In this cases they
+ are overwritten with an uninitialized MDL_request object. The cases are:
+
+ - Sql_cmd_handler_open::execute()
+ - mysql_execute_command()
+ - SELECT_LEX_UNIT::prepare()
+ - fill_defined_view_parts()
+
+ No new cases are expected. In all other cases, so far only
+ Locked_tables_list::rename_locked_table(), a move assignment is actually
+ what is intended.
+ */
+ MDL_request() {}
+
+ MDL_request(const MDL_request &rhs)
+ : type(rhs.type), duration(rhs.duration), ticket(NULL), key(rhs.key) {}
+
+ MDL_request(MDL_request &&) = default;
+
+ MDL_request &operator=(MDL_request &&) = default;
+};
+
+#define MDL_REQUEST_INIT(R, P1, P2, P3, P4, P5) \
+ (*R).init_with_source(P1, P2, P3, P4, P5, __FILE__, __LINE__)
+
+#define MDL_REQUEST_INIT_BY_KEY(R, P1, P2, P3) \
+ (*R).init_by_key_with_source(P1, P2, P3, __FILE__, __LINE__)
+
+#define MDL_REQUEST_INIT_BY_PART_KEY(R, P1, P2, P3, P4, P5, P6) \
+ (*R).init_by_part_key_with_source(P1, P2, P3, P4, P5, P6, __FILE__, __LINE__)
+
+/**
+ An abstract class for inspection of a connected
+ subgraph of the wait-for graph.
+*/
+
+class MDL_wait_for_graph_visitor {
+ public:
+ virtual bool enter_node(MDL_context *node) = 0;
+ virtual void leave_node(MDL_context *node) = 0;
+
+ virtual bool inspect_edge(MDL_context *dest) = 0;
+ virtual ~MDL_wait_for_graph_visitor();
+ MDL_wait_for_graph_visitor() : m_lock_open_count(0) {}
+
+ public:
+ /**
+ XXX, hack: During deadlock search, we may need to
+ inspect TABLE_SHAREs and acquire LOCK_open. Since
+ LOCK_open is not a recursive mutex, count here how many
+ times we "took" it (but only take and release once).
+ Not using a native recursive mutex or rwlock in 5.5 for
+ LOCK_open since it has significant performance impacts.
+ */
+ uint m_lock_open_count;
+};
+
+/**
+ Abstract class representing an edge in the waiters graph
+ to be traversed by deadlock detection algorithm.
+*/
+
+class MDL_wait_for_subgraph {
+ public:
+ virtual ~MDL_wait_for_subgraph();
+
+ /**
+ Accept a wait-for graph visitor to inspect the node
+ this edge is leading to.
+ */
+ virtual bool accept_visitor(MDL_wait_for_graph_visitor *gvisitor) = 0;
+
+ static const uint DEADLOCK_WEIGHT_DML = 0;
+ static const uint DEADLOCK_WEIGHT_ULL = 50;
+ static const uint DEADLOCK_WEIGHT_DDL = 100;
+
+ /* A helper used to determine which lock request should be aborted. */
+ virtual uint get_deadlock_weight() const = 0;
+};
+
+/**
+ A granted metadata lock.
+
+ @warning MDL_ticket members are private to the MDL subsystem.
+
+ @note Multiple shared locks on a same object are represented by a
+ single ticket. The same does not apply for other lock types.
+
+ @note There are two groups of MDL_ticket members:
+ - "Externally accessible". These members can be accessed from
+ threads/contexts different than ticket owner in cases when
+ ticket participates in some list of granted or waiting tickets
+ for a lock. Therefore one should change these members before
+ including then to waiting/granted lists or while holding lock
+ protecting those lists.
+ - "Context private". Such members are private to thread/context
+ owning this ticket. I.e. they should not be accessed from other
+ threads/contexts.
+*/
+
+class MDL_ticket : public MDL_wait_for_subgraph {
+ public:
+ /**
+ Pointers for participating in the list of lock requests for this context.
+ Context private.
+ */
+ MDL_ticket *next_in_context;
+ MDL_ticket **prev_in_context;
+
+ /**
+ Pointers for participating in the list of satisfied/pending requests
+ for the lock. Externally accessible.
+ */
+ MDL_ticket *next_in_lock;
+ MDL_ticket **prev_in_lock;
+
+ public:
+ bool has_pending_conflicting_lock() const;
+
+ MDL_context *get_ctx() const { return m_ctx; }
+ bool is_upgradable_or_exclusive() const {
+ return m_type == MDL_SHARED_UPGRADABLE || m_type == MDL_SHARED_NO_WRITE ||
+ m_type == MDL_SHARED_NO_READ_WRITE || m_type == MDL_EXCLUSIVE;
+ }
+ enum_mdl_type get_type() const { return m_type; }
+ MDL_lock *get_lock() const { return m_lock; }
+ const MDL_key *get_key() const;
+ void downgrade_lock(enum_mdl_type type);
+
+ bool has_stronger_or_equal_type(enum_mdl_type type) const;
+
+ bool is_incompatible_when_granted(enum_mdl_type type) const;
+ bool is_incompatible_when_waiting(enum_mdl_type type) const;
+
+ /** Implement MDL_wait_for_subgraph interface. */
+ virtual bool accept_visitor(MDL_wait_for_graph_visitor *dvisitor);
+ virtual uint get_deadlock_weight() const;
+
+#ifndef DBUG_OFF
+ enum_mdl_duration get_duration() const { return m_duration; }
+ void set_duration(enum_mdl_duration dur) { m_duration = dur; }
+#endif
+
+ public:
+ /**
+ Status of lock request represented by the ticket as reflected in P_S.
+ */
+ enum enum_psi_status {
+ PENDING = 0,
+ GRANTED,
+ PRE_ACQUIRE_NOTIFY,
+ POST_RELEASE_NOTIFY
+ };
+
+ private:
+ friend class MDL_context;
+
+ MDL_ticket(MDL_context *ctx_arg, enum_mdl_type type_arg
+#ifndef DBUG_OFF
+ ,
+ enum_mdl_duration duration_arg
+#endif
+ )
+ : m_type(type_arg),
+#ifndef DBUG_OFF
+ m_duration(duration_arg),
+#endif
+ m_ctx(ctx_arg),
+ m_lock(NULL),
+ m_is_fast_path(false),
+ m_hton_notified(false),
+ m_psi(NULL) {
+ }
+
+ virtual ~MDL_ticket() { DBUG_ASSERT(m_psi == NULL); }
+
+ static MDL_ticket *create(MDL_context *ctx_arg, enum_mdl_type type_arg
+#ifndef DBUG_OFF
+ ,
+ enum_mdl_duration duration_arg
+#endif
+ );
+ static void destroy(MDL_ticket *ticket);
+
+ private:
+ /** Type of metadata lock. Externally accessible. */
+ enum enum_mdl_type m_type;
+#ifndef DBUG_OFF
+ /**
+ Duration of lock represented by this ticket.
+ Context private. Debug-only.
+ */
+ enum_mdl_duration m_duration;
+#endif
+ /**
+ Context of the owner of the metadata lock ticket. Externally accessible.
+ */
+ MDL_context *m_ctx;
+
+ /**
+ Pointer to the lock object for this lock ticket. Externally accessible.
+ */
+ MDL_lock *m_lock;
+
+ /**
+ Indicates that ticket corresponds to lock acquired using "fast path"
+ algorithm. Particularly this means that it was not included into
+ MDL_lock::m_granted bitmap/list and instead is accounted for by
+ MDL_lock::m_fast_path_locks_granted_counter
+ */
+ bool m_is_fast_path;
+
+ /**
+ Indicates that ticket corresponds to lock request which required
+ storage engine notification during its acquisition and requires
+ storage engine notification after its release.
+ */
+ bool m_hton_notified;
+
+ PSI_metadata_lock *m_psi;
+
+ private:
+ MDL_ticket(const MDL_ticket &); /* not implemented */
+ MDL_ticket &operator=(const MDL_ticket &); /* not implemented */
+};
+
+/**
+ Keep track of MDL_ticket for different durations. Maintains a
+ hash-based secondary index into the linked lists, to speed up access
+ by MDL_key.
+ */
+class MDL_ticket_store {
+ public:
+ /**
+ Utility struct for representing a ticket pointer and its duration.
+ */
+ struct MDL_ticket_handle {
+ enum_mdl_duration m_dur = MDL_DURATION_END;
+ MDL_ticket *m_ticket = nullptr;
+
+ MDL_ticket_handle() = default;
+ MDL_ticket_handle(MDL_ticket *t, enum_mdl_duration d)
+ : m_dur{d}, m_ticket{t} {}
+ };
+
+ private:
+ using Ticket_p_list =
+ I_P_List<MDL_ticket,
+ I_P_List_adapter<MDL_ticket, &MDL_ticket::next_in_context,
+ &MDL_ticket::prev_in_context>>;
+
+ struct Duration {
+ Ticket_p_list m_ticket_list;
+ /**
+ m_mat_front tracks what was the front of m_ticket_list, the last
+ time MDL_context::materialize_fast_path_locks() was called. This
+ just an optimization which allows
+ MDL_context::materialize_fast_path_locks() only to consider the
+ locks added since the last time it ran. Consequently, it can be
+ assumed that every ticket after m_mat_front is materialized, but
+ the converse is not necessarily true as new, already
+ materialized, locks may have been added since the last time
+ materialize_fast_path_locks() ran.
+ */
+ MDL_ticket *m_mat_front = nullptr;
+ };
+
+ Duration m_durations[MDL_DURATION_END];
+
+ struct Hash {
+ size_t operator()(const MDL_key *k) const;
+ };
+
+ struct Key_equal {
+ bool operator()(const MDL_key *a, const MDL_key *b) const {
+ return a->is_equal(b);
+ }
+ };
+
+ using Ticket_map = std::unordered_multimap<const MDL_key *, MDL_ticket_handle,
+ Hash, Key_equal>;
+
+ /**
+ If the number of tickets in the ticket store (in all durations) is equal
+ to, or exceeds this constant the hash index (in the form of an
+ unordered_multi_map) will be maintained and used for lookups.
+
+ The value 256 is chosen as it has worked well in benchmarks.
+ */
+ const size_t THRESHOLD = 256;
+
+ /**
+ Initial number of buckets in the hash index. THRESHOLD is chosen
+ to get a fill-factor of 50% when reaching the threshold value.
+ */
+ const size_t INITIAL_BUCKET_COUNT = THRESHOLD * 2;
+ size_t m_count = 0;
+
+ std::unique_ptr<Ticket_map> m_map;
+
+ MDL_ticket_handle find_in_lists(const MDL_request &req) const;
+ MDL_ticket_handle find_in_hash(const MDL_request &req) const;
+
+ public:
+ /**
+ Public alias.
+ */
+ using List_iterator = Ticket_p_list::Iterator;
+
+ /**
+ Constructs store. The hash index is initially empty. Filled on demand.
+ */
+ MDL_ticket_store()
+ : // Comment in to test threshold values in unit test micro benchmark
+ // THRESHOLD{read_from_env("TS_THRESHOLD", 500)},
+ m_map{nullptr} {}
+
+ /**
+ Calls the closure provided as argument for each of the MDL_tickets
+ in the given duration.
+ @param dur duration list to iterate over
+ @param clos closure to invoke for each ticket in the list
+ */
+ template <typename CLOS>
+ void for_each_ticket_in_duration_list(enum_mdl_duration dur, CLOS &&clos) {
+ List_iterator it(m_durations[dur].m_ticket_list);
+ for (MDL_ticket *t = it++; t != nullptr; t = it++) {
+ clos(t, dur);
+ }
+ }
+
+ /**
+ Calls the closure provided as argument for each of the MDL_tickets
+ in the store.
+ @param clos closure to invoke for each ticket in the store
+ */
+ template <typename CLOS>
+ void for_each_ticket_in_ticket_lists(CLOS &&clos) {
+ for_each_ticket_in_duration_list(MDL_STATEMENT, std::forward<CLOS>(clos));
+ for_each_ticket_in_duration_list(MDL_TRANSACTION, std::forward<CLOS>(clos));
+ for_each_ticket_in_duration_list(MDL_EXPLICIT, std::forward<CLOS>(clos));
+ }
+
+ /**
+ Predicate for the emptiness of the store.
+ @return true if there are no tickets in the store
+ */
+ bool is_empty() const;
+
+ /**
+ Predicate for the emptiness of a given duration list.
+ @param di the duration to check
+ @return true if there are no tickets with the given duration
+ */
+ bool is_empty(int di) const;
+
+ /**
+ Return the first MDL_ticket for the given duration.
+
+ @param di duration to get first ticket for
+
+ @return first ticket in the given duration or nullptr if no such
+ tickets exist
+ */
+ MDL_ticket *front(int di);
+
+ /**
+ Push a ticket onto the list for a given duration.
+ @param dur duration list to push into
+ @param ticket to push
+ */
+ void push_front(enum_mdl_duration dur, MDL_ticket *ticket);
+
+ /**
+ Remove a ticket from a duration list. Note that since the
+ underlying list is an intrusive linked list there is no guarantee
+ that the ticket is actually in the duration list. It will be
+ removed from which ever list it is in.
+ */
+ void remove(enum_mdl_duration dur, MDL_ticket *ticket);
+
+ /**
+ Return a P-list iterator to the given duration.
+ @param di duration list index
+ @return P-list iterator to tickets with given duration
+ */
+ List_iterator list_iterator(int di) const {
+ return List_iterator{m_durations[di].m_ticket_list};
+ }
+
+ /**
+ Move all tickets to the explicit duration list.
+ */
+ void move_all_to_explicit_duration();
+
+ /**
+ Move all tickets to the transaction duration list.
+ */
+ void move_explicit_to_transaction_duration();
+
+ /**
+ Look up a ticket based on its MDL_key.
+ @param req request to locate ticket for
+ @return MDL_ticket_handle with ticket pointer and found duration
+ (or nullptr and MDL_DURATION_END if not found
+ */
+ MDL_ticket_handle find(const MDL_request &req) const;
+
+ /**
+ Mark boundary for tickets with fast_path=false, so that later
+ calls to materialize_fast_path_locks() do not have to traverse the
+ whole set of tickets.
+ */
+ void set_materialized();
+
+ /**
+ Return the first ticket for which materialize_fast_path_locks
+ already has been called for the given duration.
+
+ @param di duration list index
+ @return first materialized ticket for the given duration
+ */
+ MDL_ticket *materialized_front(int di);
+};
+
+/**
+ Savepoint for MDL context.
+
+ Doesn't include metadata locks with explicit duration as
+ they are not released during rollback to savepoint.
+*/
+
+class MDL_savepoint {
+ public:
+ MDL_savepoint() {}
+
+ private:
+ MDL_savepoint(MDL_ticket *stmt_ticket, MDL_ticket *trans_ticket)
+ : m_stmt_ticket(stmt_ticket), m_trans_ticket(trans_ticket) {}
+
+ friend class MDL_context;
+
+ private:
+ /**
+ Pointer to last lock with statement duration which was taken
+ before creation of savepoint.
+ */
+ MDL_ticket *m_stmt_ticket;
+ /**
+ Pointer to last lock with transaction duration which was taken
+ before creation of savepoint.
+ */
+ MDL_ticket *m_trans_ticket;
+};
+
+/**
+ A reliable way to wait on an MDL lock.
+*/
+
+class MDL_wait {
+ public:
+ MDL_wait();
+ ~MDL_wait();
+
+ // WS_EMPTY since EMPTY conflicts with #define in system headers on some
+ // platforms.
+ enum enum_wait_status { WS_EMPTY = 0, GRANTED, VICTIM, TIMEOUT, KILLED };
+
+ bool set_status(enum_wait_status result_arg);
+ enum_wait_status get_status();
+ void reset_status();
+ enum_wait_status timed_wait(MDL_context_owner *owner,
+ struct timespec *abs_timeout, bool signal_timeout,
+ const PSI_stage_info *wait_state_name);
+
+ private:
+ /**
+ Condvar which is used for waiting until this context's pending
+ request can be satisfied or this thread has to perform actions
+ to resolve a potential deadlock (we subscribe to such
+ notification by adding a ticket corresponding to the request
+ to an appropriate queue of waiters).
+ */
+ mysql_mutex_t m_LOCK_wait_status;
+ mysql_cond_t m_COND_wait_status;
+ enum_wait_status m_wait_status;
+};
+
+/**
+ Base class to find out if the lock represented by a given ticket
+ should be released. Users of release_locks() need to subclass
+ this and specify an implementation of release(). Only for locks
+ with explicit duration.
+*/
+
+class MDL_release_locks_visitor {
+ public:
+ virtual ~MDL_release_locks_visitor() {}
+ /**
+ Check if the given ticket represents a lock that should be released.
+
+ @retval true if the lock should be released, false otherwise.
+ */
+ virtual bool release(MDL_ticket *ticket) = 0;
+};
+
+/**
+ Abstract visitor class for inspecting MDL_context.
+*/
+
+class MDL_context_visitor {
+ public:
+ virtual ~MDL_context_visitor() {}
+ virtual void visit_context(const MDL_context *ctx) = 0;
+};
+
+typedef I_P_List<MDL_request,
+ I_P_List_adapter<MDL_request, &MDL_request::next_in_list,
+ &MDL_request::prev_in_list>,
+ I_P_List_counter>
+ MDL_request_list;
+
+/**
+ Context of the owner of metadata locks. I.e. each server
+ connection has such a context.
+*/
+
+class MDL_context {
+ public:
+ typedef I_P_List<MDL_ticket,
+ I_P_List_adapter<MDL_ticket, &MDL_ticket::next_in_context,
+ &MDL_ticket::prev_in_context>>
+ Ticket_list;
+
+ typedef Ticket_list::Iterator Ticket_iterator;
+
+ MDL_context();
+ void destroy();
+
+ bool try_acquire_lock(MDL_request *mdl_request);
+ bool acquire_lock(MDL_request *mdl_request, Timeout_type lock_wait_timeout);
+ bool acquire_locks(MDL_request_list *requests,
+ Timeout_type lock_wait_timeout);
+ bool upgrade_shared_lock(MDL_ticket *mdl_ticket, enum_mdl_type new_type,
+ Timeout_type lock_wait_timeout);
+
+ bool clone_ticket(MDL_request *mdl_request);
+
+ /**
+ Create copy of all granted tickets of particular duration from given
+ context to current context.
+ Used by XA for preserving locks during client disconnect.
+
+ @param ticket_owner Owner of tickets to be cloned
+ @param duration MDL lock duration for that tickets are to be cloned
+
+ @retval true Out of memory or deadlock happened or
+ lock request was refused by storage engine.
+ @retval false Success.
+ */
+
+ bool clone_tickets(const MDL_context *ticket_owner,
+ enum_mdl_duration duration);
+
+ void release_all_locks_for_name(MDL_ticket *ticket);
+ void release_locks(MDL_release_locks_visitor *visitor);
+ void release_lock(MDL_ticket *ticket);
+
+ bool owns_equal_or_stronger_lock(const MDL_key *mdl_key,
+ enum_mdl_type mdl_type);
+
+ bool owns_equal_or_stronger_lock(MDL_key::enum_mdl_namespace mdl_namespace,
+ const char *db, const char *name,
+ enum_mdl_type mdl_type);
+
+ bool find_lock_owner(const MDL_key *mdl_key, MDL_context_visitor *visitor);
+
+ bool has_lock(const MDL_savepoint &mdl_savepoint, MDL_ticket *mdl_ticket);
+
+ inline bool has_locks() const { return !m_ticket_store.is_empty(); }
+
+ bool has_locks(MDL_key::enum_mdl_namespace mdl_namespace) const;
+
+ bool has_locks_waited_for() const;
+
+#ifndef DBUG_OFF
+ bool has_locks(enum_mdl_duration duration) {
+ return !m_ticket_store.is_empty(duration);
+ }
+#endif
+
+ MDL_savepoint mdl_savepoint() {
+ return MDL_savepoint(m_ticket_store.front(MDL_STATEMENT),
+ m_ticket_store.front(MDL_TRANSACTION));
+ }
+
+ void set_explicit_duration_for_all_locks();
+ void set_transaction_duration_for_all_locks();
+ void set_lock_duration(MDL_ticket *mdl_ticket, enum_mdl_duration duration);
+
+ void release_statement_locks();
+ void release_transactional_locks();
+ void rollback_to_savepoint(const MDL_savepoint &mdl_savepoint);
+
+ MDL_context_owner *get_owner() const { return m_owner; }
+
+ /** @pre Only valid if we started waiting for lock. */
+ inline uint get_deadlock_weight() const {
+ return m_force_dml_deadlock_weight
+ ? MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DML
+ : m_waiting_for->get_deadlock_weight();
+ }
+
+ void init(MDL_context_owner *arg) { m_owner = arg; }
+
+ void set_needs_thr_lock_abort(bool needs_thr_lock_abort) {
+ /*
+ @note In theory, this member should be modified under protection
+ of some lock since it can be accessed from different threads.
+ In practice, this is not necessary as code which reads this
+ value and so might miss the fact that value was changed will
+ always re-try reading it after small timeout and therefore
+ will see the new value eventually.
+ */
+ m_needs_thr_lock_abort = needs_thr_lock_abort;
+
+ if (m_needs_thr_lock_abort) {
+ /*
+ For MDL_object_lock::notify_conflicting_locks() to work properly
+ all context requiring thr_lock aborts should not have any "fast
+ path" locks.
+ */
+ materialize_fast_path_locks();
+ }
+ }
+ bool get_needs_thr_lock_abort() const { return m_needs_thr_lock_abort; }
+
+ void set_force_dml_deadlock_weight(bool force_dml_deadlock_weight) {
+ m_force_dml_deadlock_weight = force_dml_deadlock_weight;
+ }
+
+ /**
+ Get pseudo random value in [0 .. 2^31-1] range.
+
+ @note We use Linear Congruential Generator with venerable constant
+ parameters for this.
+ It is known to have problems with its lower bits are not being
+ very random so probably is not good enough for generic use.
+ However, we only use it to do random dives into MDL_lock objects
+ hash when searching for unused objects to be freed, and for this
+ purposes it is sufficient.
+ We rely on values of "get_random() % 2^k" expression having "2^k"
+ as a period to ensure that random dives eventually cover all hash
+ (the former can be proven to be true). This also means that there
+ is no bias towards any specific objects to be expelled (as hash
+ values don't repeat), which is nice for performance.
+ */
+ uint get_random() {
+ if (m_rand_state > INT_MAX32) {
+ /*
+ Perform lazy initialization of LCG. We can't initialize it at the
+ point when MDL_context is created since THD represented through
+ MDL_context_owner interface is not fully initialized at this point
+ itself.
+ */
+ m_rand_state = m_owner->get_rand_seed() & INT_MAX32;
+ }
+ m_rand_state = (m_rand_state * 1103515245 + 12345) & INT_MAX32;
+ return m_rand_state;
+ }
+
+ /**
+ Within MDL subsystem this one is only used for DEBUG_SYNC.
+ Do not use it to peek/poke into other parts of THD from MDL.
+ @sa MDL_context_owner::get_thd().
+ */
+ THD *get_thd() const { return m_owner->get_thd(); }
+
+ public:
+ /**
+ If our request for a lock is scheduled, or aborted by the deadlock
+ detector, the result is recorded in this class.
+ */
+ MDL_wait m_wait;
+
+ private:
+ /**
+ Lists of all MDL tickets acquired by this connection.
+
+ Lists of MDL tickets:
+ ---------------------
+ The entire set of locks acquired by a connection can be separated
+ in three subsets according to their duration: locks released at
+ the end of statement, at the end of transaction and locks are
+ released explicitly.
+
+ Statement and transactional locks are locks with automatic scope.
+ They are accumulated in the course of a transaction, and released
+ either at the end of uppermost statement (for statement locks) or
+ on COMMIT, ROLLBACK or ROLLBACK TO SAVEPOINT (for transactional
+ locks). They must not be (and never are) released manually,
+ i.e. with release_lock() call.
+
+ Tickets with explicit duration are taken for locks that span
+ multiple transactions or savepoints.
+ These are: HANDLER SQL locks (HANDLER SQL is
+ transaction-agnostic), LOCK TABLES locks (you can COMMIT/etc
+ under LOCK TABLES, and the locked tables stay locked), user level
+ locks (GET_LOCK()/RELEASE_LOCK() functions) and
+ locks implementing "global read lock".
+
+ Statement/transactional locks are always prepended to the
+ beginning of the appropriate list. In other words, they are
+ stored in reverse temporal order. Thus, when we rollback to
+ a savepoint, we start popping and releasing tickets from the
+ front until we reach the last ticket acquired after the savepoint.
+
+ Locks with explicit duration are not stored in any
+ particular order, and among each other can be split into
+ four sets:
+ - LOCK TABLES locks
+ - User-level locks
+ - HANDLER locks
+ - GLOBAL READ LOCK locks
+ */
+ MDL_ticket_store m_ticket_store;
+
+ MDL_context_owner *m_owner;
+ /**
+ true - if for this context we will break protocol and try to
+ acquire table-level locks while having only S lock on
+ some table.
+ To avoid deadlocks which might occur during concurrent
+ upgrade of SNRW lock on such object to X lock we have to
+ abort waits for table-level locks for such connections.
+ false - Otherwise.
+ */
+ bool m_needs_thr_lock_abort;
+
+ /**
+ Indicates that we need to use DEADLOCK_WEIGHT_DML deadlock
+ weight for this context and ignore the deadlock weight provided
+ by the MDL_wait_for_subgraph object which we are waiting for.
+
+ @note Can be changed only when there is a guarantee that this
+ MDL_context is not waiting for a metadata lock or table
+ definition entry.
+ */
+ bool m_force_dml_deadlock_weight;
+
+ /**
+ Read-write lock protecting m_waiting_for member.
+
+ @note The fact that this read-write lock prefers readers is
+ important as deadlock detector won't work correctly
+ otherwise. @sa Comment for MDL_lock::m_rwlock.
+ */
+ mysql_prlock_t m_LOCK_waiting_for;
+ /**
+ Tell the deadlock detector what metadata lock or table
+ definition cache entry this session is waiting for.
+ In principle, this is redundant, as information can be found
+ by inspecting waiting queues, but we'd very much like it to be
+ readily available to the wait-for graph iterator.
+ */
+ MDL_wait_for_subgraph *m_waiting_for;
+ /**
+ Thread's pins (a.k.a. hazard pointers) to be used by lock-free
+ implementation of MDL_map::m_locks container. NULL if pins are
+ not yet allocated from container's pinbox.
+ */
+ LF_PINS *m_pins;
+ /**
+ State for pseudo random numbers generator (PRNG) which output
+ is used to perform random dives into MDL_lock objects hash
+ when searching for unused objects to free.
+ */
+ uint m_rand_state;
+
+ private:
+ MDL_ticket *find_ticket(MDL_request *mdl_req, enum_mdl_duration *duration);
+ void release_locks_stored_before(enum_mdl_duration duration,
+ MDL_ticket *sentinel);
+ void release_lock(enum_mdl_duration duration, MDL_ticket *ticket);
+ bool try_acquire_lock_impl(MDL_request *mdl_request, MDL_ticket **out_ticket);
+ void materialize_fast_path_locks();
+ inline bool fix_pins();
+
+ public:
+ void find_deadlock();
+
+ bool visit_subgraph(MDL_wait_for_graph_visitor *dvisitor);
+
+ /** Inform the deadlock detector there is an edge in the wait-for graph. */
+ void will_wait_for(MDL_wait_for_subgraph *waiting_for_arg) {
+ /*
+ Before starting wait for any resource we need to materialize
+ all "fast path" tickets belonging to this thread. Otherwise
+ locks acquired which are represented by these tickets won't
+ be present in wait-for graph and could cause missed deadlocks.
+
+ It is OK for context which doesn't wait for any resource to
+ have "fast path" tickets, as such context can't participate
+ in any deadlock.
+ */
+ materialize_fast_path_locks();
+
+ mysql_prlock_wrlock(&m_LOCK_waiting_for);
+ m_waiting_for = waiting_for_arg;
+ mysql_prlock_unlock(&m_LOCK_waiting_for);
+ }
+
+ /** Remove the wait-for edge from the graph after we're done waiting. */
+ void done_waiting_for() {
+ mysql_prlock_wrlock(&m_LOCK_waiting_for);
+ m_waiting_for = NULL;
+ mysql_prlock_unlock(&m_LOCK_waiting_for);
+ }
+ void lock_deadlock_victim() { mysql_prlock_rdlock(&m_LOCK_waiting_for); }
+ void unlock_deadlock_victim() { mysql_prlock_unlock(&m_LOCK_waiting_for); }
+
+ private:
+ MDL_context(const MDL_context &rhs); /* not implemented */
+ MDL_context &operator=(MDL_context &rhs); /* not implemented */
+};
+
+void mdl_init();
+void mdl_destroy();
+
+#ifndef DBUG_OFF
+extern mysql_mutex_t LOCK_open;
+#endif
+
+/*
+ Metadata locking subsystem tries not to grant more than
+ max_write_lock_count high priority, strong locks successively,
+ to avoid starving out weak, lower priority locks.
+*/
+extern ulong max_write_lock_count;
+
+extern int32 mdl_locks_unused_locks_low_water;
+
+/**
+ Default value for threshold for number of unused MDL_lock objects after
+ exceeding which we start considering freeing them. Only unit tests use
+ different threshold value.
+*/
+const int32 MDL_LOCKS_UNUSED_LOCKS_LOW_WATER_DEFAULT = 1000;
+
+/**
+ Ratio of unused/total MDL_lock objects after exceeding which we
+ start trying to free unused MDL_lock objects (assuming that
+ mdl_locks_unused_locks_low_water threshold is passed as well).
+ Note that this value should be high enough for our algorithm
+ using random dives into hash to work well.
+*/
+const double MDL_LOCKS_UNUSED_LOCKS_MIN_RATIO = 0.25;
+
+int32 mdl_get_unused_locks_count();
+
+/**
+ Inspect if MDL_context is owned by any thread.
+*/
+class MDL_lock_is_owned_visitor : public MDL_context_visitor {
+ public:
+ MDL_lock_is_owned_visitor() : m_exists(false) {}
+
+ /**
+ Collects relevant information about the MDL lock owner.
+
+ This function is only called by MDL_context::find_lock_owner() when
+ searching for MDL lock owners to collect extra information about the
+ owner. As we only need to know that the MDL lock is owned, setting
+ m_exists to true is enough.
+ */
+
+ void visit_context(const MDL_context *ctx MY_ATTRIBUTE((unused))) override {
+ m_exists = true;
+ }
+
+ /**
+ Returns if an owner for the MDL lock being inspected exists.
+
+ @return true when MDL lock is owned, false otherwise.
+ */
+
+ bool exists() const { return m_exists; }
+
+ private:
+ /* holds information about MDL being owned by any thread */
+ bool m_exists;
+};
+
+#endif
diff --git a/contrib/libs/libmysql_r/sql/mem_root_array.h b/contrib/libs/libmysql_r/sql/mem_root_array.h
new file mode 100644
index 0000000000..ae1919e660
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/mem_root_array.h
@@ -0,0 +1,447 @@
+/* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef MEM_ROOT_ARRAY_INCLUDED
+#define MEM_ROOT_ARRAY_INCLUDED
+
+#include <algorithm>
+#include <type_traits>
+
+#include "my_alloc.h"
+#include "my_dbug.h"
+
+/**
+ A typesafe replacement for DYNAMIC_ARRAY.
+ We use MEM_ROOT for allocating storage, rather than the C++ heap.
+ The interface is chosen to be similar to std::vector.
+
+ @remark
+ Mem_root_array_YY is constructor-less for use in the parser stack of unions.
+ For other needs please use Mem_root_array.
+
+ @remark
+ Unlike DYNAMIC_ARRAY, elements are properly copied
+ (rather than memcpy()d) if the underlying array needs to be expanded.
+
+ @remark
+ Unless Element_type's destructor is trivial, we destroy objects when they are
+ removed from the array (including when the array object itself is destroyed).
+
+ @remark
+ Note that MEM_ROOT has no facility for reusing free space,
+ so don't use this if multiple re-expansions are likely to happen.
+
+ @tparam Element_type The type of the elements of the container.
+ Elements must be copyable.
+*/
+template <typename Element_type>
+class Mem_root_array_YY {
+ /**
+ Is Element_type trivially destructible? If it is, we don't destroy
+ elements when they are removed from the array or when the array is
+ destroyed.
+ */
+ static constexpr bool has_trivial_destructor =
+ std::is_trivially_destructible<Element_type>::value;
+
+ public:
+ /// Convenience typedef, same typedef name as std::vector
+ typedef Element_type value_type;
+
+ void init(MEM_ROOT *root) {
+ DBUG_ASSERT(root != NULL);
+
+ m_root = root;
+ m_array = NULL;
+ m_size = 0;
+ m_capacity = 0;
+ }
+
+ /// Initialize empty array that we aren't going to grow
+ void init_empty_const() {
+ m_root = NULL;
+ m_array = NULL;
+ m_size = 0;
+ m_capacity = 0;
+ }
+
+ /**
+ Switches mem-root, in case original mem-root was copied.
+ NOTE: m_root should really be const, i.e. never change after initialization.
+ */
+ void set_mem_root(MEM_ROOT *new_root) {
+ m_root = new_root;
+ DBUG_ASSERT(m_root != NULL);
+ }
+
+ Element_type &at(size_t n) {
+ DBUG_ASSERT(n < size());
+ return m_array[n];
+ }
+
+ const Element_type &at(size_t n) const {
+ DBUG_ASSERT(n < size());
+ return m_array[n];
+ }
+
+ Element_type &operator[](size_t n) { return at(n); }
+ const Element_type &operator[](size_t n) const { return at(n); }
+
+ Element_type &back() { return at(size() - 1); }
+ const Element_type &back() const { return at(size() - 1); }
+
+ /// Random access iterators to value_type and const value_type.
+ typedef Element_type *iterator;
+ typedef const Element_type *const_iterator;
+
+ /// Returns a pointer to the first element in the array.
+ Element_type *begin() { return &m_array[0]; }
+ const Element_type *begin() const { return &m_array[0]; }
+
+ /// Returns a pointer to the past-the-end element in the array.
+ Element_type *end() { return &m_array[size()]; }
+ const Element_type *end() const { return &m_array[size()]; }
+
+ /// Returns a constant pointer to the first element in the array.
+ const_iterator cbegin() const { return begin(); }
+
+ /// Returns a constant pointer to the past-the-end element in the array.
+ const_iterator cend() const { return end(); }
+
+ /// Erases all of the elements.
+ void clear() {
+ if (!empty()) chop(0);
+ }
+
+ /**
+ Chops the tail off the array, erasing all tail elements.
+ @param pos Index of first element to erase.
+ */
+ void chop(const size_t pos) {
+ DBUG_ASSERT(pos < m_size);
+ if (!has_trivial_destructor) {
+ for (size_t ix = pos; ix < m_size; ++ix) {
+ Element_type *p = &m_array[ix];
+ p->~Element_type(); // Destroy discarded element.
+ }
+ }
+ m_size = pos;
+ }
+
+ /**
+ Reserves space for array elements.
+ Copies over existing elements, in case we are re-expanding the array.
+
+ @param n number of elements.
+ @retval true if out-of-memory, false otherwise.
+ */
+ bool reserve(size_t n) {
+ if (n <= m_capacity) return false;
+
+ void *mem = m_root->Alloc(n * element_size());
+ if (!mem) return true;
+ Element_type *array = static_cast<Element_type *>(mem);
+
+ // Copy all the existing elements into the new array.
+ for (size_t ix = 0; ix < m_size; ++ix) {
+ Element_type *new_p = &array[ix];
+ Element_type *old_p = &m_array[ix];
+ ::new (new_p)
+ Element_type(std::move(*old_p)); // Copy or move into new location.
+ if (!has_trivial_destructor)
+ old_p->~Element_type(); // Destroy the old element.
+ }
+
+ // Forget the old array.
+ m_array = array;
+ m_capacity = n;
+ return false;
+ }
+
+ /**
+ Adds a new element at the end of the array, after its current last
+ element. The content of this new element is initialized to a copy of
+ the input argument.
+
+ @param element Object to copy.
+ @retval true if out-of-memory, false otherwise.
+ */
+ bool push_back(const Element_type &element) {
+ const size_t min_capacity = 20;
+ const size_t expansion_factor = 2;
+ if (0 == m_capacity && reserve(min_capacity)) return true;
+ if (m_size == m_capacity && reserve(m_capacity * expansion_factor))
+ return true;
+ Element_type *p = &m_array[m_size++];
+ ::new (p) Element_type(element);
+ return false;
+ }
+
+ /**
+ Adds a new element at the end of the array, after its current last
+ element. The content of this new element is initialized by moving
+ the input element.
+
+ @param element Object to move.
+ @retval true if out-of-memory, false otherwise.
+ */
+ bool push_back(Element_type &&element) {
+ const size_t min_capacity = 20;
+ const size_t expansion_factor = 2;
+ if (0 == m_capacity && reserve(min_capacity)) return true;
+ if (m_size == m_capacity && reserve(m_capacity * expansion_factor))
+ return true;
+ Element_type *p = &m_array[m_size++];
+ ::new (p) Element_type(std::move(element));
+ return false;
+ }
+
+ /**
+ Removes the last element in the array, effectively reducing the
+ container size by one. This destroys the removed element.
+ */
+ void pop_back() {
+ DBUG_ASSERT(!empty());
+ if (!has_trivial_destructor) back().~Element_type();
+ m_size -= 1;
+ }
+
+ /**
+ Resizes the container so that it contains n elements.
+
+ If n is smaller than the current container size, the content is
+ reduced to its first n elements, removing those beyond (and
+ destroying them).
+
+ If n is greater than the current container size, the content is
+ expanded by inserting at the end as many elements as needed to
+ reach a size of n. If val is specified, the new elements are
+ initialized as copies of val, otherwise, they are
+ value-initialized.
+
+ If n is also greater than the current container capacity, an automatic
+ reallocation of the allocated storage space takes place.
+
+ Notice that this function changes the actual content of the
+ container by inserting or erasing elements from it.
+ */
+ void resize(size_t n, const value_type &val) {
+ if (n == m_size) return;
+ if (n > m_size) {
+ if (!reserve(n)) {
+ while (n != m_size) push_back(val);
+ }
+ return;
+ }
+ if (!has_trivial_destructor) {
+ while (n != m_size) pop_back();
+ }
+ m_size = n;
+ }
+
+ /**
+ Same as resize(size_t, const value_type &val), but default-constructs
+ the new elements. This allows one to resize containers even if
+ value_type is not copy-constructible.
+ */
+ void resize(size_t n) {
+ if (n == m_size) return;
+ if (n > m_size) {
+ if (!reserve(n)) {
+ while (n != m_size) push_back(value_type());
+ }
+ return;
+ }
+ if (!has_trivial_destructor) {
+ while (n != m_size) pop_back();
+ }
+ m_size = n;
+ }
+
+ /**
+ Erase all the elements in the specified range.
+
+ @param first iterator that points to the first element to remove
+ @param last iterator that points to the element after the
+ last one to remove
+ @return an iterator to the first element after the removed range
+ */
+ iterator erase(const_iterator first, const_iterator last) {
+ iterator pos = begin() + (first - cbegin());
+ if (first != last) {
+ iterator new_end = std::move(last, cend(), pos);
+ chop(new_end - begin());
+ }
+ return pos;
+ }
+
+ /**
+ Removes a single element from the array.
+
+ @param position iterator that points to the element to remove
+
+ @return an iterator to the first element after the removed range
+ */
+ iterator erase(const_iterator position) {
+ return erase(position, std::next(position));
+ }
+
+ /**
+ Removes a single element from the array.
+
+ @param ix zero-based number of the element to remove
+
+ @return an iterator to the first element after the removed range
+ */
+ iterator erase(size_t ix) {
+ DBUG_ASSERT(ix < size());
+ return erase(std::next(this->cbegin(), ix));
+ }
+
+ /**
+ Insert an element at a given position.
+
+ @param pos the new element is inserted before the element
+ at this position
+ @param value the value of the new element
+ @return an iterator that points to the inserted element
+ */
+ iterator insert(const_iterator pos, const Element_type &value) {
+ ptrdiff_t idx = pos - cbegin();
+ if (!push_back(value)) std::rotate(begin() + idx, end() - 1, end());
+ return begin() + idx;
+ }
+
+ /**
+ Removes a single element from the array by value.
+ The removed element is destroyed. This effectively reduces the
+ container size by one.
+ Note that if there are multiple elements having the same
+ value, only the first element is removed.
+
+ This is generally an inefficient operation, since we need to copy
+ elements to fill the "hole" in the array.
+
+ We use std::copy to move objects, hence Element_type must be
+ assignable.
+
+ @retval number of elements removed, 0 or 1.
+ */
+ size_t erase_value(const value_type &val) {
+ iterator position = std::find(begin(), end(), val);
+ if (position != end()) {
+ erase(position);
+ return 1;
+ }
+ return 0; // Not found
+ }
+
+ /**
+ Removes a single element from the array.
+ The removed element is destroyed.
+ This effectively reduces the container size by one.
+
+ This is generally an inefficient operation, since we need to copy
+ elements to fill the "hole" in the array.
+
+ We use std::copy to move objects, hence Element_type must be assignable.
+ */
+ iterator erase(iterator position) {
+ DBUG_ASSERT(position != end());
+ if (position + 1 != end()) std::copy(position + 1, end(), position);
+ this->pop_back();
+ return position;
+ }
+
+ size_t capacity() const { return m_capacity; }
+ size_t element_size() const { return sizeof(Element_type); }
+ bool empty() const { return size() == 0; }
+ size_t size() const { return m_size; }
+
+ private:
+ MEM_ROOT *m_root;
+ Element_type *m_array;
+ size_t m_size;
+ size_t m_capacity;
+
+ // No CTOR/DTOR for this class!
+ // Mem_root_array_YY(const Mem_root_array_YY&);
+ // Mem_root_array_YY &operator=(const Mem_root_array_YY&);
+};
+
+/**
+ A typesafe replacement for DYNAMIC_ARRAY.
+
+ @see Mem_root_array_YY.
+*/
+template <typename Element_type>
+class Mem_root_array : public Mem_root_array_YY<Element_type> {
+ typedef Mem_root_array_YY<Element_type> super;
+
+ public:
+ /// Convenience typedef, same typedef name as std::vector
+ typedef Element_type value_type;
+
+ typedef typename super::const_iterator const_iterator;
+
+ explicit Mem_root_array(MEM_ROOT *root) { super::init(root); }
+
+ Mem_root_array(MEM_ROOT *root, size_t n) {
+ super::init(root);
+ super::resize(n);
+ }
+
+ Mem_root_array(MEM_ROOT *root, size_t n, const value_type &val) {
+ super::init(root);
+ super::resize(n, val);
+ }
+
+ /**
+ Range constructor.
+
+ Constructs a container with as many elements as the range [first,last),
+ with each element constructed from its corresponding element in that range,
+ in the same order.
+
+ @param root MEM_ROOT to use for memory allocation.
+ @param first iterator that points to the first element to copy
+ @param last iterator that points to the element after the
+ last one to copy
+ */
+ Mem_root_array(MEM_ROOT *root, const_iterator first, const_iterator last) {
+ super::init(root);
+ if (this->reserve(last - first)) return;
+ for (auto it = first; it != last; ++it) this->push_back(*it);
+ }
+
+ Mem_root_array(MEM_ROOT *root, const Mem_root_array &x)
+ : Mem_root_array(root, x.cbegin(), x.cend()) {}
+
+ ~Mem_root_array() { super::clear(); }
+
+ private:
+ // Not (yet) implemented.
+ Mem_root_array(const Mem_root_array &);
+ Mem_root_array &operator=(const Mem_root_array &);
+};
+
+#endif // MEM_ROOT_ARRAY_INCLUDED
diff --git a/contrib/libs/libmysql_r/sql/memroot_allocator.h b/contrib/libs/libmysql_r/sql/memroot_allocator.h
new file mode 100644
index 0000000000..b8dc43c0f4
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/memroot_allocator.h
@@ -0,0 +1,153 @@
+/* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef MEMROOT_ALLOCATOR_INCLUDED
+#define MEMROOT_ALLOCATOR_INCLUDED
+
+#include <limits>
+#include <new>
+#include <utility> // std::forward
+
+#include "my_alloc.h"
+#include "my_dbug.h"
+
+/**
+ Memroot_allocator is a C++ STL memory allocator based on MEM_ROOT.
+
+ No deallocation is done by this allocator. Calling init_sql_alloc()
+ and free_root() on the supplied MEM_ROOT is the responsibility of
+ the caller. Do *not* call free_root() until the destructor of any
+ objects using this allocator has completed. This includes iterators.
+
+ Example of use:
+ vector<int, Memroot_allocator<int> > v((Memroot_allocator<int>(&mem_root)));
+
+ @note allocate() throws std::bad_alloc() similarly to the default
+ STL memory allocator. This is necessary - STL functions which allocate
+ memory expect it. Otherwise these functions will try to use the memory,
+ leading to seg faults if memory allocation was not successful.
+
+ @note This allocator cannot be used for std::basic_string with RHEL 6/7
+ because of this bug:
+ https://bugzilla.redhat.com/show_bug.cgi?id=1546704
+ "Define _GLIBCXX_USE_CXX11_ABI gets ignored by gcc in devtoolset-7"
+
+ @note C++98 says that STL implementors can assume that allocator objects
+ of the same type always compare equal. This will only be the case for
+ two Memroot_allocators that use the same MEM_ROOT. Care should be taken
+ when this is not the case. Especially:
+ - Using list::splice() on two lists with allocators using two different
+ MEM_ROOTs causes undefined behavior. Most implementations seem to give
+ runtime errors in such cases.
+ - swap() on two collections with allocators using two different MEM_ROOTs
+ is not well defined. At least some implementations also swap allocators,
+ but this should not be depended on.
+*/
+
+template <class T>
+class Memroot_allocator {
+ // This cannot be const if we want to be able to swap.
+ MEM_ROOT *m_memroot;
+
+ public:
+ typedef T value_type;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+
+ typedef T *pointer;
+ typedef const T *const_pointer;
+
+ typedef T &reference;
+ typedef const T &const_reference;
+
+ pointer address(reference r) const { return &r; }
+ const_pointer address(const_reference r) const { return &r; }
+
+ explicit Memroot_allocator(MEM_ROOT *memroot) : m_memroot(memroot) {}
+
+ explicit Memroot_allocator() : m_memroot(nullptr) {}
+
+ template <class U>
+ Memroot_allocator(const Memroot_allocator<U> &other)
+ : m_memroot(other.memroot()) {}
+
+ template <class U>
+ Memroot_allocator &operator=(
+ const Memroot_allocator<U> &other MY_ATTRIBUTE((unused))) {
+ DBUG_ASSERT(m_memroot == other.memroot()); // Don't swap memroot.
+ }
+
+ pointer allocate(size_type n, const_pointer hint MY_ATTRIBUTE((unused)) = 0) {
+ if (n == 0) return NULL;
+ if (n > max_size()) throw std::bad_alloc();
+
+ pointer p = static_cast<pointer>(m_memroot->Alloc(n * sizeof(T)));
+ if (p == NULL) throw std::bad_alloc();
+ return p;
+ }
+
+ void deallocate(pointer, size_type) {}
+
+ template <class U, class... Args>
+ void construct(U *p, Args &&... args) {
+ DBUG_ASSERT(p != NULL);
+ try {
+ ::new ((void *)p) U(std::forward<Args>(args)...);
+ } catch (...) {
+ DBUG_ASSERT(false); // Constructor should not throw an exception.
+ }
+ }
+
+ void destroy(pointer p) {
+ DBUG_ASSERT(p != NULL);
+ try {
+ p->~T();
+ } catch (...) {
+ DBUG_ASSERT(false); // Destructor should not throw an exception
+ }
+ }
+
+ size_type max_size() const {
+ return std::numeric_limits<size_t>::max() / sizeof(T);
+ }
+
+ template <class U>
+ struct rebind {
+ typedef Memroot_allocator<U> other;
+ };
+
+ MEM_ROOT *memroot() const { return m_memroot; }
+};
+
+template <class T>
+bool operator==(const Memroot_allocator<T> &a1,
+ const Memroot_allocator<T> &a2) {
+ return a1.memroot() == a2.memroot();
+}
+
+template <class T>
+bool operator!=(const Memroot_allocator<T> &a1,
+ const Memroot_allocator<T> &a2) {
+ return a1.memroot() != a2.memroot();
+}
+
+#endif // MEMROOT_ALLOCATOR_INCLUDED
diff --git a/contrib/libs/libmysql_r/sql/partition_element.h b/contrib/libs/libmysql_r/sql/partition_element.h
new file mode 100644
index 0000000000..a22e8042f1
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/partition_element.h
@@ -0,0 +1,175 @@
+#ifndef PARTITION_ELEMENT_INCLUDED
+#define PARTITION_ELEMENT_INCLUDED
+
+/* Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "my_base.h" /* ha_rows */
+#include "sql/handler.h" /* UNDEF_NODEGROUP */
+
+/**
+ * An enum and a struct to handle partitioning and subpartitioning.
+ */
+enum class partition_type { NONE = 0, RANGE, HASH, LIST };
+
+enum partition_state {
+ PART_NORMAL = 0,
+ PART_IS_DROPPED = 1,
+ PART_TO_BE_DROPPED = 2,
+ PART_TO_BE_ADDED = 3,
+ PART_TO_BE_REORGED = 4,
+ PART_REORGED_DROPPED = 5,
+ PART_CHANGED = 6,
+ PART_IS_CHANGED = 7,
+ PART_IS_ADDED = 8,
+ PART_ADMIN = 9
+};
+
+/*
+ This struct is used to keep track of column expressions as part
+ of the COLUMNS concept in conjunction with RANGE and LIST partitioning.
+ The value can be either of MINVALUE, MAXVALUE and an expression that
+ must be constant and evaluate to the same type as the column it
+ represents.
+
+ The data in this fixed in two steps. The parser will only fill in whether
+ it is a max_value or provide an expression. Filling in
+ column_value, part_info, partition_id, null_value is done by the
+ function fix_column_value_function. However the item tree needs
+ fixed also before writing it into the frm file (in add_column_list_values).
+ To distinguish between those two variants, fixed= 1 after the
+ fixing in add_column_list_values and fixed= 2 otherwise. This is
+ since the fixing in add_column_list_values isn't a complete fixing.
+*/
+
+typedef struct p_column_list_val {
+ union column_value_union {
+ /**
+ When a table is opened this is set to the field image of the value
+ from the item_expression below.
+ */
+ const uchar *field_image;
+ /**
+ When the values are read from dd.Partition_value it is carried as
+ a C-string.
+ */
+ const char *value_str;
+ } column_value;
+ /**
+ When partition clause is parsed this is set to the item expression
+ for the value. Must be NULL if the value was not parsed, but
+ read from dd.Partition_value instead.
+ */
+ Item *item_expression;
+ partition_info *part_info;
+ uint partition_id;
+ /** MAXVALUE is set (only for RANGE COLUMNS) */
+ bool max_value;
+ /** NULL is set (only for LIST COLUMNS) */
+ bool null_value;
+ char fixed;
+} part_column_list_val;
+
+/*
+ This struct is used to contain the value of an element
+ in the VALUES IN struct. It needs to keep knowledge of
+ whether it is a signed/unsigned value and whether it is
+ NULL or not.
+*/
+
+typedef struct p_elem_val {
+ longlong value;
+ uint added_items;
+ bool null_value;
+ bool unsigned_flag;
+ part_column_list_val *col_val_array;
+} part_elem_value;
+
+class partition_element {
+ public:
+ List<partition_element> subpartitions;
+ List<part_elem_value> list_val_list; // list of LIST values/column arrays
+ // TODO: Handle options in a more general way, like dd::Properties
+ // for max/min rows, tablespace, data/index file, nodegroup etc.
+ ha_rows part_max_rows;
+ ha_rows part_min_rows;
+ longlong range_value;
+ const char *partition_name;
+ const char *tablespace_name;
+ char *part_comment;
+ const char *data_file_name;
+ const char *index_file_name;
+ handlerton *engine_type;
+ enum partition_state part_state;
+ uint16 nodegroup_id;
+ bool has_null_value;
+ /* TODO: Move this to partition_info?*/
+ bool signed_flag; // Range value signed
+ bool max_value; // MAXVALUE range
+
+ partition_element()
+ : part_max_rows(0),
+ part_min_rows(0),
+ range_value(0),
+ partition_name(NULL),
+ tablespace_name(NULL),
+ part_comment(NULL),
+ data_file_name(NULL),
+ index_file_name(NULL),
+ engine_type(NULL),
+ part_state(PART_NORMAL),
+ nodegroup_id(UNDEF_NODEGROUP),
+ has_null_value(false),
+ signed_flag(false),
+ max_value(false) {}
+ partition_element(partition_element *part_elem)
+ : part_max_rows(part_elem->part_max_rows),
+ part_min_rows(part_elem->part_min_rows),
+ range_value(0),
+ partition_name(NULL),
+ tablespace_name(part_elem->tablespace_name),
+ part_comment(part_elem->part_comment),
+ data_file_name(part_elem->data_file_name),
+ index_file_name(part_elem->index_file_name),
+ engine_type(part_elem->engine_type),
+ part_state(part_elem->part_state),
+ nodegroup_id(part_elem->nodegroup_id),
+ has_null_value(false),
+ signed_flag(false),
+ max_value(false) {}
+ inline void set_from_info(const HA_CREATE_INFO *info) {
+ data_file_name = info->data_file_name;
+ index_file_name = info->index_file_name;
+ tablespace_name = info->tablespace;
+ part_max_rows = info->max_rows;
+ part_min_rows = info->min_rows;
+ }
+ inline void put_to_info(HA_CREATE_INFO *info) const {
+ info->data_file_name = data_file_name;
+ info->index_file_name = index_file_name;
+ info->tablespace = tablespace_name;
+ info->max_rows = part_max_rows;
+ info->min_rows = part_min_rows;
+ }
+};
+
+#endif /* PARTITION_ELEMENT_INCLUDED */
diff --git a/contrib/libs/libmysql_r/sql/partition_info.h b/contrib/libs/libmysql_r/sql/partition_info.h
new file mode 100644
index 0000000000..b6ee9cc38b
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/partition_info.h
@@ -0,0 +1,638 @@
+#ifndef PARTITION_INFO_INCLUDED
+#define PARTITION_INFO_INCLUDED
+
+/* Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <stddef.h>
+#include <sys/types.h>
+
+#include "my_bitmap.h"
+#include "my_inttypes.h"
+#include "sql/lock.h" // Tablespace_hash_set
+#include "sql/partition_element.h"
+#include "sql/sql_bitmap.h" // Bitmap
+#include "sql/sql_data_change.h" // enum_duplicates
+#include "sql/sql_list.h"
+
+class Field;
+class Item;
+class Partition_handler;
+class String;
+class THD;
+class handler;
+struct HA_CREATE_INFO;
+struct TABLE;
+struct handlerton;
+
+#define NOT_A_PARTITION_ID UINT_MAX32
+
+class Create_field;
+class partition_info;
+struct PARTITION_ITERATOR;
+struct TABLE_LIST;
+
+/**
+ A "Get next" function for partition iterator.
+
+
+ Depending on whether partitions or sub-partitions are iterated, the
+ function returns next subpartition id/partition number. The sequence of
+ returned numbers is not ordered and may contain duplicates.
+
+ When the end of sequence is reached, NOT_A_PARTITION_ID is returned, and
+ the iterator resets itself (so next get_next() call will start to
+ enumerate the set all over again).
+
+ @param[in,out] part_iter Partition iterator, you call only
+ "iter.get_next(&iter)"
+
+ @return Partition id
+ @retval NOT_A_PARTITION_ID if there are no more partitions.
+ @retval [sub]partition_id of the next partition
+*/
+typedef uint32 (*partition_iter_func)(PARTITION_ITERATOR *part_iter);
+
+/**
+ Partition set iterator. Used to enumerate a set of [sub]partitions
+ obtained in partition interval analysis (see get_partitions_in_range_iter).
+
+ For the user, the only meaningful field is get_next, which may be used as
+ follows:
+ part_iterator.get_next(&part_iterator);
+
+ Initialization is done by any of the following calls:
+ - get_partitions_in_range_iter-type function call
+ - init_single_partition_iterator()
+ - init_all_partitions_iterator()
+ Cleanup is not needed.
+*/
+
+struct PARTITION_ITERATOR {
+ partition_iter_func get_next;
+ /*
+ Valid for "Interval mapping" in LIST partitioning: if true, let the
+ iterator also produce id of the partition that contains NULL value.
+ */
+ bool ret_null_part, ret_null_part_orig;
+ struct st_part_num_range {
+ uint32 start;
+ uint32 cur;
+ uint32 end;
+ };
+
+ struct st_field_value_range {
+ ulonglong start;
+ ulonglong cur;
+ ulonglong end;
+ };
+
+ union {
+ struct st_part_num_range part_nums;
+ struct st_field_value_range field_vals;
+ };
+ partition_info *part_info;
+};
+
+typedef struct {
+ longlong list_value;
+ uint32 partition_id;
+} LIST_PART_ENTRY;
+
+/* Some function typedefs */
+typedef int (*get_part_id_func)(partition_info *part_info, uint32 *part_id,
+ longlong *func_value);
+typedef int (*get_subpart_id_func)(partition_info *part_info, uint32 *part_id);
+
+/**
+ Get an iterator for set of partitions that match given field-space interval.
+
+ Functions with this signature are used to perform "Partitioning Interval
+ Analysis". This analysis is applicable for any type of [sub]partitioning
+ by some function of a single fieldX. The idea is as follows:
+ Given an interval "const1 <=? fieldX <=? const2", find a set of partitions
+ that may contain records with value of fieldX within the given interval.
+
+ The min_val, max_val and flags parameters specify the interval.
+ The set of partitions is returned by initializing an iterator in *part_iter
+
+ @note
+ There are currently three functions of this type:
+ - get_part_iter_for_interval_via_walking
+ - get_part_iter_for_interval_cols_via_map
+ - get_part_iter_for_interval_via_mapping
+
+ @param part_info Partitioning info
+ @param is_subpart When true, act for sub partitions. When false, act
+ for partitions.
+ @param store_length_array Length of fields packed in opt_range_key format
+ @param min_val Left edge, field value in opt_range_key format
+ @param max_val Right edge, field value in opt_range_key format
+ @param min_len Length of minimum value
+ @param max_len Length of maximum value
+ @param flags Some combination of NEAR_MIN, NEAR_MAX,
+ NO_MIN_RANGE, NO_MAX_RANGE
+ @param part_iter Iterator structure to be initialized
+
+ @return Operation status
+ @retval 0 No matching partitions, iterator not initialized
+ @retval 1 Some partitions would match, iterator intialized for traversing
+ them
+ @retval -1 All partitions would match, iterator not initialized
+*/
+
+typedef int (*get_partitions_in_range_iter)(
+ partition_info *part_info, bool is_subpart, uint32 *store_length_array,
+ uchar *min_val, uchar *max_val, uint min_len, uint max_len, uint flags,
+ PARTITION_ITERATOR *part_iter);
+/**
+ PARTITION BY KEY ALGORITHM=N
+ Which algorithm to use for hashing the fields.
+ N = 1 - Use 5.1 hashing (numeric fields are hashed as binary)
+ N = 2 - Use 5.5 hashing (numeric fields are hashed like latin1 bytes)
+*/
+enum class enum_key_algorithm {
+ KEY_ALGORITHM_NONE = 0,
+ KEY_ALGORITHM_51 = 1,
+ KEY_ALGORITHM_55 = 2
+};
+
+class Parser_partition_info {
+ public:
+ partition_info *const part_info;
+ partition_element *const current_partition; // partition
+ partition_element *const curr_part_elem; // part or sub part
+ part_elem_value *curr_list_val;
+ uint curr_list_object;
+ uint count_curr_subparts;
+
+ public:
+ Parser_partition_info(partition_info *const part_info,
+ partition_element *const current_partition,
+ partition_element *const curr_part_elem,
+ part_elem_value *curr_list_val, uint curr_list_object)
+ : part_info(part_info),
+ current_partition(current_partition),
+ curr_part_elem(curr_part_elem),
+ curr_list_val(curr_list_val),
+ curr_list_object(curr_list_object),
+ count_curr_subparts(0) {}
+
+ void init_col_val(part_column_list_val *col_val, Item *item);
+ part_column_list_val *add_column_value();
+ bool add_max_value();
+ bool reorganize_into_single_field_col_val();
+ bool init_column_part();
+ bool add_column_list_value(THD *thd, Item *item);
+};
+
+class partition_info {
+ public:
+ /*
+ * Here comes a set of definitions needed for partitioned table handlers.
+ */
+ List<partition_element> partitions;
+ List<partition_element> temp_partitions;
+
+ List<char> part_field_list;
+ List<char> subpart_field_list;
+
+ /*
+ If there is no subpartitioning, use only this func to get partition ids.
+
+ If there is subpartitioning use this to get the partition_id which will
+ consider the subpartition as well. See the below example
+
+ A table with 3 partition and 0 subpartition then the return value will
+ lie in the range of [0, 2]
+
+ A table with 3 partition and 3 subpartition then the return value will
+ lie in the range of [0, 8(no of partition X no of sub_partition -1)].
+ */
+ get_part_id_func get_partition_id;
+
+ /* Get partition id when we don't have subpartitioning
+ OR
+ Have both partition and subpartition fields but we don't want to consider
+ the subpartitions.
+ For example:
+ A table with 3 partition and 3 subpartition then the return value will
+ lie in the range of [0, 2].
+ */
+ get_part_id_func get_part_partition_id;
+
+ /*
+ Get subpartition id when we have don't have partition fields by we do
+ have subpartition ids.
+ Mikael said that for given constant tuple
+ {subpart_field1, ..., subpart_fieldN} the subpartition id will be the
+ same in all subpartitions
+ */
+ get_subpart_id_func get_subpartition_id;
+
+ /*
+ When we have various string fields we might need some preparation
+ before and clean-up after calling the get_part_id_func's. We need
+ one such method for get_part_partition_id and one for
+ get_subpartition_id.
+ */
+ get_part_id_func get_part_partition_id_charset;
+ get_subpart_id_func get_subpartition_id_charset;
+
+ /* NULL-terminated array of fields used in partitioned expression */
+ Field **part_field_array;
+ Field **subpart_field_array;
+ Field **part_charset_field_array;
+ Field **subpart_charset_field_array;
+ /*
+ Array of all fields used in partition and subpartition expression,
+ without duplicates, NULL-terminated.
+ */
+ Field **full_part_field_array;
+ /*
+ Set of all fields used in partition and subpartition expression.
+ Required for testing of partition fields in write_set when
+ updating. We need to set all bits in read_set because the row may
+ need to be inserted in a different [sub]partition.
+ */
+ MY_BITMAP full_part_field_set;
+
+ /*
+ When we have a field that requires transformation before calling the
+ partition functions we must allocate field buffers for the field of
+ the fields in the partition function.
+ */
+ uchar **part_field_buffers;
+ uchar **subpart_field_buffers;
+ uchar **restore_part_field_ptrs;
+ uchar **restore_subpart_field_ptrs;
+
+ Item *part_expr;
+ Item *subpart_expr;
+
+ Item *item_list;
+
+ /*
+ Bitmaps of partitions used by the current query.
+ * read_partitions - partitions to be used for reading.
+ * lock_partitions - partitions that must be locked (read or write).
+ Usually read_partitions is the same set as lock_partitions, but
+ in case of UPDATE the WHERE clause can limit the read_partitions set,
+ but not neccesarily the lock_partitions set.
+ Usage pattern:
+ * Initialized in ha_partition::open().
+ * read+lock_partitions is set according to explicit PARTITION,
+ WL#5217, in open_and_lock_tables().
+ * Bits in read_partitions can be cleared in prune_partitions()
+ in the optimizing step.
+ (WL#4443 is about allowing prune_partitions() to affect lock_partitions
+ and be done before locking too).
+ * When the partition enabled handler get an external_lock call it locks
+ all partitions in lock_partitions (and remembers which partitions it
+ locked, so that it can unlock them later). In case of LOCK TABLES it will
+ lock all partitions, and keep them locked while lock_partitions can
+ change for each statement under LOCK TABLES.
+ * Freed at the same time item_list is freed.
+ */
+ MY_BITMAP read_partitions;
+ MY_BITMAP lock_partitions;
+ bool bitmaps_are_initialized;
+ // TODO: Add first_read_partition and num_read_partitions?
+
+ union {
+ longlong *range_int_array;
+ LIST_PART_ENTRY *list_array;
+ part_column_list_val *range_col_array;
+ part_column_list_val *list_col_array;
+ };
+
+ /********************************************
+ * INTERVAL ANALYSIS
+ ********************************************/
+ /*
+ Partitioning interval analysis function for partitioning, or NULL if
+ interval analysis is not supported for this kind of partitioning.
+ */
+ get_partitions_in_range_iter get_part_iter_for_interval;
+ /*
+ Partitioning interval analysis function for subpartitioning, or NULL if
+ interval analysis is not supported for this kind of partitioning.
+ */
+ get_partitions_in_range_iter get_subpart_iter_for_interval;
+
+ /********************************************
+ * INTERVAL ANALYSIS ENDS
+ ********************************************/
+
+ longlong err_value;
+
+ char *part_func_string; //!< Partition expression as string
+ char *subpart_func_string; //!< Subpartition expression as string
+
+ uint num_columns;
+
+ TABLE *table;
+ /*
+ These Key_maps are used for Partitioning to enable quick decisions
+ on whether we can derive more information about which partition to
+ scan just by looking at what index is used.
+ */
+ Key_map all_fields_in_PF, all_fields_in_PPF, all_fields_in_SPF;
+ Key_map some_fields_in_PF;
+
+ handlerton *default_engine_type;
+ partition_type part_type;
+ partition_type subpart_type;
+
+ size_t part_func_len;
+ size_t subpart_func_len;
+
+ uint num_parts;
+ uint num_subparts;
+
+ uint num_list_values;
+
+ uint num_part_fields;
+ uint num_subpart_fields;
+ uint num_full_part_fields;
+
+ uint has_null_part_id;
+ /*
+ This variable is used to calculate the partition id when using
+ LINEAR KEY/HASH. This functionality is kept in the MySQL Server
+ but mainly of use to handlers supporting partitioning.
+ */
+ uint16 linear_hash_mask;
+
+ enum_key_algorithm key_algorithm;
+
+ /* Only the number of partitions defined (uses default names and options). */
+ bool use_default_partitions;
+ bool use_default_num_partitions;
+ /* Only the number of subpartitions defined (uses default names etc.). */
+ bool use_default_subpartitions;
+ bool use_default_num_subpartitions;
+ bool default_partitions_setup;
+ bool defined_max_value;
+ bool list_of_part_fields; // KEY or COLUMNS PARTITIONING
+ bool list_of_subpart_fields; // KEY SUBPARTITIONING
+ bool linear_hash_ind; // LINEAR HASH/KEY
+ bool fixed;
+ bool is_auto_partitioned;
+ bool has_null_value;
+ bool column_list; // COLUMNS PARTITIONING, 5.5+
+ /**
+ True if pruning has been completed and can not be pruned any further,
+ even if there are subqueries or stored programs in the condition.
+
+ Some times it is needed to run prune_partitions() a second time to prune
+ read partitions after tables are locked, when subquery and
+ stored functions might have been evaluated.
+ */
+ bool is_pruning_completed;
+
+ partition_info()
+ : get_partition_id(NULL),
+ get_part_partition_id(NULL),
+ get_subpartition_id(NULL),
+ part_field_array(NULL),
+ subpart_field_array(NULL),
+ part_charset_field_array(NULL),
+ subpart_charset_field_array(NULL),
+ full_part_field_array(NULL),
+ part_field_buffers(NULL),
+ subpart_field_buffers(NULL),
+ restore_part_field_ptrs(NULL),
+ restore_subpart_field_ptrs(NULL),
+ part_expr(NULL),
+ subpart_expr(NULL),
+ item_list(NULL),
+ bitmaps_are_initialized(false),
+ list_array(NULL),
+ err_value(0),
+ part_func_string(NULL),
+ subpart_func_string(NULL),
+ num_columns(0),
+ table(NULL),
+ default_engine_type(NULL),
+ part_type(partition_type::NONE),
+ subpart_type(partition_type::NONE),
+ part_func_len(0),
+ subpart_func_len(0),
+ num_parts(0),
+ num_subparts(0),
+ num_list_values(0),
+ num_part_fields(0),
+ num_subpart_fields(0),
+ num_full_part_fields(0),
+ has_null_part_id(0),
+ linear_hash_mask(0),
+ key_algorithm(enum_key_algorithm::KEY_ALGORITHM_NONE),
+ use_default_partitions(true),
+ use_default_num_partitions(true),
+ use_default_subpartitions(true),
+ use_default_num_subpartitions(true),
+ default_partitions_setup(false),
+ defined_max_value(false),
+ list_of_part_fields(false),
+ list_of_subpart_fields(false),
+ linear_hash_ind(false),
+ fixed(false),
+ is_auto_partitioned(false),
+ has_null_value(false),
+ column_list(false),
+ is_pruning_completed(false) {
+ partitions.empty();
+ temp_partitions.empty();
+ part_field_list.empty();
+ subpart_field_list.empty();
+ }
+
+ partition_info *get_clone(THD *thd, bool reset = false);
+ partition_info *get_full_clone(THD *thd);
+ bool set_named_partition_bitmap(const char *part_name, size_t length);
+ bool set_partition_bitmaps(TABLE_LIST *table_list);
+ bool set_read_partitions(List<String> *partition_names);
+ /* Answers the question if subpartitioning is used for a certain table */
+ inline bool is_sub_partitioned() const {
+ return subpart_type != partition_type::NONE;
+ }
+
+ /* Returns the total number of partitions on the leaf level */
+ inline uint get_tot_partitions() const {
+ return num_parts * (is_sub_partitioned() ? num_subparts : 1);
+ }
+
+ bool set_up_defaults_for_partitioning(Partition_handler *part_handler,
+ HA_CREATE_INFO *info, uint start_no);
+ char *find_duplicate_field();
+ const char *find_duplicate_name();
+ bool check_engine_mix(handlerton *engine_type, bool default_engine);
+ bool check_range_constants(THD *thd);
+ bool check_list_constants(THD *thd);
+ bool check_partition_info(THD *thd, handlerton **eng_type, handler *file,
+ HA_CREATE_INFO *info,
+ bool check_partition_function);
+ void print_no_partition_found(THD *thd, TABLE *table);
+ void print_debug(const char *str, uint *);
+ Item *get_column_item(Item *item, Field *field);
+ bool fix_partition_values(part_elem_value *val, partition_element *part_elem,
+ uint part_id);
+ bool fix_column_value_functions(THD *thd, part_elem_value *val, uint part_id);
+ bool fix_parser_data(THD *thd);
+ bool set_part_expr(char *start_token, Item *item_ptr, char *end_token,
+ bool is_subpart);
+ static bool compare_column_values(const part_column_list_val *a,
+ const part_column_list_val *b);
+ bool set_up_charset_field_preps();
+ bool check_partition_field_length();
+ void set_show_version_string(String *packet);
+ partition_element *get_part_elem(const char *partition_name, char *file_name,
+ uint32 *part_id);
+ void report_part_expr_error(bool use_subpart_expr);
+ bool set_used_partition(List<Item> &fields, List<Item> &values,
+ COPY_INFO &info, bool copy_default_values,
+ MY_BITMAP *used_partitions);
+ /**
+ PRUNE_NO - Unable to prune.
+ PRUNE_DEFAULTS - Partitioning field is only set to
+ DEFAULT values, only need to check
+ pruning for one row where the DEFAULTS
+ values are set.
+ PRUNE_YES - Pruning is possible, calculate the used partition set
+ by evaluate the partition_id on row by row basis.
+ */
+ enum enum_can_prune { PRUNE_NO = 0, PRUNE_DEFAULTS, PRUNE_YES };
+ bool can_prune_insert(THD *thd, enum_duplicates duplic, COPY_INFO &update,
+ List<Item> &update_fields, List<Item> &fields,
+ bool empty_values, enum_can_prune *can_prune_partitions,
+ bool *prune_needs_default_values,
+ MY_BITMAP *used_partitions);
+ bool has_same_partitioning(partition_info *new_part_info);
+ inline bool is_partition_used(uint part_id) const {
+ return bitmap_is_set(&read_partitions, part_id);
+ }
+ inline bool is_partition_locked(uint part_id) const {
+ return bitmap_is_set(&lock_partitions, part_id);
+ }
+ inline uint num_partitions_used() {
+ return bitmap_bits_set(&read_partitions);
+ }
+ inline uint get_first_used_partition() const {
+ return bitmap_get_first_set(&read_partitions);
+ }
+ inline uint get_next_used_partition(uint part_id) const {
+ return bitmap_get_next_set(&read_partitions, part_id);
+ }
+ bool same_key_column_order(List<Create_field> *create_list);
+
+ /**
+ Allocate memory for one partitions bitmap and initialize it.
+
+ @param bitmap Bitmap instance to initialize.
+ @param mem_root Memory root to use for bitmap buffer allocation.
+
+ @retval true Memory allocation failure
+ @retval false Success
+ */
+ bool init_partition_bitmap(MY_BITMAP *bitmap, MEM_ROOT *mem_root);
+
+ private:
+ bool set_up_default_partitions(Partition_handler *part_handler,
+ HA_CREATE_INFO *info, uint start_no);
+ bool set_up_default_subpartitions(Partition_handler *part_handler,
+ HA_CREATE_INFO *info);
+ char *create_default_partition_names(uint num_parts, uint start_no);
+ char *create_default_subpartition_name(uint subpart_no,
+ const char *part_name);
+ bool add_named_partition(const char *part_name, size_t length);
+ bool is_fields_in_part_expr(List<Item> &fields);
+ bool is_full_part_expr_in_fields(List<Item> &fields);
+};
+
+uint32 get_next_partition_id_range(PARTITION_ITERATOR *part_iter);
+bool check_partition_dirs(partition_info *part_info);
+
+/* Initialize the iterator to return a single partition with given part_id */
+
+static inline void init_single_partition_iterator(
+ uint32 part_id, PARTITION_ITERATOR *part_iter) {
+ part_iter->part_nums.start = part_iter->part_nums.cur = part_id;
+ part_iter->part_nums.end = part_id + 1;
+ part_iter->ret_null_part = part_iter->ret_null_part_orig = false;
+ part_iter->get_next = get_next_partition_id_range;
+}
+
+/* Initialize the iterator to enumerate all partitions */
+static inline void init_all_partitions_iterator(partition_info *part_info,
+ PARTITION_ITERATOR *part_iter) {
+ part_iter->part_nums.start = part_iter->part_nums.cur = 0;
+ part_iter->part_nums.end = part_info->num_parts;
+ part_iter->ret_null_part = part_iter->ret_null_part_orig = false;
+ part_iter->get_next = get_next_partition_id_range;
+}
+
+bool fill_partition_tablespace_names(partition_info *part_info,
+ Tablespace_hash_set *tablespace_set);
+
+/**
+ Check if all tablespace names specified for partitions have a valid length.
+
+ @param part_info Partition info that could be using tablespaces.
+
+ @return true One of the tablespace names specified has invalid length
+ and an error is reported.
+ @return false All the tablespace names specified for partitions have
+ a valid length.
+*/
+
+bool validate_partition_tablespace_name_lengths(partition_info *part_info);
+
+/**
+ Check if all tablespace names specified for partitions are valid.
+
+ Do the validation by invoking the SE specific validation function.
+
+ @param part_info Partition info that could be using tablespaces.
+ @param default_engine Table level engine.
+
+ @return true One of the tablespace names specified is invalid
+ and an error is reported.
+ @return false All the tablespace names specified for
+ partitions are valid.
+*/
+
+bool validate_partition_tablespace_names(partition_info *part_info,
+ const handlerton *default_engine);
+
+/**
+ Predicate which returns true if any partition or subpartition uses
+ an external data directory or external index directory.
+
+ @param pi partitioning information
+ @retval true if any partition or subpartition has an external
+ data directory or external index directory.
+ @retval false otherwise
+ */
+bool has_external_data_or_index_dir(partition_info &pi);
+
+#endif /* PARTITION_INFO_INCLUDED */
diff --git a/contrib/libs/libmysql_r/sql/psi_memory_key.h b/contrib/libs/libmysql_r/sql/psi_memory_key.h
new file mode 100644
index 0000000000..964ea6aa93
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/psi_memory_key.h
@@ -0,0 +1,189 @@
+/* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef PSI_MEMORY_KEY_INCLUDED
+#define PSI_MEMORY_KEY_INCLUDED
+
+/**
+ Instrumented memory key.
+ To instrument memory, a memory key must be obtained using @c register_memory.
+ Using a zero key always disable the instrumentation.
+*/
+
+void register_server_memory_keys();
+
+typedef unsigned int PSI_memory_key;
+
+/*
+ MAINTAINER: Please keep this list in order, to limit merge collisions.
+*/
+
+/*
+ These are defined in misc. .cc files, to avoid linkage problems
+ for tools like mysqlbinlog.cc and for unit tests.
+*/
+extern PSI_memory_key key_memory_Filesort_buffer_sort_keys;
+extern PSI_memory_key key_memory_Gtid_set_Interval_chunk;
+extern PSI_memory_key key_memory_Gtid_set_to_string;
+extern PSI_memory_key key_memory_Incident_log_event_message;
+extern PSI_memory_key key_memory_NAMED_ILINK_name;
+extern PSI_memory_key key_memory_Rows_query_log_event_rows_query;
+extern PSI_memory_key key_memory_Sid_map_Node;
+extern PSI_memory_key key_memory_String_value;
+extern PSI_memory_key key_memory_log_error_loaded_services;
+extern PSI_memory_key key_memory_log_error_stack;
+extern PSI_memory_key key_memory_log_event;
+extern PSI_memory_key key_memory_Gtid_state_group_commit_sidno;
+
+/*
+ These are defined in psi_memory_key.cc
+ */
+extern PSI_memory_key key_memory_DATE_TIME_FORMAT;
+extern PSI_memory_key key_memory_DD_column_statistics;
+extern PSI_memory_key key_memory_DD_default_values;
+extern PSI_memory_key key_memory_DD_import;
+extern PSI_memory_key key_memory_DD_String_type;
+extern PSI_memory_key key_memory_Event_queue_element_for_exec_names;
+extern PSI_memory_key key_memory_Event_scheduler_scheduler_param;
+extern PSI_memory_key key_memory_File_query_log_name;
+extern PSI_memory_key key_memory_Filesort_info_merge;
+extern PSI_memory_key key_memory_Filesort_info_record_pointers;
+extern PSI_memory_key key_memory_Gcalc_dyn_list_block;
+extern PSI_memory_key key_memory_Geometry_objects_data;
+extern PSI_memory_key key_memory_Gis_read_stream_err_msg;
+extern PSI_memory_key key_memory_HASH_ROW_ENTRY;
+extern PSI_memory_key key_memory_JOIN_CACHE;
+extern PSI_memory_key key_memory_JSON;
+extern PSI_memory_key key_memory_LOG_POS_COORD;
+extern PSI_memory_key key_memory_LOG_name;
+extern PSI_memory_key key_memory_MPVIO_EXT_auth_info;
+extern PSI_memory_key key_memory_MYSQL_BIN_LOG_basename;
+extern PSI_memory_key key_memory_MYSQL_BIN_LOG_index;
+extern PSI_memory_key key_memory_MYSQL_LOCK;
+extern PSI_memory_key key_memory_MYSQL_LOG_name;
+extern PSI_memory_key key_memory_MYSQL_RELAY_LOG_basename;
+extern PSI_memory_key key_memory_MYSQL_RELAY_LOG_index;
+extern PSI_memory_key key_memory_Mutex_cond_array_Mutex_cond;
+extern PSI_memory_key key_memory_NET_buff;
+extern PSI_memory_key key_memory_NET_compress_packet;
+extern PSI_memory_key key_memory_Owned_gtids_sidno_to_hash;
+extern PSI_memory_key key_memory_Owned_gtids_to_string;
+extern PSI_memory_key key_memory_PROFILE;
+extern PSI_memory_key key_memory_QUICK_RANGE_SELECT_mrr_buf_desc;
+extern PSI_memory_key key_memory_Quick_ranges;
+extern PSI_memory_key key_memory_READ_INFO;
+extern PSI_memory_key key_memory_READ_RECORD_cache;
+extern PSI_memory_key key_memory_Recovered_xa_transactions;
+extern PSI_memory_key key_memory_Relay_log_info_group_relay_log_name;
+extern PSI_memory_key key_memory_Row_data_memory_memory;
+extern PSI_memory_key key_memory_Rpl_info_file_buffer;
+extern PSI_memory_key key_memory_Rpl_info_table;
+extern PSI_memory_key key_memory_SLAVE_INFO;
+extern PSI_memory_key key_memory_ST_SCHEMA_TABLE;
+extern PSI_memory_key key_memory_Security_context;
+extern PSI_memory_key key_memory_Slave_applier_json_diff_vector;
+extern PSI_memory_key key_memory_Slave_job_group_group_relay_log_name;
+extern PSI_memory_key key_memory_Sort_param_tmp_buffer;
+extern PSI_memory_key key_memory_Sys_var_charptr_value;
+extern PSI_memory_key key_memory_TABLE;
+extern PSI_memory_key key_memory_TABLE_RULE_ENT;
+extern PSI_memory_key key_memory_TABLE_sort_io_cache;
+extern PSI_memory_key key_memory_TC_LOG_MMAP_pages;
+extern PSI_memory_key key_memory_THD_Session_sysvar_resource_manager;
+extern PSI_memory_key key_memory_THD_Session_tracker;
+extern PSI_memory_key key_memory_THD_db;
+extern PSI_memory_key key_memory_THD_handler_tables_hash;
+extern PSI_memory_key key_memory_THD_variables;
+extern PSI_memory_key key_memory_Table_trigger_dispatcher;
+extern PSI_memory_key key_memory_Unique_merge_buffer;
+extern PSI_memory_key key_memory_Unique_sort_buffer;
+extern PSI_memory_key key_memory_User_level_lock;
+extern PSI_memory_key key_memory_XID;
+extern PSI_memory_key key_memory_XID_STATE;
+extern PSI_memory_key key_memory_acl_mem;
+extern PSI_memory_key key_memory_acl_memex;
+extern PSI_memory_key key_memory_acl_cache;
+extern PSI_memory_key key_memory_acl_map_cache;
+extern PSI_memory_key key_memory_binlog_cache_mngr;
+extern PSI_memory_key key_memory_binlog_pos;
+extern PSI_memory_key key_memory_binlog_recover_exec;
+extern PSI_memory_key key_memory_binlog_statement_buffer;
+extern PSI_memory_key key_memory_binlog_ver_1_event;
+extern PSI_memory_key key_memory_bison_stack;
+extern PSI_memory_key key_memory_blob_mem_storage;
+extern PSI_memory_key key_memory_db_worker_hash_entry;
+extern PSI_memory_key key_memory_delegate;
+extern PSI_memory_key key_memory_errmsgs;
+extern PSI_memory_key key_memory_fill_schema_schemata;
+extern PSI_memory_key key_memory_native_functions;
+extern PSI_memory_key key_memory_gdl;
+extern PSI_memory_key key_memory_global_system_variables;
+extern PSI_memory_key key_memory_handler_errmsgs;
+extern PSI_memory_key key_memory_handlerton;
+extern PSI_memory_key key_memory_hash_index_key_buffer;
+extern PSI_memory_key key_memory_help;
+extern PSI_memory_key key_memory_histograms;
+extern PSI_memory_key key_memory_host_cache_hostname;
+extern PSI_memory_key key_memory_ignored_db;
+extern PSI_memory_key key_memory_locked_table_list;
+extern PSI_memory_key key_memory_locked_thread_list;
+extern PSI_memory_key key_memory_my_bitmap_map;
+extern PSI_memory_key key_memory_my_str_malloc;
+extern PSI_memory_key key_memory_opt_bin_logname;
+extern PSI_memory_key key_memory_partition_syntax_buffer;
+extern PSI_memory_key key_memory_prepared_statement_map;
+extern PSI_memory_key key_memory_prepared_statement_main_mem_root;
+extern PSI_memory_key key_memory_protocol_rset_root;
+extern PSI_memory_key key_memory_prune_partitions_exec;
+extern PSI_memory_key key_memory_queue_item;
+extern PSI_memory_key key_memory_quick_group_min_max_select_root;
+extern PSI_memory_key key_memory_quick_index_merge_root;
+extern PSI_memory_key key_memory_quick_range_select_root;
+extern PSI_memory_key key_memory_quick_ror_intersect_select_root;
+extern PSI_memory_key key_memory_quick_ror_union_select_root;
+extern PSI_memory_key key_memory_rpl_filter;
+extern PSI_memory_key key_memory_rpl_slave_check_temp_dir;
+extern PSI_memory_key key_memory_rpl_slave_command_buffer;
+extern PSI_memory_key key_memory_servers;
+extern PSI_memory_key key_memory_shared_memory_name;
+extern PSI_memory_key key_memory_show_slave_status_io_gtid_set;
+extern PSI_memory_key key_memory_sp_head_call_root;
+extern PSI_memory_key key_memory_sp_head_execute_root;
+extern PSI_memory_key key_memory_sp_head_main_root;
+extern PSI_memory_key key_memory_string_iterator;
+extern PSI_memory_key key_memory_table_def_memory;
+extern PSI_memory_key key_memory_table_mapping_root;
+extern PSI_memory_key key_memory_table_share;
+extern PSI_memory_key key_memory_table_triggers_list;
+extern PSI_memory_key key_memory_test_quick_select_exec;
+extern PSI_memory_key key_memory_thd_main_mem_root;
+extern PSI_memory_key key_memory_thd_timer;
+extern PSI_memory_key key_memory_thd_transactions;
+extern PSI_memory_key key_memory_user_conn;
+extern PSI_memory_key key_memory_user_var_entry;
+extern PSI_memory_key key_memory_user_var_entry_value;
+extern PSI_memory_key key_memory_warning_info_warn_root;
+extern PSI_memory_key key_memory_sp_cache;
+extern PSI_memory_key key_memory_write_set_extraction;
+extern PSI_memory_key key_memory_string_service_iterator;
+
+#endif // PSI_MEMORY_KEY_INCLUDED
diff --git a/contrib/libs/libmysql_r/sql/query_options.h b/contrib/libs/libmysql_r/sql/query_options.h
new file mode 100644
index 0000000000..fe4b27b6ce
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/query_options.h
@@ -0,0 +1,125 @@
+/* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef QUERY_OPTIONS_INCLUDED
+#define QUERY_OPTIONS_INCLUDED
+
+/**
+ @file
+
+ @details
+ This file is used in the server, and the mysqlbinlog client.
+*/
+
+/*
+ This is included in the server and in the client.
+ Options for select set by the yacc parser (stored in lex->options).
+
+ NOTE
+ log_event.h defines OPTIONS_WRITTEN_TO_BIN_LOG to specify what THD
+ options list are written into binlog. These options can NOT change their
+ values, or it will break replication between version.
+
+ context is encoded as following:
+ SELECT - SELECT_LEX::options
+ THD - THD::options
+ intern - neither. used only as
+ func(..., select_node->options | thd->options | OPTION_XXX, ...)
+
+ TODO: separate three contexts above, move them to separate bitfields.
+*/
+
+#define SELECT_DISTINCT (1ULL << 0) // SELECT, user
+#define SELECT_STRAIGHT_JOIN (1ULL << 1) // SELECT, user
+// Free slot, used to be SELECT_DESCRIBE: (1ULL << 2)
+#define SELECT_SMALL_RESULT (1ULL << 3) // SELECT, user
+#define SELECT_BIG_RESULT (1ULL << 4) // SELECT, user
+#define OPTION_FOUND_ROWS \
+ (1ULL << 5) // SELECT, user
+ // 1ULL << 6 is free
+#define SELECT_NO_JOIN_CACHE (1ULL << 7) // intern
+/** always the opposite of OPTION_NOT_AUTOCOMMIT except when in fix_autocommit()
+ */
+#define OPTION_AUTOCOMMIT (1ULL << 8) // THD, user
+#define OPTION_BIG_SELECTS (1ULL << 9) // THD, user
+#define OPTION_LOG_OFF (1ULL << 10) // THD, user
+#define OPTION_QUOTE_SHOW_CREATE (1ULL << 11) // THD, user, unused
+#define TMP_TABLE_ALL_COLUMNS (1ULL << 12) // SELECT, intern
+#define OPTION_WARNINGS (1ULL << 13) // THD, user
+#define OPTION_AUTO_IS_NULL (1ULL << 14) // THD, user, binlog
+#define OPTION_FOUND_COMMENT (1ULL << 15) // DEPRECATED
+#define OPTION_SAFE_UPDATES (1ULL << 16) // THD, user
+#define OPTION_BUFFER_RESULT (1ULL << 17) // SELECT, user
+#define OPTION_BIN_LOG (1ULL << 18) // THD, user
+#define OPTION_NOT_AUTOCOMMIT (1ULL << 19) // THD, user
+#define OPTION_BEGIN (1ULL << 20) // THD, intern
+#define OPTION_TABLE_LOCK (1ULL << 21) // THD, intern
+#define OPTION_QUICK (1ULL << 22) // SELECT (for DELETE)
+#define OPTION_NO_CONST_TABLES (1ULL << 23) // No const tables, intern
+
+/* The following is used to detect a conflict with DISTINCT */
+#define SELECT_ALL (1ULL << 24) // SELECT, user, parser
+/** The following can be set when importing tables in a 'wrong order'
+ to suppress foreign key checks */
+#define OPTION_NO_FOREIGN_KEY_CHECKS (1ULL << 26) // THD, user, binlog
+/** The following speeds up inserts to InnoDB tables by suppressing unique
+ key checks in some cases */
+#define OPTION_RELAXED_UNIQUE_CHECKS (1ULL << 27) // THD, user, binlog
+#define SELECT_NO_UNLOCK (1ULL << 28) // SELECT, intern
+#define OPTION_SCHEMA_TABLE (1ULL << 29) // SELECT, intern
+/** Flag set if setup_tables already done */
+#define OPTION_SETUP_TABLES_DONE (1ULL << 30) // intern
+/** If not set then the thread will ignore all warnings with level notes. */
+#define OPTION_SQL_NOTES (1ULL << 31) // THD, user
+
+/** (1ULL << 32) is not used after removing TMP_TABLE_FORCE_MYISAM option */
+
+#define OPTION_PROFILING (1ULL << 33)
+/**
+ Indicates that this is a HIGH_PRIORITY SELECT.
+ Currently used only for printing of such selects.
+ Type of locks to be acquired is specified directly.
+*/
+#define SELECT_HIGH_PRIORITY (1ULL << 34) // SELECT, user
+/**
+ Is set in slave SQL thread when there was an
+ error on master, which, when is not reproducible
+ on slave (i.e. the query succeeds on slave),
+ is not terminal to the state of repliation,
+ and should be ignored. The slave SQL thread,
+ however, needs to rollback the effects of the
+ succeeded statement to keep replication consistent.
+*/
+#define OPTION_MASTER_SQL_ERROR (1ULL << 35)
+
+/*
+ Dont report errors for individual rows,
+ But just report error on commit (or read ofcourse)
+ Note! Reserved for use in MySQL Cluster
+*/
+#define OPTION_ALLOW_BATCH (1ULL << 36) // THD, intern (slave)
+
+#define OPTION_SELECT_FOR_SHOW (1ULL << 37) // SELECT for SHOW over DD.
+
+// Is set while thread is updating the data dictionary tables.
+#define OPTION_DD_UPDATE_CONTEXT (1ULL << 38) // intern
+#endif /* QUERY_OPTIONS_INCLUDED */
diff --git a/contrib/libs/libmysql_r/sql/rpl_constants.h b/contrib/libs/libmysql_r/sql/rpl_constants.h
new file mode 100644
index 0000000000..333f36e85a
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/rpl_constants.h
@@ -0,0 +1,75 @@
+/* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef RPL_CONSTANTS_H
+#define RPL_CONSTANTS_H
+
+/*
+ Constants used to parse the stream of bytes sent by a slave
+ when commands COM_BINLOG_DUMP or COM_BINLOG_DUMP_GTID are
+ sent.
+*/
+const int BINLOG_POS_INFO_SIZE = 8;
+const int BINLOG_DATA_SIZE_INFO_SIZE = 4;
+const int BINLOG_POS_OLD_INFO_SIZE = 4;
+const int BINLOG_FLAGS_INFO_SIZE = 2;
+const int BINLOG_SERVER_ID_INFO_SIZE = 4;
+const int BINLOG_NAME_SIZE_INFO_SIZE = 4;
+
+/**
+ If there is no more events to send send a @ref page_protocol_basic_err_packet
+ instead of blocking the connection.
+
+ @sa ::COM_BINLOG_DUMP, ::COM_BINLOG_DUMP_GTID
+*/
+const int BINLOG_DUMP_NON_BLOCK = 1 << 0;
+
+/**
+ Enumeration of the reserved formats of Binlog extra row information
+*/
+enum ExtraRowInfoFormat {
+ /** Ndb format */
+ ERIF_NDB = 0,
+
+ /** Reserved formats 0 -> 63 inclusive */
+ ERIF_LASTRESERVED = 63,
+
+ /**
+ Available / uncontrolled formats
+ 64 -> 254 inclusive
+ */
+ ERIF_OPEN1 = 64,
+ ERIF_OPEN2 = 65,
+
+ ERIF_LASTOPEN = 254,
+
+ /**
+ Multi-payload format 255
+
+ Length is total length, payload is sequence of
+ sub-payloads with their own headers containing
+ length + format.
+ */
+ ERIF_MULTI = 255
+};
+
+#endif /* RPL_CONSTANTS_H */
diff --git a/contrib/libs/libmysql_r/sql/rpl_gtid.h b/contrib/libs/libmysql_r/sql/rpl_gtid.h
new file mode 100644
index 0000000000..2eb3b94204
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/rpl_gtid.h
@@ -0,0 +1,3914 @@
+/* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef RPL_GTID_H_INCLUDED
+#define RPL_GTID_H_INCLUDED
+
+#include <atomic>
+#include <list>
+
+#include "libbinlogevents/include/uuid.h"
+#include "map_helpers.h"
+#include "my_dbug.h"
+#include "my_thread_local.h"
+#include "mysql/psi/mysql_cond.h"
+#include "mysql/psi/mysql_rwlock.h" // mysql_rwlock_t
+#include "prealloced_array.h" // Prealloced_array
+#include "sql/rpl_reporting.h" // MAX_SLAVE_ERRMSG
+#include "template_utils.h"
+#include "typelib.h"
+
+struct TABLE_LIST;
+class THD;
+
+/**
+ Report an error from code that can be linked into either the server
+ or mysqlbinlog. There is no common error reporting mechanism, so we
+ have to duplicate the error message (write it out in the source file
+ for mysqlbinlog, write it in share/errmsg-utf8.txt for the server).
+
+ @param MYSQLBINLOG_ERROR arguments to mysqlbinlog's 'error'
+ function, including the function call parentheses
+ @param SERVER_ERROR arguments to my_error, including the function
+ call parentheses.
+*/
+#ifndef MYSQL_SERVER
+#define BINLOG_ERROR(MYSQLBINLOG_ERROR, SERVER_ERROR) error MYSQLBINLOG_ERROR
+#else
+#define BINLOG_ERROR(MYSQLBINLOG_ERROR, SERVER_ERROR) my_error SERVER_ERROR
+#endif
+
+extern PSI_memory_key key_memory_Gtid_set_to_string;
+extern PSI_memory_key key_memory_Owned_gtids_to_string;
+extern PSI_memory_key key_memory_Gtid_state_to_string;
+extern PSI_memory_key key_memory_Gtid_cache_to_string;
+extern PSI_memory_key key_memory_Gtid_set_Interval_chunk;
+extern PSI_memory_key key_memory_Gtid_state_group_commit_sidno;
+
+extern std::atomic<ulong> gtid_mode_counter;
+
+/**
+ This macro is used to check that the given character, pointed to by the
+ character pointer, is a space or not.
+*/
+#define SKIP_WHITESPACE() \
+ while (my_isspace(&my_charset_utf8_general_ci, *s)) s++
+/*
+ This macro must be used to filter out parts of the code that
+ is not used now but may be useful in future. In other words,
+ we want to keep such code until we make up our minds on whether
+ it should be removed or not.
+*/
+#undef NON_DISABLED_GTID
+
+/*
+ This macro must be used to filter out parts of the code that
+ is not used now but we are not sure if there is a bug around
+ them. In other words, we want to keep such code until we have
+ time to investigate it.
+*/
+#undef NON_ERROR_GTID
+
+#ifdef MYSQL_SERVER
+class String;
+class THD;
+#endif // ifdef MYSQL_SERVER
+
+/// Type of SIDNO (source ID number, first component of GTID)
+typedef int rpl_sidno;
+/// Type of GNO, the second (numeric) component of GTID
+typedef long long int rpl_gno;
+typedef int64 rpl_binlog_pos;
+
+/**
+ Generic return type for many functions that can succeed or fail.
+
+ This is used in conjuction with the macros below for functions where
+ the return status either indicates "success" or "failure". It
+ provides the following features:
+
+ - The macros can be used to conveniently propagate errors from
+ called functions back to the caller.
+
+ - If a function is expected to print an error using my_error before
+ it returns an error status, then the macros assert that my_error
+ has been called.
+
+ - Does a DBUG_PRINT before returning failure.
+*/
+enum enum_return_status {
+ /// The function completed successfully.
+ RETURN_STATUS_OK = 0,
+ /// The function completed with error but did not report it.
+ RETURN_STATUS_UNREPORTED_ERROR = 1,
+ /// The function completed with error and has called my_error.
+ RETURN_STATUS_REPORTED_ERROR = 2
+};
+
+/**
+ @def __CHECK_RETURN_STATUS
+ Lowest level macro used in the PROPAGATE_* and RETURN_* macros
+ below.
+
+ If DBUG_OFF is defined, does nothing. Otherwise, if STATUS is
+ RETURN_STATUS_OK, does nothing; otherwise, make a dbug printout and
+ (if ALLOW_UNREPORTED==0) assert that STATUS !=
+ RETURN_STATUS_UNREPORTED.
+
+ @param STATUS The status to return.
+ @param ACTION A text that describes what we are doing: either
+ "Returning" or "Propagating" (used in DBUG_PRINT macros)
+ @param STATUS_NAME The stringified version of the STATUS (used in
+ DBUG_PRINT macros).
+ @param ALLOW_UNREPORTED If false, the macro asserts that STATUS is
+ not RETURN_STATUS_UNREPORTED_ERROR.
+*/
+#ifdef DBUG_OFF
+#define __CHECK_RETURN_STATUS(STATUS, ACTION, STATUS_NAME, ALLOW_UNREPORTED)
+#else
+extern void check_return_status(enum_return_status status, const char *action,
+ const char *status_name, int allow_unreported);
+#define __CHECK_RETURN_STATUS(STATUS, ACTION, STATUS_NAME, ALLOW_UNREPORTED) \
+ check_return_status(STATUS, ACTION, STATUS_NAME, ALLOW_UNREPORTED);
+#endif
+/**
+ Low-level macro that checks if STATUS is RETURN_STATUS_OK; if it is
+ not, then RETURN_VALUE is returned.
+ @see __DO_RETURN_STATUS
+*/
+#define __PROPAGATE_ERROR(STATUS, RETURN_VALUE, ALLOW_UNREPORTED) \
+ do { \
+ enum_return_status __propagate_error_status = STATUS; \
+ if (__propagate_error_status != RETURN_STATUS_OK) { \
+ __CHECK_RETURN_STATUS(__propagate_error_status, "Propagating", #STATUS, \
+ ALLOW_UNREPORTED); \
+ DBUG_RETURN(RETURN_VALUE); \
+ } \
+ } while (0)
+/// Low-level macro that returns STATUS. @see __DO_RETURN_STATUS
+#define __RETURN_STATUS(STATUS, ALLOW_UNREPORTED) \
+ do { \
+ enum_return_status __return_status_status = STATUS; \
+ __CHECK_RETURN_STATUS(__return_status_status, "Returning", #STATUS, \
+ ALLOW_UNREPORTED); \
+ DBUG_RETURN(__return_status_status); \
+ } while (0)
+/**
+ If STATUS (of type enum_return_status) returns RETURN_STATUS_OK,
+ does nothing; otherwise, does a DBUG_PRINT and returns STATUS.
+*/
+#define PROPAGATE_ERROR(STATUS) \
+ __PROPAGATE_ERROR(STATUS, __propagate_error_status, true)
+/**
+ If STATUS (of type enum_return_status) returns RETURN_STATUS_OK,
+ does nothing; otherwise asserts that STATUS ==
+ RETURN_STATUS_REPORTED_ERROR, does a DBUG_PRINT, and returns STATUS.
+*/
+#define PROPAGATE_REPORTED_ERROR(STATUS) \
+ __PROPAGATE_ERROR(STATUS, __propagate_error_status, false)
+/**
+ If STATUS (of type enum_return_status) returns RETURN_STATUS_OK,
+ does nothing; otherwise asserts that STATUS ==
+ RETURN_STATUS_REPORTED_ERROR, does a DBUG_PRINT, and returns 1.
+*/
+#define PROPAGATE_REPORTED_ERROR_INT(STATUS) __PROPAGATE_ERROR(STATUS, 1, false)
+/**
+ If STATUS returns something else than RETURN_STATUS_OK, does a
+ DBUG_PRINT. Then, returns STATUS.
+*/
+#define RETURN_STATUS(STATUS) __RETURN_STATUS(STATUS, true)
+/**
+ Asserts that STATUS is not RETURN_STATUS_UNREPORTED_ERROR. Then, if
+ STATUS is RETURN_STATUS_REPORTED_ERROR, does a DBUG_PRINT. Then,
+ returns STATUS.
+*/
+#define RETURN_REPORTED_STATUS(STATUS) __RETURN_STATUS(STATUS, false)
+/// Returns RETURN_STATUS_OK.
+#define RETURN_OK DBUG_RETURN(RETURN_STATUS_OK)
+/// Does a DBUG_PRINT and returns RETURN_STATUS_REPORTED_ERROR.
+#define RETURN_REPORTED_ERROR RETURN_STATUS(RETURN_STATUS_REPORTED_ERROR)
+/// Does a DBUG_PRINT and returns RETURN_STATUS_UNREPORTED_ERROR.
+#define RETURN_UNREPORTED_ERROR RETURN_STATUS(RETURN_STATUS_UNREPORTED_ERROR)
+
+/**
+ enum to map the result of Uuid::parse to the above Macros
+*/
+inline enum_return_status map_macro_enum(int status) {
+ DBUG_ENTER("map status error with the return value of uuid::parse_method");
+ if (status == 0)
+ RETURN_OK;
+ else
+ RETURN_UNREPORTED_ERROR;
+}
+
+/// Possible values for @@GLOBAL.GTID_MODE.
+enum enum_gtid_mode {
+ /**
+ New transactions are anonymous. Replicated transactions must be
+ anonymous; replicated GTID-transactions generate an error.
+ */
+ GTID_MODE_OFF = 0,
+ DEFAULT_GTID_MODE = GTID_MODE_OFF,
+ /**
+ New transactions are anonyomus. Replicated transactions can be
+ either anonymous or GTID-transactions.
+ */
+ GTID_MODE_OFF_PERMISSIVE = 1,
+ /**
+ New transactions are GTID-transactions. Replicated transactions
+ can be either anonymous or GTID-transactions.
+ */
+ GTID_MODE_ON_PERMISSIVE = 2,
+ /**
+ New transactions are GTID-transactions. Replicated transactions
+ must be GTID-transactions; replicated anonymous transactions
+ generate an error.
+ */
+ GTID_MODE_ON = 3
+};
+
+/**
+ The gtid_mode.
+
+ Please do not access this directly - use the getters and setters
+ defined below.
+
+ It is ulong rather than enum_gtid_mode because of how sys_vars are
+ updated.
+*/
+extern ulong _gtid_mode;
+/**
+ Strings holding the enumeration values for gtid_mode. Use
+ get_gtid_mode_string instead of accessing this directly.
+*/
+extern const char *gtid_mode_names[];
+/**
+ 'Typelib' for the mode names. Use get_gtid_mode_string instead
+ of accessing this directly.
+*/
+extern TYPELIB gtid_mode_typelib;
+
+/**
+ Return the given string GTID_MODE as an enumeration value.
+
+ @param string The string to decode.
+
+ @param[out] error If the string does not represent a valid
+ GTID_MODE, this is set to true, otherwise it is left untouched.
+
+ @return The GTID_MODE.
+*/
+inline enum_gtid_mode get_gtid_mode(const char *string, bool *error) {
+ int ret = find_type(string, &gtid_mode_typelib, 1);
+ if (ret == 0) {
+ *error = true;
+ return GTID_MODE_OFF;
+ } else
+ return (enum_gtid_mode)(ret - 1);
+}
+/// Return the given GTID_MODE as a string.
+inline const char *get_gtid_mode_string(enum_gtid_mode gtid_mode_arg) {
+ return gtid_mode_names[gtid_mode_arg];
+}
+
+/**
+ Locks needed to access gtid_mode.
+
+ When writing, all these locks must be held (for the rwlocks, the
+ wrlock must be held).
+
+ When reading, one of them must be held (for the wrlocks, the rdlock
+ suffices).
+*/
+enum enum_gtid_mode_lock {
+ /// No lock held.
+ GTID_MODE_LOCK_NONE,
+ /// The specific gtid_mode_lock is held.
+ GTID_MODE_LOCK_GTID_MODE,
+ /// global_sid_lock held.
+ GTID_MODE_LOCK_SID,
+ /// read or write lock on channel_map lock is held.
+ GTID_MODE_LOCK_CHANNEL_MAP
+ /*
+ Currently, no function that calls get_gtid_mode needs
+ this. Uncomment this, and uncomment the case in get_gtid_mode, if it
+ is ever needed.
+
+ /// mysql_bin_log.get_log_lock() held.
+ GTID_MODE_LOCK_LOG
+ */
+};
+/**
+ Return the current GTID_MODE as an enumeration value.
+
+ This variable can be read while holding any one of the locks
+ enumerated in enum_gtid_mode_lock (see above).
+
+ When the variable is updated by a SET GTID_MODE statement, all these
+ locks will be taken (the wrlock on global_sid_map).
+
+ To avoid the mistake of reading the GTID_MODE with no lock, the
+ caller has to pass the lock type as a parameter. The function will
+ assert that the corresponding lock is held. If no lock is held, it
+ will acquire and release global_sid_lock.rdlock.
+
+ @param have_lock The lock type held by the caller.
+*/
+enum_gtid_mode get_gtid_mode(enum_gtid_mode_lock have_lock);
+
+#ifndef DBUG_OFF
+/**
+ Return the current GTID_MODE as a string. Used only for debugging.
+
+ @param have_lock Pass this parameter to get_gtid_mode(bool).
+*/
+inline const char *get_gtid_mode_string(enum_gtid_mode_lock have_lock) {
+ return get_gtid_mode_string(get_gtid_mode(have_lock));
+}
+#endif // ifndef DBUG_OFF
+
+/**
+ Possible values for ENFORCE_GTID_CONSISTENCY.
+*/
+enum enum_gtid_consistency_mode {
+ GTID_CONSISTENCY_MODE_OFF = 0,
+ GTID_CONSISTENCY_MODE_ON = 1,
+ GTID_CONSISTENCY_MODE_WARN = 2
+};
+/**
+ Strings holding the enumeration values for
+ gtid_consistency_mode_names. Use get_gtid_consistency_mode_string
+ instead of accessing this directly.
+*/
+extern const char *gtid_consistency_mode_names[];
+/**
+ Current value for ENFORCE_GTID_CONSISTENCY.
+ Don't use this directly; use get_gtid_consistency_mode.
+*/
+extern ulong _gtid_consistency_mode;
+/**
+ Return the current value of ENFORCE_GTID_CONSISTENCY.
+
+ Caller must hold global_sid_lock.rdlock.
+*/
+enum_gtid_consistency_mode get_gtid_consistency_mode();
+/// Return the given GTID_CONSISTENCY_MODE as a string.
+inline const char *get_gtid_consistency_mode_string(
+ enum_gtid_consistency_mode mode) {
+ return gtid_consistency_mode_names[(int)mode];
+}
+/**
+ Return the current value of ENFORCE_GTID_CONSISTENCY as a string.
+
+ Caller must hold global_sid_lock.rdlock.
+*/
+inline const char *get_gtid_consistency_mode_string() {
+ return get_gtid_consistency_mode_string(get_gtid_consistency_mode());
+}
+
+/// The maximum value of GNO
+const rpl_gno MAX_GNO = LLONG_MAX;
+/// The length of MAX_GNO when printed in decimal.
+const int MAX_GNO_TEXT_LENGTH = 19;
+/// The maximal possible length of thread_id when printed in decimal.
+const int MAX_THREAD_ID_TEXT_LENGTH = 19;
+
+/**
+ Parse a GNO from a string.
+
+ @param s Pointer to the string. *s will advance to the end of the
+ parsed GNO, if a correct GNO is found.
+ @retval GNO if a correct GNO (i.e., 0 or positive number) was found.
+ @retval -1 otherwise.
+*/
+rpl_gno parse_gno(const char **s);
+/**
+ Formats a GNO as a string.
+
+ @param s The buffer.
+ @param gno The GNO.
+ @return Length of the generated string.
+*/
+int format_gno(char *s, rpl_gno gno);
+
+typedef binary_log::Uuid rpl_sid;
+
+/**
+ This has the functionality of mysql_rwlock_t, with two differences:
+ 1. It has additional operations to check if the read and/or write lock
+ is held at the moment.
+ 2. It is wrapped in an object-oriented interface.
+
+ Note that the assertions do not check whether *this* thread has
+ taken the lock (that would be more complicated as it would require a
+ dynamic data structure). Luckily, it is still likely that the
+ assertions find bugs where a thread forgot to take a lock, because
+ most of the time most locks are only used by one thread at a time.
+
+ The assertions are no-ops when DBUG is off.
+*/
+class Checkable_rwlock {
+ public:
+ /// Initialize this Checkable_rwlock.
+ Checkable_rwlock(
+#if defined(HAVE_PSI_INTERFACE)
+ PSI_rwlock_key psi_key MY_ATTRIBUTE((unused)) = 0
+#endif
+ ) {
+#ifndef DBUG_OFF
+ lock_state.store(0);
+ dbug_trace = true;
+#else
+ is_write_lock = false;
+#endif
+#if defined(HAVE_PSI_INTERFACE)
+ mysql_rwlock_init(psi_key, &rwlock);
+#else
+ mysql_rwlock_init(0, &rwlock);
+#endif
+ }
+ /// Destroy this Checkable_lock.
+ ~Checkable_rwlock() { mysql_rwlock_destroy(&rwlock); }
+
+ /// Acquire the read lock.
+ inline void rdlock() {
+ mysql_rwlock_rdlock(&rwlock);
+ assert_no_wrlock();
+#ifndef DBUG_OFF
+ if (dbug_trace) DBUG_PRINT("info", ("%p.rdlock()", this));
+ ++lock_state;
+#endif
+ }
+ /// Acquire the write lock.
+ inline void wrlock() {
+ mysql_rwlock_wrlock(&rwlock);
+ assert_no_lock();
+#ifndef DBUG_OFF
+ if (dbug_trace) DBUG_PRINT("info", ("%p.wrlock()", this));
+ lock_state.store(-1);
+#else
+ is_write_lock = true;
+#endif
+ }
+ /// Release the lock (whether it is a write or read lock).
+ inline void unlock() {
+ assert_some_lock();
+#ifndef DBUG_OFF
+ if (dbug_trace) DBUG_PRINT("info", ("%p.unlock()", this));
+ int val = lock_state.load();
+ if (val > 0)
+ --lock_state;
+ else if (val == -1)
+ lock_state.store(0);
+ else
+ DBUG_ASSERT(0);
+#else
+ is_write_lock = false;
+#endif
+ mysql_rwlock_unlock(&rwlock);
+ }
+ /**
+ Return true if the write lock is held. Must only be called by
+ threads that hold a lock.
+ */
+ inline bool is_wrlock() {
+ assert_some_lock();
+#ifndef DBUG_OFF
+ return get_state() == -1;
+#else
+ return is_write_lock;
+#endif
+ }
+
+ /**
+ Return 0 if the write lock is held, otherwise an error will be returned.
+ */
+ inline int trywrlock() {
+ int ret = mysql_rwlock_trywrlock(&rwlock);
+
+ if (ret == 0) {
+ assert_no_lock();
+#ifndef DBUG_OFF
+ if (dbug_trace) DBUG_PRINT("info", ("%p.wrlock()", this));
+ lock_state.store(-1);
+#else
+ is_write_lock = true;
+#endif
+ }
+
+ return ret;
+ }
+
+ /// Assert that some thread holds either the read or the write lock.
+ inline void assert_some_lock() const { DBUG_ASSERT(get_state() != 0); }
+ /// Assert that some thread holds the read lock.
+ inline void assert_some_rdlock() const { DBUG_ASSERT(get_state() > 0); }
+ /// Assert that some thread holds the write lock.
+ inline void assert_some_wrlock() const { DBUG_ASSERT(get_state() == -1); }
+ /// Assert that no thread holds the write lock.
+ inline void assert_no_wrlock() const { DBUG_ASSERT(get_state() >= 0); }
+ /// Assert that no thread holds the read lock.
+ inline void assert_no_rdlock() const { DBUG_ASSERT(get_state() <= 0); }
+ /// Assert that no thread holds read or write lock.
+ inline void assert_no_lock() const { DBUG_ASSERT(get_state() == 0); }
+
+#ifndef DBUG_OFF
+
+ /// If enabled, print any lock/unlock operations to the DBUG trace.
+ bool dbug_trace;
+
+ private:
+ /**
+ The state of the lock:
+ 0 - not locked
+ -1 - write locked
+ >0 - read locked by that many threads
+ */
+ std::atomic<int32> lock_state;
+ /// Read lock_state atomically and return the value.
+ inline int32 get_state() const { return lock_state.load(); }
+
+#else
+
+ private:
+ bool is_write_lock;
+
+#endif
+ /// The rwlock.
+ mysql_rwlock_t rwlock;
+};
+
+/// Protects Gtid_state. See comment above gtid_state for details.
+extern Checkable_rwlock *global_sid_lock;
+
+/// One of the locks that protects GTID_MODE. See
+/// get_gtid_mode(enum_gtid_mode_lock).
+extern Checkable_rwlock *gtid_mode_lock;
+
+/**
+ Represents a bidirectional map between SID and SIDNO.
+
+ SIDNOs are always numbers greater or equal to 1.
+
+ This data structure OPTIONALLY knows of a read-write lock that
+ protects the number of SIDNOs. The lock is provided by the invoker
+ of the constructor and it is generally the caller's responsibility
+ to acquire the read lock. If the lock is not NULL, access methods
+ assert that the caller already holds the read (or write) lock. If
+ the lock is not NULL and a method of this class grows the number of
+ SIDNOs, then the method temporarily upgrades this lock to a write
+ lock and then degrades it to a read lock again; there will be a
+ short period when the lock is not held at all.
+*/
+class Sid_map {
+ public:
+ /**
+ Create this Sid_map.
+
+ @param sid_lock Read-write lock that protects updates to the
+ number of SIDNOs.
+ */
+ Sid_map(Checkable_rwlock *sid_lock);
+ /// Destroy this Sid_map.
+ ~Sid_map();
+ /**
+ Clears this Sid_map (for RESET SLAVE)
+
+ @return RETURN_STATUS_OK or RETURN_STAUTS_REPORTED_ERROR
+ */
+ enum_return_status clear();
+ /**
+ Add the given SID to this map if it does not already exist.
+
+ The caller must hold the read lock or write lock on sid_lock
+ before invoking this function. If the SID does not exist in this
+ map, it will release the read lock, take a write lock, update the
+ map, release the write lock, and take the read lock again.
+
+ @param sid The SID.
+ @retval SIDNO The SIDNO for the SID (a new SIDNO if the SID did
+ not exist, an existing if it did exist).
+ @retval negative Error. This function calls my_error.
+ */
+ rpl_sidno add_sid(const rpl_sid &sid);
+ /**
+ Get the SIDNO for a given SID
+
+ The caller must hold the read lock on sid_lock before invoking
+ this function.
+
+ @param sid The SID.
+ @retval SIDNO if the given SID exists in this map.
+ @retval 0 if the given SID does not exist in this map.
+ */
+ rpl_sidno sid_to_sidno(const rpl_sid &sid) const {
+ if (sid_lock != nullptr) sid_lock->assert_some_lock();
+ const auto it = _sid_to_sidno.find(sid);
+ if (it == _sid_to_sidno.end()) return 0;
+ return it->second->sidno;
+ }
+ /**
+ Get the SID for a given SIDNO.
+
+ Raises an assertion if the SIDNO is not valid.
+
+ If need_lock is true, acquires sid_lock->rdlock; otherwise asserts
+ that it is held already.
+
+ @param sidno The SIDNO.
+ @param need_lock If true, and sid_lock!=NULL, this function will
+ acquire sid_lock before looking up the sid, and then release
+ it. If false, and sid_lock!=NULL, this function will assert the
+ sid_lock is already held. If sid_lock==NULL, nothing is done
+ w.r.t. locking.
+ @retval NULL The SIDNO does not exist in this map.
+ @retval pointer Pointer to the SID. The data is shared with this
+ Sid_map, so should not be modified. It is safe to read the data
+ even after this Sid_map is modified, but not if this Sid_map is
+ destroyed.
+ */
+ const rpl_sid &sidno_to_sid(rpl_sidno sidno, bool need_lock = false) const {
+ if (sid_lock != nullptr) {
+ if (need_lock)
+ sid_lock->rdlock();
+ else
+ sid_lock->assert_some_lock();
+ }
+ DBUG_ASSERT(sidno >= 1 && sidno <= get_max_sidno());
+ const rpl_sid &ret = (_sidno_to_sid[sidno - 1])->sid;
+ if (sid_lock != nullptr && need_lock) sid_lock->unlock();
+ return ret;
+ }
+ /**
+ Return the n'th smallest sidno, in the order of the SID's UUID.
+
+ The caller must hold the read or write lock on sid_lock before
+ invoking this function.
+
+ @param n A number in the interval [0, get_max_sidno()-1], inclusively.
+ */
+ rpl_sidno get_sorted_sidno(rpl_sidno n) const {
+ if (sid_lock != nullptr) sid_lock->assert_some_lock();
+ return _sorted[n];
+ }
+ /**
+ Return the biggest sidno in this Sid_map.
+
+ The caller must hold the read or write lock on sid_lock before
+ invoking this function.
+ */
+ rpl_sidno get_max_sidno() const {
+ if (sid_lock != nullptr) sid_lock->assert_some_lock();
+ return static_cast<rpl_sidno>(_sidno_to_sid.size());
+ }
+
+ /// Return the sid_lock.
+ Checkable_rwlock *get_sid_lock() const { return sid_lock; }
+
+ /**
+ Deep copy this Sid_map to dest.
+
+ The caller must hold:
+ * the read lock on this sid_lock
+ * the write lock on the dest sid_lock
+ before invoking this function.
+
+ @param[out] dest The Sid_map to which the sids and sidnos will
+ be copied.
+ @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+ */
+ enum_return_status copy(Sid_map *dest);
+
+ private:
+ /// Node pointed to by both the hash and the array.
+ struct Node {
+ rpl_sidno sidno;
+ rpl_sid sid;
+ };
+
+ static const uchar *sid_map_get_key(const uchar *ptr, size_t *length) {
+ const Node *node = pointer_cast<const Node *>(ptr);
+ *length = binary_log::Uuid::BYTE_LENGTH;
+ return node->sid.bytes;
+ }
+
+ /**
+ Create a Node from the given SIDNO and SID and add it to
+ _sidno_to_sid, _sid_to_sidno, and _sorted.
+
+ The caller must hold the write lock on sid_lock before invoking
+ this function.
+
+ @param sidno The SIDNO to add.
+ @param sid The SID to add.
+ @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+ */
+ enum_return_status add_node(rpl_sidno sidno, const rpl_sid &sid);
+
+ /// Read-write lock that protects updates to the number of SIDNOs.
+ mutable Checkable_rwlock *sid_lock;
+
+ /**
+ Array that maps SIDNO to SID; the element at index N points to a
+ Node with SIDNO N-1.
+ */
+ Prealloced_array<Node *, 8> _sidno_to_sid;
+ /**
+ Hash that maps SID to SIDNO.
+ */
+ malloc_unordered_map<rpl_sid, unique_ptr_my_free<Node>, binary_log::Hash_Uuid>
+ _sid_to_sidno{key_memory_Sid_map_Node};
+ /**
+ Array that maps numbers in the interval [0, get_max_sidno()-1] to
+ SIDNOs, in order of increasing SID.
+
+ @see Sid_map::get_sorted_sidno.
+ */
+ Prealloced_array<rpl_sidno, 8> _sorted;
+};
+
+extern Sid_map *global_sid_map;
+
+/**
+ Represents a growable array where each element contains a mutex and
+ a condition variable.
+
+ Each element can be locked, unlocked, broadcast, or waited for, and
+ it is possible to call "THD::enter_cond" for the condition. The
+ allowed indexes range from 0, inclusive, to get_max_index(),
+ inclusive. Initially there are zero elements (and get_max_index()
+ returns -1); more elements can be allocated by calling
+ ensure_index().
+
+ This data structure has a read-write lock that protects the number
+ of elements. The lock is provided by the invoker of the constructor
+ and it is generally the caller's responsibility to acquire the read
+ lock. Access methods assert that the caller already holds the read
+ (or write) lock. If a method of this class grows the number of
+ elements, then the method temporarily upgrades this lock to a write
+ lock and then degrades it to a read lock again; there will be a
+ short period when the lock is not held at all.
+*/
+class Mutex_cond_array {
+ public:
+ /**
+ Create a new Mutex_cond_array.
+
+ @param global_lock Read-write lock that protects updates to the
+ number of elements.
+ */
+ Mutex_cond_array(Checkable_rwlock *global_lock);
+ /// Destroy this object.
+ ~Mutex_cond_array();
+ /// Lock the n'th mutex.
+ inline void lock(int n) const {
+ assert_not_owner(n);
+ mysql_mutex_lock(&get_mutex_cond(n)->mutex);
+ }
+ /// Unlock the n'th mutex.
+ inline void unlock(int n) const {
+ assert_owner(n);
+ mysql_mutex_unlock(&get_mutex_cond(n)->mutex);
+ }
+ /// Broadcast the n'th condition.
+ inline void broadcast(int n) const {
+ mysql_cond_broadcast(&get_mutex_cond(n)->cond);
+ }
+ /**
+ Assert that this thread owns the n'th mutex.
+ This is a no-op if DBUG_OFF is on.
+ */
+ inline void assert_owner(int n MY_ATTRIBUTE((unused))) const {
+#ifndef DBUG_OFF
+ mysql_mutex_assert_owner(&get_mutex_cond(n)->mutex);
+#endif
+ }
+ /**
+ Assert that this thread does not own the n'th mutex.
+ This is a no-op if DBUG_OFF is on.
+ */
+ inline void assert_not_owner(int n MY_ATTRIBUTE((unused))) const {
+#ifndef DBUG_OFF
+ mysql_mutex_assert_not_owner(&get_mutex_cond(n)->mutex);
+#endif
+ }
+
+ /**
+ Wait for signal on the n'th condition variable.
+
+ The caller must hold the read lock or write lock on sid_lock, as
+ well as the nth mutex lock, before invoking this function. The
+ sid_lock will be released, whereas the mutex will be released
+ during the wait and (atomically) re-acquired when the wait ends
+ or the timeout is reached.
+
+ @param[in] thd THD object for the calling thread.
+ @param[in] sidno Condition variable to wait for.
+ @param[in] abstime The absolute point in time when the wait times
+ out and stops, or NULL to wait indefinitely.
+
+ @retval false Success.
+ @retval true Failure: either timeout or thread was killed. If
+ thread was killed, the error has been generated.
+ */
+ inline bool wait(const THD *thd, int sidno, struct timespec *abstime) const {
+ DBUG_ENTER("Mutex_cond_array::wait");
+ int error = 0;
+ Mutex_cond *mutex_cond = get_mutex_cond(sidno);
+ global_lock->unlock();
+ mysql_mutex_assert_owner(&mutex_cond->mutex);
+ if (is_thd_killed(thd)) DBUG_RETURN(true);
+ if (abstime != nullptr)
+ error =
+ mysql_cond_timedwait(&mutex_cond->cond, &mutex_cond->mutex, abstime);
+ else
+ mysql_cond_wait(&mutex_cond->cond, &mutex_cond->mutex);
+ mysql_mutex_assert_owner(&mutex_cond->mutex);
+ DBUG_RETURN(is_timeout(error));
+ }
+#ifdef MYSQL_SERVER
+ /// Execute THD::enter_cond for the n'th condition variable.
+ void enter_cond(THD *thd, int n, PSI_stage_info *stage,
+ PSI_stage_info *old_stage) const;
+#endif // ifdef MYSQL_SERVER
+ /// Return the greatest addressable index in this Mutex_cond_array.
+ inline int get_max_index() const {
+ global_lock->assert_some_lock();
+ return static_cast<int>(m_array.size() - 1);
+ }
+ /**
+ Grows the array so that the given index fits.
+
+ If the array is grown, the global_lock is temporarily upgraded to
+ a write lock and then degraded again; there will be a
+ short period when the lock is not held at all.
+
+ @param n The index.
+ @return RETURN_OK or RETURN_REPORTED_ERROR
+ */
+ enum_return_status ensure_index(int n);
+
+ private:
+ /**
+ Return true if the given THD is killed.
+
+ @param[in] thd - The thread object
+ @retval true - thread is killed
+ false - thread not killed
+ */
+ bool is_thd_killed(const THD *thd) const;
+ /// A mutex/cond pair.
+ struct Mutex_cond {
+ mysql_mutex_t mutex;
+ mysql_cond_t cond;
+ };
+ /// Return the Nth Mutex_cond object
+ inline Mutex_cond *get_mutex_cond(int n) const {
+ global_lock->assert_some_lock();
+ DBUG_ASSERT(n <= get_max_index());
+ Mutex_cond *ret = m_array[n];
+ DBUG_ASSERT(ret);
+ return ret;
+ }
+ /// Read-write lock that protects updates to the number of elements.
+ mutable Checkable_rwlock *global_lock;
+ Prealloced_array<Mutex_cond *, 8> m_array;
+};
+
+/**
+ Holds information about a GTID interval: the sidno, the first gno
+ and the last gno of this interval.
+*/
+struct Gtid_interval {
+ /* SIDNO of this Gtid interval. */
+ rpl_sidno sidno;
+ /* The first GNO of this Gtid interval. */
+ rpl_gno gno_start;
+ /* The last GNO of this Gtid interval. */
+ rpl_gno gno_end;
+ void set(rpl_sidno sid_no, rpl_gno start, rpl_gno end) {
+ sidno = sid_no;
+ gno_start = start;
+ gno_end = end;
+ }
+};
+
+/**
+ TODO: Move this structure to libbinlogevents/include/control_events.h
+ when we start using C++11.
+ Holds information about a GTID: the sidno and the gno.
+
+ This is a POD. It has to be a POD because it is part of
+ Gtid_specification, which has to be a POD because it is used in
+ THD::variables.
+*/
+struct Gtid {
+ /// SIDNO of this Gtid.
+ rpl_sidno sidno;
+ /// GNO of this Gtid.
+ rpl_gno gno;
+
+ /// Set both components to 0.
+ void clear() {
+ sidno = 0;
+ gno = 0;
+ }
+ /// Set both components to the given, positive values.
+ void set(rpl_sidno sidno_arg, rpl_gno gno_arg) {
+ DBUG_ASSERT(sidno_arg > 0);
+ DBUG_ASSERT(gno_arg > 0);
+ sidno = sidno_arg;
+ gno = gno_arg;
+ }
+ /**
+ Return true if sidno is zero (and assert that gno is zero too in
+ this case).
+ */
+ bool is_empty() const {
+ // check that gno is not set inconsistently
+ if (sidno <= 0)
+ DBUG_ASSERT(gno == 0);
+ else
+ DBUG_ASSERT(gno > 0);
+ return sidno == 0;
+ }
+ /**
+ The maximal length of the textual representation of a SID, not
+ including the terminating '\0'.
+ */
+ static const int MAX_TEXT_LENGTH =
+ binary_log::Uuid::TEXT_LENGTH + 1 + MAX_GNO_TEXT_LENGTH;
+ /**
+ Return true if parse() would succeed, but don't store the
+ result anywhere.
+ */
+ static bool is_valid(const char *text);
+ /**
+ Convert a Gtid to a string.
+ @param sid the sid to use. This overrides the sidno of this Gtid.
+ @param[out] buf Buffer to store the Gtid in (normally
+ MAX_TEXT_LENGTH+1 bytes long).
+ @return Length of the string, not counting '\0'.
+ */
+ int to_string(const rpl_sid &sid, char *buf) const;
+ /**
+ Convert this Gtid to a string.
+ @param sid_map sid_map to use when converting sidno to a SID.
+ @param[out] buf Buffer to store the Gtid in (normally
+ MAX_TEXT_LENGTH+1 bytes long).
+ @param need_lock If true, the function will acquire sid_map->sid_lock;
+ otherwise it will assert that the lock is held.
+ @return Length of the string, not counting '\0'.
+ */
+ int to_string(const Sid_map *sid_map, char *buf,
+ bool need_lock = false) const;
+ /// Returns true if this Gtid has the same sid and gno as 'other'.
+ bool equals(const Gtid &other) const {
+ return sidno == other.sidno && gno == other.gno;
+ }
+ /**
+ Parses the given string and stores in this Gtid.
+
+ @param sid_map sid_map to use when converting SID to a sidno.
+ @param text The text to parse
+ @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+ */
+ enum_return_status parse(Sid_map *sid_map, const char *text);
+
+#ifndef DBUG_OFF
+ /// Debug only: print this Gtid to stdout.
+ void print(const Sid_map *sid_map) const {
+ char buf[MAX_TEXT_LENGTH + 1];
+ to_string(sid_map, buf);
+ printf("%s\n", buf);
+ }
+#endif
+ /// Print this Gtid to the trace file if debug is enabled; no-op otherwise.
+ void dbug_print(const Sid_map *sid_map MY_ATTRIBUTE((unused)),
+ const char *text MY_ATTRIBUTE((unused)) = "",
+ bool need_lock MY_ATTRIBUTE((unused)) = false) const {
+#ifndef DBUG_OFF
+ char buf[MAX_TEXT_LENGTH + 1];
+ to_string(sid_map, buf, need_lock);
+ DBUG_PRINT("info", ("%s%s%s", text, *text ? ": " : "", buf));
+#endif
+ }
+};
+
+/// Structure to store the GTID and timing information.
+struct Trx_monitoring_info {
+ /// GTID being monitored.
+ Gtid gtid;
+ /// OCT of the GTID being monitored.
+ ulonglong original_commit_timestamp;
+ /// ICT of the GTID being monitored.
+ ulonglong immediate_commit_timestamp;
+ /// When the GTID transaction started to be processed.
+ ulonglong start_time;
+ /// When the GTID transaction finished to be processed.
+ ulonglong end_time;
+ /// True if the GTID is being applied but will be skipped.
+ bool skipped;
+ /// True when this information contains useful data.
+ bool is_info_set;
+ /// Number of the last transient error of this transaction
+ uint last_transient_error_number;
+ /// Message of the last transient error of this transaction
+ char last_transient_error_message[MAX_SLAVE_ERRMSG];
+ /// Timestamp in microseconds of the last transient error of this transaction
+ ulonglong last_transient_error_timestamp;
+ /// Number of times this transaction was retried
+ ulong transaction_retries;
+ /// True when the transaction is retrying
+ bool is_retrying;
+
+ /// Constructor
+ Trx_monitoring_info();
+ /// Copy constructor
+ Trx_monitoring_info(const Trx_monitoring_info &info);
+
+ Trx_monitoring_info &operator=(const Trx_monitoring_info &) = default;
+
+ /// Clear all fields of the structure.
+ void clear();
+
+ /**
+ Copies this transaction monitoring information to the output parameters
+ passed as input, which are the corresponding fields in a replication
+ performance schema table.
+
+ @param[in] sid_map The SID map for the GTID.
+ @param[out] gtid_arg GTID field in the PS table.
+ @param[out] gtid_length_arg Length of the GTID as string.
+ @param[out] original_commit_ts_arg The original commit timestamp.
+ @param[out] immediate_commit_ts_arg The immediate commit timestamp.
+ @param[out] start_time_arg The start time field.
+ */
+ void copy_to_ps_table(Sid_map *sid_map, char *gtid_arg, uint *gtid_length_arg,
+ ulonglong *original_commit_ts_arg,
+ ulonglong *immediate_commit_ts_arg,
+ ulonglong *start_time_arg);
+
+ /**
+ Copies this transaction monitoring information to the output parameters
+ passed as input, which are the corresponding fields in a replication
+ performance schema table.
+
+ @param[in] sid_map The SID map for the GTID.
+ @param[out] gtid_arg GTID field in the PS table.
+ @param[out] gtid_length_arg Length of the GTID as string.
+ @param[out] original_commit_ts_arg The original commit timestamp.
+ @param[out] immediate_commit_ts_arg The immediate commit timestamp.
+ @param[out] start_time_arg The start time field.
+ @param[out] end_time_arg The end time field. This can be null
+ when the PS table fields are for the
+ "still processing" information.
+ */
+ void copy_to_ps_table(Sid_map *sid_map, char *gtid_arg, uint *gtid_length_arg,
+ ulonglong *original_commit_ts_arg,
+ ulonglong *immediate_commit_ts_arg,
+ ulonglong *start_time_arg, ulonglong *end_time_arg);
+
+ /**
+ Copies this transaction monitoring information to the output parameters
+ passed as input, which are the corresponding fields in a replication
+ performance schema table.
+
+ @param[in] sid_map The SID map for the GTID.
+ @param[out] gtid_arg GTID field in the PS table.
+ @param[out] gtid_length_arg Length of the GTID as string.
+ @param[out] original_commit_ts_arg The original commit timestamp.
+ @param[out] immediate_commit_ts_arg The immediate commit timestamp.
+ @param[out] start_time_arg The start time field.
+ @param[out] last_transient_errno_arg The last transient error
+ number.
+ @param[out] last_transient_errmsg_arg The last transient error
+ message.
+ @param[out] last_transient_errmsg_length_arg Length of the last transient
+ error message.
+ @param[out] last_transient_timestamp_arg The last transient error
+ timestamp.
+ @param[out] retries_count_arg The total number of retries for
+ this transaction.
+ */
+ void copy_to_ps_table(
+ Sid_map *sid_map, char *gtid_arg, uint *gtid_length_arg,
+ ulonglong *original_commit_ts_arg, ulonglong *immediate_commit_ts_arg,
+ ulonglong *start_time_arg, uint *last_transient_errno_arg,
+ char *last_transient_errmsg_arg, uint *last_transient_errmsg_length_arg,
+ ulonglong *last_transient_timestamp_arg, ulong *retries_count_arg);
+
+ /**
+ Copies this transaction monitoring information to the output parameters
+ passed as input, which are the corresponding fields in a replication
+ performance schema table.
+
+ @param[in] sid_map The SID map for the GTID.
+ @param[out] gtid_arg GTID field in the PS table.
+ @param[out] gtid_length_arg Length of the GTID as string.
+ @param[out] original_commit_ts_arg The original commit timestamp.
+ @param[out] immediate_commit_ts_arg The immediate commit timestamp.
+ @param[out] start_time_arg The start time field.
+ @param[out] end_time_arg The end time field. This can be
+ null when the PS table fields
+ are for the "still processing"
+ information.
+ @param[out] last_transient_errno_arg The last transient error
+ number.
+ @param[out] last_transient_errmsg_arg The last transient error
+ message.
+ @param[out] last_transient_errmsg_length_arg Length of the last transient
+ error message.
+ @param[out] last_transient_timestamp_arg The last transient error
+ timestamp.
+ @param[out] retries_count_arg The total number of retries for
+ this transaction.
+ */
+ void copy_to_ps_table(Sid_map *sid_map, char *gtid_arg, uint *gtid_length_arg,
+ ulonglong *original_commit_ts_arg,
+ ulonglong *immediate_commit_ts_arg,
+ ulonglong *start_time_arg, ulonglong *end_time_arg,
+ uint *last_transient_errno_arg,
+ char *last_transient_errmsg_arg,
+ uint *last_transient_errmsg_length_arg,
+ ulonglong *last_transient_timestamp_arg,
+ ulong *retries_count_arg);
+};
+
+/**
+ Stores information to monitor a transaction during the different replication
+ stages.
+*/
+class Gtid_monitoring_info {
+ public:
+ /**
+ Create this GTID monitoring info object.
+
+ @param atomic_mutex_arg When specified, this object will rely on the mutex
+ to arbitrate the read/update access to object data.
+ This will be used by the receiver thread, relying
+ on mi->data_lock. When no mutex is specified, the
+ object will rely on its own atomic mechanism.
+ */
+ Gtid_monitoring_info(mysql_mutex_t *atomic_mutex_arg = nullptr);
+
+ /// Destroy this GTID monitoring info object.
+ ~Gtid_monitoring_info();
+
+ protected:
+ /// Holds information about transaction being processed.
+ Trx_monitoring_info *processing_trx;
+ /// Holds information about the last processed transaction.
+ Trx_monitoring_info *last_processed_trx;
+
+ private:
+ /**
+ Mutex arbitrating the atomic access to the object.
+
+ Some Gtid_monitoring_info will rely on replication thread locks
+ (i.e.: the Master_info's one rely on mi->data_lock, that is already
+ acquired every time the Gtid_monitoring_info needs to be updated).
+
+ Other Gtid_monitoring_info will rely on an atomic lock implemented
+ in this class to avoid overlapped reads and writes over the information.
+ (i.e.: the Relay_log_info's one sometimes is updated without rli locks).
+
+ When atomic_mutex is NULL, the object will rely on its own atomic
+ mechanism.
+ */
+ mysql_mutex_t *atomic_mutex;
+
+ /// The atomic locked flag.
+ std::atomic<bool> atomic_locked{false};
+#ifndef DBUG_OFF
+ /// Flag to assert the atomic lock behavior.
+ bool is_locked = false;
+#endif
+
+ public:
+ /**
+ Lock this object when no thread mutex is used to arbitrate the access.
+ */
+ void atomic_lock();
+ /**
+ Unlock this object when no thread mutex is used to arbitrate the access.
+ */
+ void atomic_unlock();
+ /**
+ Clear all monitoring information.
+ */
+ void clear();
+ /**
+ Clear only the processing_trx monitoring info.
+ */
+ void clear_processing_trx();
+ /**
+ Clear only the last_processed_trx monitoring info.
+ */
+ void clear_last_processed_trx();
+ /**
+ Sets the initial monitoring information.
+
+ @param gtid_arg The Gtid to be stored.
+ @param original_ts_arg The original commit timestamp of the GTID.
+ @param immediate_ts_arg The immediate commit timestamp of the GTID.
+ @param skipped_arg True if the GTID was already applied.
+ This only make sense for applier threads.
+ That's why it is false by default.
+ */
+ void start(Gtid gtid_arg, ulonglong original_ts_arg,
+ ulonglong immediate_ts_arg, bool skipped_arg = false);
+
+ /**
+ Sets the final information, copy processing info to last_processed
+ and clears processing info.
+ */
+ void finish();
+ /**
+ Copies both processing_trx and last_processed_trx info to other
+ Trx_monitoring_info structures.
+
+ @param[out] processing_dest The destination of processing_trx.
+ @param[out] last_processed_dest The destination of last_processed_trx.
+ */
+ void copy_info_to(Trx_monitoring_info *processing_dest,
+ Trx_monitoring_info *last_processed_dest);
+ /**
+ Copies all monitoring info to other Gtid_monitoring_info object.
+
+ @param[out] dest The destination Gtid_monitoring_info.
+ */
+ void copy_info_to(Gtid_monitoring_info *dest);
+ /// Returns true if the processing_trx is set, false otherwise.
+ bool is_processing_trx_set();
+ /// Returns the GTID of the processing_trx.
+ const Gtid *get_processing_trx_gtid();
+ /**
+ Stores the information about the last transient error in the current
+ transaction, namely: the error number, message and total number of retries.
+ It also sets the timestamp for this error.
+
+ @param transient_errno_arg The number of the transient error in this
+ transaction.
+ @param transient_err_message_arg The message of this transient error.
+ @param trans_retries_arg The number of times this transaction has
+ been retried.
+ */
+ void store_transient_error(uint transient_errno_arg,
+ const char *transient_err_message_arg,
+ ulong trans_retries_arg);
+};
+
+/**
+ Represents a set of GTIDs.
+
+ This is structured as an array, indexed by SIDNO, where each element
+ contains a linked list of intervals.
+
+ This data structure OPTIONALLY knows of a Sid_map that gives a
+ correspondence between SIDNO and SID. If the Sid_map is NULL, then
+ operations that require a Sid_map - printing and parsing - raise an
+ assertion.
+
+ This data structure OPTIONALLY knows of a read-write lock that
+ protects the number of SIDNOs. The lock is provided by the invoker
+ of the constructor and it is generally the caller's responsibility
+ to acquire the read lock. If the lock is not NULL, access methods
+ assert that the caller already holds the read (or write) lock. If
+ the lock is not NULL and a method of this class grows the number of
+ SIDNOs, then the method temporarily upgrades this lock to a write
+ lock and then degrades it to a read lock again; there will be a
+ short period when the lock is not held at all.
+*/
+class Gtid_set {
+ public:
+ static PSI_mutex_key key_gtid_executed_free_intervals_mutex;
+ /**
+ Constructs a new, empty Gtid_set.
+
+ @param sid_map The Sid_map to use, or NULL if this Gtid_set
+ should not have a Sid_map.
+ @param sid_lock Read-write lock that protects updates to the
+ number of SIDs. This may be NULL if such changes do not need to be
+ protected.
+ */
+ Gtid_set(Sid_map *sid_map, Checkable_rwlock *sid_lock = nullptr);
+ /**
+ Constructs a new Gtid_set that contains the gtids in the given
+ string, in the same format as add_gtid_text(char *).
+
+ @param sid_map The Sid_map to use for SIDs.
+ @param text The text to parse.
+ @param status Will be set to RETURN_STATUS_OK on success or
+ RETURN_STATUS_REPORTED_ERROR on error.
+ @param sid_lock Read/write lock to protect changes in the number
+ of SIDs with. This may be NULL if such changes do not need to be
+ protected.
+ If sid_lock != NULL, then the read lock on sid_lock must be held
+ before calling this function. If the array is grown, sid_lock is
+ temporarily upgraded to a write lock and then degraded again;
+ there will be a short period when the lock is not held at all.
+ */
+ Gtid_set(Sid_map *sid_map, const char *text, enum_return_status *status,
+ Checkable_rwlock *sid_lock = nullptr);
+
+ private:
+ /// Worker for the constructor.
+ void init();
+
+ public:
+ /// Destroy this Gtid_set.
+ ~Gtid_set();
+ /**
+ Removes all gtids from this Gtid_set.
+
+ This does not deallocate anything: if gtids are added later,
+ existing allocated memory will be re-used.
+ */
+ void clear();
+ /**
+ Removes all gtids from this Gtid_set and clear all the sidnos
+ used by the Gtid_set and it's SID map.
+
+ This does not deallocate anything: if gtids are added later,
+ existing allocated memory will be re-used.
+ */
+ void clear_set_and_sid_map();
+ /**
+ Adds the given GTID to this Gtid_set.
+
+ The SIDNO must exist in the Gtid_set before this function is called.
+
+ @param sidno SIDNO of the GTID to add.
+ @param gno GNO of the GTID to add.
+ */
+ void _add_gtid(rpl_sidno sidno, rpl_gno gno) {
+ DBUG_ENTER("Gtid_set::_add_gtid(sidno, gno)");
+ Interval_iterator ivit(this, sidno);
+ Free_intervals_lock lock(this);
+ add_gno_interval(&ivit, gno, gno + 1, &lock);
+ DBUG_VOID_RETURN;
+ }
+ /**
+ Removes the given GTID from this Gtid_set.
+
+ @param sidno SIDNO of the GTID to remove.
+ @param gno GNO of the GTID to remove.
+ */
+ void _remove_gtid(rpl_sidno sidno, rpl_gno gno) {
+ DBUG_ENTER("Gtid_set::_remove_gtid(rpl_sidno, rpl_gno)");
+ if (sidno <= get_max_sidno()) {
+ Interval_iterator ivit(this, sidno);
+ Free_intervals_lock lock(this);
+ remove_gno_interval(&ivit, gno, gno + 1, &lock);
+ }
+ DBUG_VOID_RETURN;
+ }
+ /**
+ Adds the given GTID to this Gtid_set.
+
+ The SIDNO must exist in the Gtid_set before this function is called.
+
+ @param gtid Gtid to add.
+ */
+ void _add_gtid(const Gtid &gtid) { _add_gtid(gtid.sidno, gtid.gno); }
+ /**
+ Removes the given GTID from this Gtid_set.
+
+ @param gtid Gtid to remove.
+ */
+ void _remove_gtid(const Gtid &gtid) { _remove_gtid(gtid.sidno, gtid.gno); }
+ /**
+ Adds all gtids from the given Gtid_set to this Gtid_set.
+
+ If sid_lock != NULL, then the read lock must be held before
+ calling this function. If a new sidno is added so that the array
+ of lists of intervals is grown, sid_lock is temporarily upgraded
+ to a write lock and then degraded again; there will be a short
+ period when the lock is not held at all.
+
+ @param other The Gtid_set to add.
+ @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+ */
+ enum_return_status add_gtid_set(const Gtid_set *other);
+ /**
+ Removes all gtids in the given Gtid_set from this Gtid_set.
+
+ @param other The Gtid_set to remove.
+ */
+ void remove_gtid_set(const Gtid_set *other);
+ /**
+ Removes all intervals of 'other' for a given SIDNO, from 'this'.
+
+ Example:
+ this = A:1-100, B:1-100
+ other = A:1-100, B:1-50, C:1-100
+ this.remove_intervals_for_sidno(other, B) = A:1-100, B:51-100
+
+ It is not required that the intervals exist in this Gtid_set.
+
+ @param other The set to remove.
+ @param sidno The sidno to remove.
+ */
+ void remove_intervals_for_sidno(Gtid_set *other, rpl_sidno sidno);
+ /**
+ Adds the set of GTIDs represented by the given string to this Gtid_set.
+
+ The string must have the format of a comma-separated list of zero
+ or more of the following items:
+
+ XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX(:NUMBER+(-NUMBER)?)*
+ | ANONYMOUS
+
+ Each X is a hexadecimal digit (upper- or lowercase).
+ NUMBER is a decimal, 0xhex, or 0oct number.
+
+ The start of an interval must be greater than 0. The end of an
+ interval may be 0, but any interval that has an endpoint that
+ is smaller than the start is discarded.
+
+ The string can start with an optional '+' appender qualifier
+ which triggers @c executed_gtids and @c lost_gtids set examination
+ on the matter of disjointness with the one being added.
+
+ If sid_lock != NULL, then the read lock on sid_lock must be held
+ before calling this function. If a new sidno is added so that the
+ array of lists of intervals is grown, sid_lock is temporarily
+ upgraded to a write lock and then degraded again; there will be a
+ short period when the lock is not held at all.
+
+ @param text The string to parse.
+ @param [in,out] anonymous If this is NULL, ANONYMOUS is not
+ allowed. If this is not NULL, it will be set to true if the
+ anonymous GTID was found; false otherwise.
+ @param[in,out] starts_with_plus If this is not NULL, the string may
+ optionally begin with a '+' character, and *starts_with_plus will
+ be set to true if the plus character is present. If this is NULL,
+ no plus is allowed at the begin of the string.
+
+ @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+ */
+ enum_return_status add_gtid_text(const char *text, bool *anonymous = nullptr,
+ bool *starts_with_plus = nullptr);
+ /**
+ Decodes a Gtid_set from the given string.
+
+ @param encoded The string to parse.
+ @param length The number of bytes.
+ @param actual_length If this is not NULL, it is set to the number
+ of bytes used by the encoding (which may be less than 'length').
+ If this is NULL, an error is generated if the encoding is shorter
+ than the given 'length'.
+ @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+ */
+ enum_return_status add_gtid_encoding(const uchar *encoded, size_t length,
+ size_t *actual_length = nullptr);
+ /// Return true iff the given GTID exists in this set.
+ bool contains_gtid(rpl_sidno sidno, rpl_gno gno) const;
+ /// Return true iff the given GTID exists in this set.
+ bool contains_gtid(const Gtid &gtid) const {
+ return contains_gtid(gtid.sidno, gtid.gno);
+ }
+ // Get last gno or 0 if this set is empty.
+ rpl_gno get_last_gno(rpl_sidno sidno) const;
+ /// Returns the maximal sidno that this Gtid_set currently has space for.
+ rpl_sidno get_max_sidno() const {
+ if (sid_lock) sid_lock->assert_some_lock();
+ return static_cast<rpl_sidno>(m_intervals.size());
+ }
+ /**
+ Allocates space for all sidnos up to the given sidno in the array of
+ intervals. The sidno must exist in the Sid_map associated with this
+ Gtid_set.
+
+ If sid_lock != NULL, then the read lock on sid_lock must be held
+ before calling this function. If the array is grown, sid_lock is
+ temporarily upgraded to a write lock and then degraded again;
+ there will be a short period when the lock is not held at all.
+
+ @param sidno The SIDNO.
+ @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+ */
+ enum_return_status ensure_sidno(rpl_sidno sidno);
+ /// Returns true if this Gtid_set is a subset of the other Gtid_set.
+ bool is_subset(const Gtid_set *super) const;
+ /// Returns true if this Gtid_set is a non equal subset of the other Gtid_set.
+ bool is_subset_not_equals(const Gtid_set *super) const {
+ return (is_subset(super) && !equals(super));
+ }
+
+ /**
+ Returns true if this Gtid_set is a subset of the given gtid_set
+ on the given superset_sidno and subset_sidno.
+
+ @param super Gtid_set with which this->gtid_set needs to be
+ compared
+ @param superset_sidno The sidno that will be compared, relative to
+ super->sid_map.
+ @param subset_sidno The sidno that will be compared, relative to
+ this->sid_map.
+ @return true If 'this' Gtid_set is subset of given
+ 'super' Gtid_set.
+ false If 'this' Gtid_set is *not* subset of given
+ 'super' Gtid_set.
+ */
+ bool is_subset_for_sid(const Gtid_set *super, rpl_sidno superset_sidno,
+ rpl_sidno subset_sidno) const;
+ /// Returns true if there is a least one element of this Gtid_set in
+ /// the other Gtid_set.
+ bool is_intersection_nonempty(const Gtid_set *other) const;
+ /**
+ Add the intersection of this Gtid_set and the other Gtid_set to result.
+
+ @param other The Gtid_set to intersect with this Gtid_set
+ @param result Gtid_set where the result will be stored.
+ @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+ */
+ enum_return_status intersection(const Gtid_set *other, Gtid_set *result);
+ /// Returns true if this Gtid_set is empty.
+ bool is_empty() const {
+ Gtid_iterator git(this);
+ return git.get().sidno == 0;
+ }
+
+ /**
+ What is the count of all the GTIDs in all intervals for a sidno
+
+ @param sidno The sidno that contains the intervals
+
+ @return the number of all GTIDs in all intervals
+ */
+ ulonglong get_interval_count(rpl_sidno sidno) const {
+ Const_interval_iterator ivit(this, sidno);
+ ulonglong ret = 0;
+ while (ivit.get() != nullptr) {
+ ret += ivit.get()->end - ivit.get()->start;
+ ivit.next();
+ }
+ return ret;
+ }
+
+ /**
+ What is the count of all the GTIDs for all sidno
+
+ @return the number of all GTIDs
+ */
+ ulonglong get_gtid_number() const {
+ if (sid_lock != nullptr) sid_lock->assert_some_wrlock();
+ rpl_sidno max_sidno = get_max_sidno();
+ ulonglong ret = 0;
+ for (rpl_sidno sidno = 1; sidno <= max_sidno; sidno++)
+ ret += get_interval_count(sidno);
+ return ret;
+ }
+
+ /**
+ Returns true if this Gtid_set contains at least one GTID with
+ the given SIDNO.
+
+ @param sidno The SIDNO to test.
+ @retval true The SIDNO is less than or equal to the max SIDNO, and
+ there is at least one GTID with this SIDNO.
+ @retval false The SIDNO is greater than the max SIDNO, or there is
+ no GTID with this SIDNO.
+ */
+ bool contains_sidno(rpl_sidno sidno) const {
+ DBUG_ASSERT(sidno >= 1);
+ if (sidno > get_max_sidno()) return false;
+ Const_interval_iterator ivit(this, sidno);
+ return ivit.get() != nullptr;
+ }
+ /**
+ Returns true if the given string is a valid specification of a
+ Gtid_set, false otherwise.
+ */
+ static bool is_valid(const char *text);
+
+ /**
+ Class Gtid_set::String_format defines the separators used by
+ Gtid_set::to_string.
+ */
+ struct String_format {
+ /// The generated string begins with this.
+ const char *begin;
+ /// The generated string begins with this.
+ const char *end;
+ /// In 'SID:GNO', this is the ':'
+ const char *sid_gno_separator;
+ /// In 'SID:GNO-GNO', this is the '-'
+ const char *gno_start_end_separator;
+ /// In 'SID:GNO:GNO', this is the second ':'
+ const char *gno_gno_separator;
+ /// In 'SID:GNO,SID:GNO', this is the ','
+ const char *gno_sid_separator;
+ /// If the set is empty and this is not NULL, then this string is generated.
+ const char *empty_set_string;
+ /// The following fields are the lengths of each field above.
+ const int begin_length;
+ const int end_length;
+ const int sid_gno_separator_length;
+ const int gno_start_end_separator_length;
+ const int gno_gno_separator_length;
+ const int gno_sid_separator_length;
+ const int empty_set_string_length;
+ };
+ /**
+ Returns the length of the output from to_string.
+
+ @warning This does not include the trailing '\0', so your buffer
+ needs space for get_string_length() + 1 characters.
+
+ @param string_format String_format object that specifies
+ separators in the resulting text.
+ @return The length.
+ */
+ size_t get_string_length(const String_format *string_format = nullptr) const;
+ /**
+ Formats this Gtid_set as a string and saves in a given buffer.
+
+ @param[out] buf Pointer to the buffer where the string should be
+ stored. This should have size at least get_string_length()+1.
+ @param need_lock If this Gtid_set has a sid_lock, then the write
+ lock must be held while generating the string. If this parameter
+ is true, then this function acquires and releases the lock;
+ otherwise it asserts that the caller holds the lock.
+ @param string_format String_format object that specifies
+ separators in the resulting text.
+ @return Length of the generated string.
+ */
+ size_t to_string(char *buf, bool need_lock = false,
+ const String_format *string_format = nullptr) const;
+
+ /**
+ Formats a Gtid_set as a string and saves in a newly allocated buffer.
+ @param[out] buf Pointer to pointer to string. The function will
+ set it to point to the newly allocated buffer, or NULL on out of memory.
+ @param need_lock If this Gtid_set has a sid_lock, then the write
+ lock must be held while generating the string. If this parameter
+ is true, then this function acquires and releases the lock;
+ otherwise it asserts that the caller holds the lock.
+ @param string_format Specifies how to format the string.
+ @retval Length of the generated string, or -1 on out of memory.
+ */
+ long to_string(char **buf, bool need_lock = false,
+ const String_format *string_format = nullptr) const;
+#ifndef DBUG_OFF
+ /// Debug only: Print this Gtid_set to stdout.
+
+ /// For use with C `printf`
+ void print(bool need_lock = false,
+ const Gtid_set::String_format *sf = nullptr) const {
+ char *str;
+ to_string(&str, need_lock, sf);
+ printf("%s\n", str ? str : "out of memory in Gtid_set::print");
+ my_free(str);
+ }
+
+ /// For use with C++ `std::ostream`
+ inline friend std::ostream &operator<<(std::ostream &os, const Gtid_set &in) {
+ char *str;
+ in.to_string(&str, true, nullptr);
+ os << std::string(str) << std::flush;
+ my_free(str);
+ return os;
+ }
+#endif
+ /**
+ Print this Gtid_set to the trace file if debug is enabled; no-op
+ otherwise.
+ */
+ void dbug_print(const char *text MY_ATTRIBUTE((unused)) = "",
+ bool need_lock MY_ATTRIBUTE((unused)) = false,
+ const Gtid_set::String_format *sf MY_ATTRIBUTE((unused)) =
+ nullptr) const {
+#ifndef DBUG_OFF
+ char *str;
+ to_string(&str, need_lock, sf);
+ DBUG_PRINT("info", ("%s%s'%s'", text, *text ? ": " : "",
+ str ? str : "out of memory in Gtid_set::dbug_print"));
+ my_free(str);
+#endif
+ }
+ /**
+ Gets all gtid intervals from this Gtid_set.
+
+ @param[out] gtid_intervals Store all gtid intervals from this Gtid_set.
+ */
+ void get_gtid_intervals(std::list<Gtid_interval> *gtid_intervals) const;
+ /**
+ The default String_format: the format understood by
+ add_gtid_text(const char *).
+ */
+ static const String_format default_string_format;
+ /**
+ String_format useful to generate an SQL string: the string is
+ wrapped in single quotes and there is a newline between SIDs.
+ */
+ static const String_format sql_string_format;
+ /**
+ String_format for printing the Gtid_set commented: the string is
+ not quote-wrapped, and every SID is on a new line with a leading '# '.
+ */
+ static const String_format commented_string_format;
+
+ /// Return the Sid_map associated with this Gtid_set.
+ Sid_map *get_sid_map() const { return sid_map; }
+
+ /**
+ Represents one element in the linked list of intervals associated
+ with a SIDNO.
+ */
+ struct Interval {
+ public:
+ /// The first GNO of this interval.
+ rpl_gno start;
+ /// The first GNO after this interval.
+ rpl_gno end;
+ /// Return true iff this interval is equal to the given interval.
+ bool equals(const Interval &other) const {
+ return start == other.start && end == other.end;
+ }
+ /// Pointer to next interval in list.
+ Interval *next;
+ };
+
+ /**
+ Provides an array of Intervals that this Gtid_set can use when
+ gtids are subsequently added. This can be used as an
+ optimization, to reduce allocation for sets that have a known
+ number of intervals.
+
+ @param n_intervals The number of intervals to add.
+ @param intervals_param Array of n_intervals intervals.
+ */
+ void add_interval_memory(int n_intervals, Interval *intervals_param) {
+ if (sid_lock != nullptr) mysql_mutex_lock(&free_intervals_mutex);
+ add_interval_memory_lock_taken(n_intervals, intervals_param);
+ if (sid_lock != nullptr) mysql_mutex_unlock(&free_intervals_mutex);
+ }
+
+ /**
+ Iterator over intervals for a given SIDNO.
+
+ This is an abstract template class, used as a common base class
+ for Const_interval_iterator and Interval_iterator.
+
+ The iterator always points to an interval pointer. The interval
+ pointer is either the initial pointer into the list, or the next
+ pointer of one of the intervals in the list.
+ */
+ template <typename Gtid_set_p, typename Interval_p>
+ class Interval_iterator_base {
+ public:
+ /**
+ Construct a new iterator over the GNO intervals for a given Gtid_set.
+
+ @param gtid_set The Gtid_set.
+ @param sidno The SIDNO.
+ */
+ Interval_iterator_base(Gtid_set_p gtid_set, rpl_sidno sidno) {
+ DBUG_ASSERT(sidno >= 1 && sidno <= gtid_set->get_max_sidno());
+ init(gtid_set, sidno);
+ }
+ /// Construct a new iterator over the free intervals of a Gtid_set.
+ Interval_iterator_base(Gtid_set_p gtid_set) {
+ p = const_cast<Interval_p *>(&gtid_set->free_intervals);
+ }
+ /// Reset this iterator.
+ inline void init(Gtid_set_p gtid_set, rpl_sidno sidno) {
+ p = const_cast<Interval_p *>(&gtid_set->m_intervals[sidno - 1]);
+ }
+ /// Advance current_elem one step.
+ inline void next() {
+ DBUG_ASSERT(*p != nullptr);
+ p = const_cast<Interval_p *>(&(*p)->next);
+ }
+ /// Return current_elem.
+ inline Interval_p get() const { return *p; }
+
+ protected:
+ /**
+ Holds the address of the 'next' pointer of the previous element,
+ or the address of the initial pointer into the list, if the
+ current element is the first element.
+ */
+ Interval_p *p;
+ };
+
+ /**
+ Iterator over intervals of a const Gtid_set.
+ */
+ class Const_interval_iterator
+ : public Interval_iterator_base<const Gtid_set *, const Interval *> {
+ public:
+ /// Create this Const_interval_iterator.
+ Const_interval_iterator(const Gtid_set *gtid_set, rpl_sidno sidno)
+ : Interval_iterator_base<const Gtid_set *, const Interval *>(gtid_set,
+ sidno) {}
+ /// Create this Const_interval_iterator.
+ Const_interval_iterator(const Gtid_set *gtid_set)
+ : Interval_iterator_base<const Gtid_set *, const Interval *>(gtid_set) {
+ }
+ };
+
+ /**
+ Iterator over intervals of a non-const Gtid_set, with additional
+ methods to modify the Gtid_set.
+ */
+ class Interval_iterator
+ : public Interval_iterator_base<Gtid_set *, Interval *> {
+ public:
+ /// Create this Interval_iterator.
+ Interval_iterator(Gtid_set *gtid_set, rpl_sidno sidno)
+ : Interval_iterator_base<Gtid_set *, Interval *>(gtid_set, sidno) {}
+ /// Destroy this Interval_iterator.
+ Interval_iterator(Gtid_set *gtid_set)
+ : Interval_iterator_base<Gtid_set *, Interval *>(gtid_set) {}
+
+ private:
+ /**
+ Set current_elem to the given Interval but do not touch the
+ next pointer of the given Interval.
+ */
+ inline void set(Interval *iv) { *p = iv; }
+ /// Insert the given element before current_elem.
+ inline void insert(Interval *iv) {
+ iv->next = *p;
+ set(iv);
+ }
+ /// Remove current_elem.
+ inline void remove(Gtid_set *gtid_set) {
+ DBUG_ASSERT(get() != nullptr);
+ Interval *next = (*p)->next;
+ gtid_set->put_free_interval(*p);
+ set(next);
+ }
+ /**
+ Only Gtid_set is allowed to use set/insert/remove.
+
+ They are not safe to use from other code because: (1) very easy
+ to make a mistakes (2) they don't clear cached_string_format or
+ cached_string_length.
+ */
+ friend class Gtid_set;
+ };
+
+ /**
+ Iterator over all gtids in a Gtid_set. This is a const
+ iterator; it does not allow modification of the Gtid_set.
+ */
+ class Gtid_iterator {
+ public:
+ Gtid_iterator(const Gtid_set *gs) : gtid_set(gs), sidno(0), ivit(gs) {
+ if (gs->sid_lock != nullptr) gs->sid_lock->assert_some_wrlock();
+ next_sidno();
+ }
+ /// Advance to next gtid.
+ inline void next() {
+ DBUG_ASSERT(gno > 0 && sidno > 0);
+ // go to next GTID in current interval
+ gno++;
+ // end of interval? then go to next interval for this sidno
+ if (gno == ivit.get()->end) {
+ ivit.next();
+ const Interval *iv = ivit.get();
+ // last interval for this sidno? then go to next sidno
+ if (iv == nullptr) {
+ next_sidno();
+ // last sidno? then don't try more
+ if (sidno == 0) return;
+ iv = ivit.get();
+ }
+ gno = iv->start;
+ }
+ }
+ /// Return next gtid, or {0,0} if we reached the end.
+ inline Gtid get() const {
+ Gtid ret = {sidno, gno};
+ return ret;
+ }
+
+ private:
+ /// Find the next sidno that has one or more intervals.
+ inline void next_sidno() {
+ const Interval *iv;
+ do {
+ sidno++;
+ if (sidno > gtid_set->get_max_sidno()) {
+ sidno = 0;
+ gno = 0;
+ return;
+ }
+ ivit.init(gtid_set, sidno);
+ iv = ivit.get();
+ } while (iv == nullptr);
+ gno = iv->start;
+ }
+ /// The Gtid_set we iterate over.
+ const Gtid_set *gtid_set;
+ /**
+ The SIDNO of the current element, or 0 if the iterator is past
+ the last element.
+ */
+ rpl_sidno sidno;
+ /**
+ The GNO of the current element, or 0 if the iterator is past the
+ last element.
+ */
+ rpl_gno gno;
+ /// Iterator over the intervals for the current SIDNO.
+ Const_interval_iterator ivit;
+ };
+
+ public:
+ /**
+ Encodes this Gtid_set as a binary string.
+ */
+ void encode(uchar *buf) const;
+ /**
+ Returns the length of this Gtid_set when encoded using the
+ encode() function.
+ */
+ size_t get_encoded_length() const;
+
+ private:
+ /**
+ Contains a list of intervals allocated by this Gtid_set. When a
+ method of this class needs a new interval and there are no more
+ free intervals, a new Interval_chunk is allocated and the
+ intervals of it are added to the list of free intervals.
+ */
+ struct Interval_chunk {
+ Interval_chunk *next;
+ Interval intervals[1];
+ };
+ /// The default number of intervals in an Interval_chunk.
+ static const int CHUNK_GROW_SIZE = 8;
+
+ /**
+ Return true if the given sidno of this Gtid_set contains the same
+ intervals as the given sidno of the other Gtid_set.
+
+ @param sidno SIDNO to check for this Gtid_set.
+ @param other Other Gtid_set
+ @param other_sidno SIDNO to check in other.
+ @return true if equal, false is not equal.
+ */
+ bool sidno_equals(rpl_sidno sidno, const Gtid_set *other,
+ rpl_sidno other_sidno) const;
+ /// Returns true if this Gtid_set is equal to the other Gtid_set.
+ bool equals(const Gtid_set *other) const;
+
+ /// Return the number of intervals for the given sidno.
+ int get_n_intervals(rpl_sidno sidno) const {
+ Const_interval_iterator ivit(this, sidno);
+ int ret = 0;
+ while (ivit.get() != nullptr) {
+ ret++;
+ ivit.next();
+ }
+ return ret;
+ }
+ /// Return the number of intervals in this Gtid_set.
+ int get_n_intervals() const {
+ if (sid_lock != nullptr) sid_lock->assert_some_wrlock();
+ rpl_sidno max_sidno = get_max_sidno();
+ int ret = 0;
+ for (rpl_sidno sidno = 1; sidno < max_sidno; sidno++)
+ ret += get_n_intervals(sidno);
+ return ret;
+ }
+ /**
+ Allocates a new chunk of Intervals and adds them to the list of
+ unused intervals.
+
+ @param size The number of intervals in this chunk
+ */
+ void create_new_chunk(int size);
+ /**
+ Returns a fresh new Interval object.
+
+ This usually does not require any real allocation, it only pops
+ the first interval from the list of free intervals. If there are
+ no free intervals, it calls create_new_chunk.
+
+ @param out The resulting Interval* will be stored here.
+ */
+ void get_free_interval(Interval **out);
+ /**
+ Puts the given interval in the list of free intervals. Does not
+ unlink it from its place in any other list.
+ */
+ void put_free_interval(Interval *iv);
+ /**
+ Like add_interval_memory, but does not acquire
+ free_intervals_mutex.
+ @see Gtid_set::add_interval_memory
+ */
+ void add_interval_memory_lock_taken(int n_ivs, Interval *ivs);
+
+ /// Read-write lock that protects updates to the number of SIDs.
+ mutable Checkable_rwlock *sid_lock;
+ /**
+ Lock protecting the list of free intervals. This lock is only
+ used if sid_lock is not NULL.
+ */
+ mysql_mutex_t free_intervals_mutex;
+ /**
+ Class representing a lock on free_intervals_mutex.
+
+ This is used by the add_* and remove_* functions. The lock is
+ declared by the top-level function and a pointer to the lock is
+ passed down to low-level functions. If the low-level function
+ decides to access the free intervals list, then it acquires the
+ lock. The lock is then automatically released by the destructor
+ when the top-level function returns.
+
+ The lock is not taken if Gtid_set->sid_lock == NULL; such
+ Gtid_sets are assumed to be thread-local.
+ */
+ class Free_intervals_lock {
+ public:
+ /// Create a new lock, but do not acquire it.
+ Free_intervals_lock(Gtid_set *_gtid_set)
+ : gtid_set(_gtid_set), locked(false) {}
+ /// Lock the lock if it is not already locked.
+ void lock_if_not_locked() {
+ if (gtid_set->sid_lock && !locked) {
+ mysql_mutex_lock(&gtid_set->free_intervals_mutex);
+ locked = true;
+ }
+ }
+ /// Lock the lock if it is locked.
+ void unlock_if_locked() {
+ if (gtid_set->sid_lock && locked) {
+ mysql_mutex_unlock(&gtid_set->free_intervals_mutex);
+ locked = false;
+ }
+ }
+ /// Destroy this object and unlock the lock if it is locked.
+ ~Free_intervals_lock() { unlock_if_locked(); }
+
+ private:
+ Gtid_set *gtid_set;
+ bool locked;
+ };
+ void assert_free_intervals_locked() {
+ if (sid_lock != nullptr) mysql_mutex_assert_owner(&free_intervals_mutex);
+ }
+
+ /**
+ Adds the interval (start, end) to the given Interval_iterator.
+
+ This is the lowest-level function that adds gtids; this is where
+ Interval objects are added, grown, or merged.
+
+ @param ivitp Pointer to iterator. After this function returns,
+ the current_element of the iterator will be the interval that
+ contains start and end.
+ @param start The first GNO in the interval.
+ @param end The first GNO after the interval.
+ @param lock If this function has to add or remove an interval,
+ then this lock will be taken unless it is already taken. This
+ mechanism means that the lock will be taken lazily by
+ e.g. add_gtid_set() the first time that the list of free intervals
+ is accessed, and automatically released when add_gtid_set()
+ returns.
+ */
+ void add_gno_interval(Interval_iterator *ivitp, rpl_gno start, rpl_gno end,
+ Free_intervals_lock *lock);
+ /**
+ Removes the interval (start, end) from the given
+ Interval_iterator. This is the lowest-level function that removes
+ gtids; this is where Interval objects are removed, truncated, or
+ split.
+
+ It is not required that the gtids in the interval exist in this
+ Gtid_set.
+
+ @param ivitp Pointer to iterator. After this function returns,
+ the current_element of the iterator will be the next interval
+ after end.
+ @param start The first GNO in the interval.
+ @param end The first GNO after the interval.
+ @param lock If this function has to add or remove an interval,
+ then this lock will be taken unless it is already taken. This
+ mechanism means that the lock will be taken lazily by
+ e.g. add_gtid_set() the first time that the list of free intervals
+ is accessed, and automatically released when add_gtid_set()
+ returns.
+ */
+ void remove_gno_interval(Interval_iterator *ivitp, rpl_gno start, rpl_gno end,
+ Free_intervals_lock *lock);
+ /**
+ Adds a list of intervals to the given SIDNO.
+
+ The SIDNO must exist in the Gtid_set before this function is called.
+
+ @param sidno The SIDNO to which intervals will be added.
+ @param ivit Iterator over the intervals to add. This is typically
+ an iterator over some other Gtid_set.
+ @param lock If this function has to add or remove an interval,
+ then this lock will be taken unless it is already taken. This
+ mechanism means that the lock will be taken lazily by
+ e.g. add_gtid_set() the first time that the list of free intervals
+ is accessed, and automatically released when add_gtid_set()
+ returns.
+ */
+ void add_gno_intervals(rpl_sidno sidno, Const_interval_iterator ivit,
+ Free_intervals_lock *lock);
+ /**
+ Removes a list of intervals from the given SIDNO.
+
+ It is not required that the intervals exist in this Gtid_set.
+
+ @param sidno The SIDNO from which intervals will be removed.
+ @param ivit Iterator over the intervals to remove. This is typically
+ an iterator over some other Gtid_set.
+ @param lock If this function has to add or remove an interval,
+ then this lock will be taken unless it is already taken. This
+ mechanism means that the lock will be taken lazily by
+ e.g. add_gtid_set() the first time that the list of free intervals
+ is accessed, and automatically released when add_gtid_set()
+ returns.
+ */
+ void remove_gno_intervals(rpl_sidno sidno, Const_interval_iterator ivit,
+ Free_intervals_lock *lock);
+
+ /// Returns true if every interval of sub is a subset of some
+ /// interval of super.
+ static bool is_interval_subset(Const_interval_iterator *sub,
+ Const_interval_iterator *super);
+ /// Returns true if at least one sidno in ivit1 is also in ivit2.
+ static bool is_interval_intersection_nonempty(Const_interval_iterator *ivit1,
+ Const_interval_iterator *ivit2);
+
+ /// Sid_map associated with this Gtid_set.
+ Sid_map *sid_map;
+ /**
+ Array where the N'th element contains the head pointer to the
+ intervals of SIDNO N+1.
+ */
+ Prealloced_array<Interval *, 8> m_intervals;
+ /// Linked list of free intervals.
+ Interval *free_intervals;
+ /// Linked list of chunks.
+ Interval_chunk *chunks;
+ /// If the string is cached.
+ mutable bool has_cached_string_length;
+ /// The string length.
+ mutable size_t cached_string_length;
+ /// The String_format that was used when cached_string_length was computed.
+ mutable const String_format *cached_string_format;
+#ifndef DBUG_OFF
+ /**
+ The number of chunks. Used only to check some invariants when
+ DBUG is on.
+ */
+ int n_chunks;
+#endif
+ /// Used by unit tests that need to access private members.
+#ifdef FRIEND_OF_GTID_SET
+ friend FRIEND_OF_GTID_SET;
+#endif
+ /// Only Free_intervals_lock is allowed to access free_intervals_mutex.
+ friend class Gtid_set::Free_intervals_lock;
+};
+
+/**
+ Holds information about a Gtid_set. Can also be NULL.
+
+ This is used as backend storage for @@session.gtid_next_list. The
+ idea is that we allow the user to set this to NULL, but we keep the
+ Gtid_set object so that we can re-use the allocated memory and
+ avoid costly allocations later.
+
+ This is stored in struct system_variables (defined in sql_class.h),
+ which is cleared using memset(0); hence the negated form of
+ is_non_null.
+
+ The convention is: if is_non_null is false, then the value of the
+ session variable is NULL, and the field gtid_set may be NULL or
+ non-NULL. If is_non_null is true, then the value of the session
+ variable is not NULL, and the field gtid_set has to be non-NULL.
+
+ This is a POD. It has to be a POD because it is stored in
+ THD::variables.
+*/
+struct Gtid_set_or_null {
+ /// Pointer to the Gtid_set.
+ Gtid_set *gtid_set;
+ /// True if this Gtid_set is NULL.
+ bool is_non_null;
+ /// Return NULL if this is NULL, otherwise return the Gtid_set.
+ inline Gtid_set *get_gtid_set() const {
+ DBUG_ASSERT(!(is_non_null && gtid_set == nullptr));
+ return is_non_null ? gtid_set : nullptr;
+ }
+ /**
+ Do nothing if this object is non-null; set to empty set otherwise.
+
+ @return NULL if out of memory; Gtid_set otherwise.
+ */
+ Gtid_set *set_non_null(Sid_map *sm) {
+ if (!is_non_null) {
+ if (gtid_set == nullptr)
+ gtid_set = new Gtid_set(sm);
+ else
+ gtid_set->clear();
+ }
+ is_non_null = (gtid_set != nullptr);
+ return gtid_set;
+ }
+ /// Set this Gtid_set to NULL.
+ inline void set_null() { is_non_null = false; }
+};
+
+/**
+ Represents the set of GTIDs that are owned by some thread.
+
+ This data structure has a read-write lock that protects the number
+ of SIDNOs. The lock is provided by the invoker of the constructor
+ and it is generally the caller's responsibility to acquire the read
+ lock. Access methods assert that the caller already holds the read
+ (or write) lock. If a method of this class grows the number of
+ SIDNOs, then the method temporarily upgrades this lock to a write
+ lock and then degrades it to a read lock again; there will be a
+ short period when the lock is not held at all.
+
+ The internal representation is a multi-valued map from GTIDs to
+ threads, mapping GTIDs to one or more threads that owns it.
+
+ In Group Replication multiple threads can own a GTID whereas if GR
+ is disabeld there is at most one owner per GTID.
+*/
+class Owned_gtids {
+ public:
+ /**
+ Constructs a new, empty Owned_gtids object.
+
+ @param sid_lock Read-write lock that protects updates to the
+ number of SIDs.
+ */
+ Owned_gtids(Checkable_rwlock *sid_lock);
+ /// Destroys this Owned_gtids.
+ ~Owned_gtids();
+ /**
+ Add a GTID to this Owned_gtids.
+
+ @param gtid The Gtid to add.
+ @param owner The my_thread_id of the gtid to add.
+ @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+ */
+ enum_return_status add_gtid_owner(const Gtid &gtid, my_thread_id owner);
+
+ /*
+ Fill all gtids into the given Gtid_set object. It doesn't clear the given
+ gtid set before filling its owned gtids into it.
+ */
+ void get_gtids(Gtid_set &gtid_set) const;
+ /**
+ Removes the given GTID.
+
+ If the gtid does not exist in this Owned_gtids object, does
+ nothing.
+
+ @param gtid The Gtid.
+ @param owner thread_id of the owner thread
+ */
+ void remove_gtid(const Gtid &gtid, const my_thread_id owner);
+ /**
+ Ensures that this Owned_gtids object can accomodate SIDNOs up to
+ the given SIDNO.
+
+ If this Owned_gtids object needs to be resized, then the lock
+ will be temporarily upgraded to a write lock and then degraded to
+ a read lock again; there will be a short period when the lock is
+ not held at all.
+
+ @param sidno The SIDNO.
+ @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+ */
+ enum_return_status ensure_sidno(rpl_sidno sidno);
+ /// Returns true if there is a least one element of this Owned_gtids
+ /// set in the other Gtid_set.
+ bool is_intersection_nonempty(const Gtid_set *other) const;
+ /// Returns true if this Owned_gtids is empty.
+ bool is_empty() const {
+ Gtid_iterator git(this);
+ return git.get().sidno == 0;
+ }
+ /// Returns the maximal sidno that this Owned_gtids currently has space for.
+ rpl_sidno get_max_sidno() const {
+ sid_lock->assert_some_lock();
+ return static_cast<rpl_sidno>(sidno_to_hash.size());
+ }
+
+ /**
+ Write a string representation of this Owned_gtids to the given buffer.
+
+ @param out Buffer to write to.
+ @return Number of characters written.
+ */
+ int to_string(char *out) const {
+ char *p = out;
+ rpl_sidno max_sidno = get_max_sidno();
+ rpl_sidno sid_map_max_sidno = global_sid_map->get_max_sidno();
+ for (rpl_sidno sid_i = 0; sid_i < sid_map_max_sidno; sid_i++) {
+ rpl_sidno sidno = global_sid_map->get_sorted_sidno(sid_i);
+ if (sidno > max_sidno) continue;
+ bool printed_sid = false;
+ for (const auto &key_and_value : *get_hash(sidno)) {
+ Node *node = key_and_value.second.get();
+ DBUG_ASSERT(node != nullptr);
+ if (!printed_sid) {
+ p += global_sid_map->sidno_to_sid(sidno).to_string(p);
+ printed_sid = true;
+ }
+ p += sprintf(p, ":%lld#%u", node->gno, node->owner);
+ }
+ }
+ *p = 0;
+ return (int)(p - out);
+ }
+
+ /**
+ Return an upper bound on the length of the string representation
+ of this Owned_gtids. The actual length may be smaller. This
+ includes the trailing '\0'.
+ */
+ size_t get_max_string_length() const {
+ rpl_sidno max_sidno = get_max_sidno();
+ size_t ret = 0;
+ for (rpl_sidno sidno = 1; sidno <= max_sidno; sidno++) {
+ size_t records = get_hash(sidno)->size();
+ if (records > 0)
+ ret +=
+ binary_log::Uuid::TEXT_LENGTH +
+ records * (1 + MAX_GNO_TEXT_LENGTH + 1 + MAX_THREAD_ID_TEXT_LENGTH);
+ }
+ return 1 + ret;
+ }
+
+ /**
+ Return true if the given thread is the owner of any gtids.
+ */
+ bool thread_owns_anything(my_thread_id thd_id) const {
+ Gtid_iterator git(this);
+ Node *node = git.get_node();
+ while (node != nullptr) {
+ if (node->owner == thd_id) return true;
+ git.next();
+ node = git.get_node();
+ }
+ return false;
+ }
+
+#ifndef DBUG_OFF
+ /**
+ Debug only: return a newly allocated string representation of
+ this Owned_gtids.
+ */
+ char *to_string() const {
+ char *str = (char *)my_malloc(key_memory_Owned_gtids_to_string,
+ get_max_string_length(), MYF(MY_WME));
+ DBUG_ASSERT(str != nullptr);
+ to_string(str);
+ return str;
+ }
+ /// Debug only: print this Owned_gtids to stdout.
+ void print() const {
+ char *str = to_string();
+ printf("%s\n", str);
+ my_free(str);
+ }
+#endif
+ /**
+ Print this Owned_gtids to the trace file if debug is enabled; no-op
+ otherwise.
+ */
+ void dbug_print(const char *text MY_ATTRIBUTE((unused)) = "") const {
+#ifndef DBUG_OFF
+ char *str = to_string();
+ DBUG_PRINT("info", ("%s%s%s", text, *text ? ": " : "", str));
+ my_free(str);
+#endif
+ }
+
+ /**
+ If thd_id==0, returns true when gtid is not owned by any thread.
+ If thd_id!=0, returns true when gtid is owned by that thread.
+ */
+ bool is_owned_by(const Gtid &gtid, const my_thread_id thd_id) const;
+
+ private:
+ /// Represents one owned GTID.
+ struct Node {
+ /// GNO of the GTID.
+ rpl_gno gno;
+ /// Owner of the GTID.
+ my_thread_id owner;
+ };
+ /// Read-write lock that protects updates to the number of SIDs.
+ mutable Checkable_rwlock *sid_lock;
+ /// Returns the hash for the given SIDNO.
+ malloc_unordered_multimap<rpl_gno, unique_ptr_my_free<Node>> *get_hash(
+ rpl_sidno sidno) const {
+ DBUG_ASSERT(sidno >= 1 && sidno <= get_max_sidno());
+ sid_lock->assert_some_lock();
+ return sidno_to_hash[sidno - 1];
+ }
+ /// Return true iff this Owned_gtids object contains the given gtid.
+ bool contains_gtid(const Gtid &gtid) const;
+
+ /// Growable array of hashes.
+ Prealloced_array<
+ malloc_unordered_multimap<rpl_gno, unique_ptr_my_free<Node>> *, 8>
+ sidno_to_hash;
+
+ public:
+ /**
+ Iterator over all gtids in a Owned_gtids set. This is a const
+ iterator; it does not allow modification of the set.
+ */
+ class Gtid_iterator {
+ public:
+ Gtid_iterator(const Owned_gtids *og)
+ : owned_gtids(og), sidno(1), hash(nullptr), node(nullptr) {
+ max_sidno = owned_gtids->get_max_sidno();
+ if (sidno <= max_sidno) {
+ hash = owned_gtids->get_hash(sidno);
+ node_it = hash->begin();
+ }
+ next();
+ }
+ /// Advance to next GTID.
+ inline void next() {
+#ifndef DBUG_OFF
+ if (owned_gtids->sid_lock) owned_gtids->sid_lock->assert_some_wrlock();
+#endif
+
+ while (sidno <= max_sidno) {
+ DBUG_ASSERT(hash != nullptr);
+ if (node_it != hash->end()) {
+ node = node_it->second.get();
+ DBUG_ASSERT(node != nullptr);
+ // Jump to next node on next iteration.
+ ++node_it;
+ return;
+ }
+
+ // hash is initialized on constructor or in previous iteration
+ // for current SIDNO, so we must increment for next iteration.
+ sidno++;
+ if (sidno <= max_sidno) {
+ hash = owned_gtids->get_hash(sidno);
+ node_it = hash->begin();
+ }
+ }
+ node = nullptr;
+ }
+ /// Return next GTID, or {0,0} if we reached the end.
+ inline Gtid get() const {
+ Gtid ret = {0, 0};
+ if (node) {
+ ret.sidno = sidno;
+ ret.gno = node->gno;
+ }
+ return ret;
+ }
+ /// Return the current GTID Node, or NULL if we reached the end.
+ inline Node *get_node() const { return node; }
+
+ private:
+ /// The Owned_gtids set we iterate over.
+ const Owned_gtids *owned_gtids;
+ /// The SIDNO of the current element, or 1 in the initial iteration.
+ rpl_sidno sidno;
+ /// Max SIDNO of the current iterator.
+ rpl_sidno max_sidno;
+ /// Current SIDNO hash.
+ malloc_unordered_multimap<rpl_gno, unique_ptr_my_free<Node>> *hash;
+ /// Current node iterator on current SIDNO hash.
+ malloc_unordered_multimap<rpl_gno, unique_ptr_my_free<Node>>::const_iterator
+ node_it;
+ /// Current node on current SIDNO hash.
+ Node *node;
+ };
+};
+
+/**
+ Represents the server's GTID state: the set of committed GTIDs, the
+ set of lost gtids, the set of owned gtids, the owner of each owned
+ gtid, and a Mutex_cond_array that protects updates to gtids of
+ each SIDNO.
+
+ Locking:
+
+ This data structure has a read-write lock that protects the number
+ of SIDNOs, and a Mutex_cond_array that contains one mutex per SIDNO.
+ The rwlock is always the global_sid_lock.
+
+ Access methods generally assert that the caller already holds the
+ appropriate lock:
+
+ - before accessing any global data, hold at least the rdlock.
+
+ - before accessing a specific SIDNO in a Gtid_set or Owned_gtids
+ (e.g., calling Gtid_set::_add_gtid(Gtid)), hold either the rdlock
+ and the SIDNO's mutex lock; or the wrlock. If you need to hold
+ multiple mutexes, they must be acquired in order of increasing
+ SIDNO.
+
+ - before starting an operation that needs to access all SIDs
+ (e.g. Gtid_set::to_string()), hold the wrlock.
+
+ The access type (read/write) does not matter; the write lock only
+ implies that the entire data structure is locked whereas the read
+ lock implies that everything except SID-specific data is locked.
+*/
+class Gtid_state {
+ public:
+ /**
+ Constructs a new Gtid_state object.
+
+ @param _sid_lock Read-write lock that protects updates to the
+ number of SIDs.
+ @param _sid_map Sid_map used by this Gtid_state.
+ */
+ Gtid_state(Checkable_rwlock *_sid_lock, Sid_map *_sid_map)
+ : sid_lock(_sid_lock),
+ sid_map(_sid_map),
+ sid_locks(sid_lock),
+ lost_gtids(sid_map, sid_lock),
+ executed_gtids(sid_map, sid_lock),
+ gtids_only_in_table(sid_map, sid_lock),
+ previous_gtids_logged(sid_map, sid_lock),
+ owned_gtids(sid_lock),
+ commit_group_sidnos(key_memory_Gtid_state_group_commit_sidno) {}
+ /**
+ Add @@GLOBAL.SERVER_UUID to this binlog's Sid_map.
+
+ This can't be done in the constructor because the constructor is
+ invoked at server startup before SERVER_UUID is initialized.
+
+ The caller must hold the read lock or write lock on sid_locks
+ before invoking this function.
+
+ @retval 0 Success
+ @retval 1 Error (out of memory or IO error).
+ */
+ int init();
+ /**
+ Reset the state and persistor after RESET MASTER: remove all logged
+ and lost gtids, but keep owned gtids as they are.
+
+ The caller must hold the write lock on sid_lock before calling
+ this function.
+
+ @param thd Thread requesting to reset the persistor
+
+ @retval 0 Success
+ @retval -1 Error
+ */
+ int clear(THD *thd);
+ /**
+ Returns true if the given GTID is logged.
+
+ @param gtid The Gtid to check.
+
+ @retval true The gtid is logged in the binary log.
+ @retval false The gtid is not logged in the binary log.
+ */
+ bool is_executed(const Gtid &gtid) const {
+ DBUG_ENTER("Gtid_state::is_executed");
+ bool ret = executed_gtids.contains_gtid(gtid);
+ DBUG_RETURN(ret);
+ }
+ /**
+ Returns true if GTID is owned, otherwise returns 0.
+
+ @param gtid The Gtid to check.
+ @return true if some thread owns the gtid, false if the gtid is
+ not owned
+ */
+ bool is_owned(const Gtid &gtid) const {
+ return !owned_gtids.is_owned_by(gtid, 0);
+ }
+#ifdef MYSQL_SERVER
+ /**
+ Acquires ownership of the given GTID, on behalf of the given thread.
+
+ The caller must lock the SIDNO before invoking this function.
+
+ @param thd The thread that will own the GTID.
+ @param gtid The Gtid to acquire ownership of.
+ @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+ */
+ enum_return_status acquire_ownership(THD *thd, const Gtid &gtid);
+ /**
+ This function updates both the THD and the Gtid_state to reflect that
+ the transaction set of transactions has ended, and it does this for the
+ whole commit group (by following the thd->next_to_commit pointer).
+
+ It will:
+
+ - Clean up the thread state when a thread owned GTIDs is empty.
+ - Release ownership of all GTIDs owned by the THDs. This removes
+ the GTIDs from Owned_gtids and clears the ownership status in the
+ THDs object.
+ - Add the owned GTIDs to executed_gtids when the thread is committing.
+ - Decrease counters of GTID-violating transactions.
+ - Send a broadcast on the condition variable for every sidno for
+ which we released ownership.
+
+ @param first_thd The first thread of the group commit that needs GTIDs to
+ be updated.
+ */
+ void update_commit_group(THD *first_thd);
+ /**
+ Remove the GTID owned by thread from owned GTIDs, stating that
+ thd->owned_gtid was committed.
+
+ This will:
+ - remove owned GTID from owned_gtids;
+ - remove all owned GTIDS from thd->owned_gtid and thd->owned_gtid_set;
+
+ @param thd Thread for which owned gtids are updated.
+ */
+ void update_on_commit(THD *thd);
+ /**
+ Update the state after the given thread has rollbacked.
+
+ This will:
+ - release ownership of all GTIDs owned by the THD;
+ - remove owned GTID from owned_gtids;
+ - remove all owned GTIDS from thd->owned_gtid and thd->owned_gtid_set;
+ - send a broadcast on the condition variable for every sidno for
+ which we released ownership.
+
+ @param thd Thread for which owned gtids are updated.
+ */
+ void update_on_rollback(THD *thd);
+
+ /**
+ Acquire anonymous ownership.
+
+ The caller must hold either sid_lock.rdlock or
+ sid_lock.wrlock. (The caller must have taken the lock and checked
+ that gtid_mode!=ON before calling this function, or else the
+ gtid_mode could have changed to ON by a concurrent SET GTID_MODE.)
+ */
+ void acquire_anonymous_ownership() {
+ DBUG_ENTER("Gtid_state::acquire_anonymous_ownership");
+ sid_lock->assert_some_lock();
+ DBUG_ASSERT(get_gtid_mode(GTID_MODE_LOCK_SID) != GTID_MODE_ON);
+#ifndef DBUG_OFF
+ int32 new_value =
+#endif
+ ++atomic_anonymous_gtid_count;
+ DBUG_PRINT("info",
+ ("atomic_anonymous_gtid_count increased to %d", new_value));
+ DBUG_ASSERT(new_value >= 1);
+ DBUG_VOID_RETURN;
+ }
+
+ /// Release anonymous ownership.
+ void release_anonymous_ownership() {
+ DBUG_ENTER("Gtid_state::release_anonymous_ownership");
+ sid_lock->assert_some_lock();
+ DBUG_ASSERT(get_gtid_mode(GTID_MODE_LOCK_SID) != GTID_MODE_ON);
+#ifndef DBUG_OFF
+ int32 new_value =
+#endif
+ --atomic_anonymous_gtid_count;
+ DBUG_PRINT("info",
+ ("atomic_anonymous_gtid_count decreased to %d", new_value));
+ DBUG_ASSERT(new_value >= 0);
+ DBUG_VOID_RETURN;
+ }
+
+ /// Return the number of clients that hold anonymous ownership.
+ int32 get_anonymous_ownership_count() { return atomic_anonymous_gtid_count; }
+
+ /**
+ Increase the global counter when starting a GTID-violating
+ transaction having GTID_NEXT=AUTOMATIC.
+ */
+ void begin_automatic_gtid_violating_transaction() {
+ DBUG_ENTER("Gtid_state::begin_automatic_gtid_violating_transaction");
+ DBUG_ASSERT(get_gtid_mode(GTID_MODE_LOCK_SID) <= GTID_MODE_OFF_PERMISSIVE);
+ DBUG_ASSERT(get_gtid_consistency_mode() != GTID_CONSISTENCY_MODE_ON);
+#ifndef DBUG_OFF
+ int32 new_value =
+#endif
+ ++atomic_automatic_gtid_violation_count;
+ DBUG_PRINT(
+ "info",
+ ("ongoing_automatic_gtid_violating_transaction_count increased to %d",
+ new_value));
+ DBUG_ASSERT(new_value >= 1);
+ DBUG_VOID_RETURN;
+ }
+
+ /**
+ Decrease the global counter when ending a GTID-violating
+ transaction having GTID_NEXT=AUTOMATIC.
+ */
+ void end_automatic_gtid_violating_transaction() {
+ DBUG_ENTER("Gtid_state::end_automatic_gtid_violating_transaction");
+#ifndef DBUG_OFF
+ global_sid_lock->rdlock();
+ DBUG_ASSERT(get_gtid_mode(GTID_MODE_LOCK_SID) <= GTID_MODE_OFF_PERMISSIVE);
+ DBUG_ASSERT(get_gtid_consistency_mode() != GTID_CONSISTENCY_MODE_ON);
+ global_sid_lock->unlock();
+ int32 new_value =
+#endif
+ --atomic_automatic_gtid_violation_count;
+ DBUG_PRINT(
+ "info",
+ ("ongoing_automatic_gtid_violating_transaction_count decreased to %d",
+ new_value));
+ DBUG_ASSERT(new_value >= 0);
+ DBUG_VOID_RETURN;
+ }
+
+ /**
+ Return the number of ongoing GTID-violating transactions having
+ GTID_NEXT=AUTOMATIC.
+ */
+ int32 get_automatic_gtid_violating_transaction_count() {
+ return atomic_automatic_gtid_violation_count;
+ }
+
+ /**
+ Increase the global counter when starting a GTID-violating
+ transaction having GTID_NEXT=ANONYMOUS.
+ */
+ void begin_anonymous_gtid_violating_transaction() {
+ DBUG_ENTER("Gtid_state::begin_anonymous_gtid_violating_transaction");
+ DBUG_ASSERT(get_gtid_mode(GTID_MODE_LOCK_SID) != GTID_MODE_ON);
+ DBUG_ASSERT(get_gtid_consistency_mode() != GTID_CONSISTENCY_MODE_ON);
+#ifndef DBUG_OFF
+ int32 new_value =
+#endif
+ ++atomic_anonymous_gtid_violation_count;
+ DBUG_PRINT("info", ("atomic_anonymous_gtid_violation_count increased to %d",
+ new_value));
+ DBUG_ASSERT(new_value >= 1);
+ DBUG_VOID_RETURN;
+ }
+
+ /**
+ Decrease the global counter when ending a GTID-violating
+ transaction having GTID_NEXT=ANONYMOUS.
+ */
+ void end_anonymous_gtid_violating_transaction() {
+ DBUG_ENTER("Gtid_state::end_anonymous_gtid_violating_transaction");
+#ifndef DBUG_OFF
+ global_sid_lock->rdlock();
+ DBUG_ASSERT(get_gtid_mode(GTID_MODE_LOCK_SID) != GTID_MODE_ON);
+ DBUG_ASSERT(get_gtid_consistency_mode() != GTID_CONSISTENCY_MODE_ON);
+ global_sid_lock->unlock();
+ int32 new_value =
+#endif
+ --atomic_anonymous_gtid_violation_count;
+ DBUG_PRINT(
+ "info",
+ ("ongoing_anonymous_gtid_violating_transaction_count decreased to %d",
+ new_value));
+ DBUG_ASSERT(new_value >= 0);
+ DBUG_VOID_RETURN;
+ }
+
+ void end_gtid_violating_transaction(THD *thd);
+
+ /**
+ Return the number of ongoing GTID-violating transactions having
+ GTID_NEXT=AUTOMATIC.
+ */
+ int32 get_anonymous_gtid_violating_transaction_count() {
+ return atomic_anonymous_gtid_violation_count;
+ }
+
+ /**
+ Increase the global counter when starting a call to
+ WAIT_FOR_EXECUTED_GTID_SET or WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS.
+ */
+ void begin_gtid_wait(
+ enum_gtid_mode_lock gtid_mode_lock MY_ATTRIBUTE((unused))) {
+ DBUG_ENTER("Gtid_state::begin_gtid_wait");
+ DBUG_ASSERT(get_gtid_mode(gtid_mode_lock) != GTID_MODE_OFF);
+#ifndef DBUG_OFF
+ int32 new_value =
+#endif
+ ++atomic_gtid_wait_count;
+ DBUG_PRINT("info", ("atomic_gtid_wait_count changed from %d to %d",
+ new_value - 1, new_value));
+ DBUG_ASSERT(new_value >= 1);
+ DBUG_VOID_RETURN;
+ }
+
+ /**
+ Decrease the global counter when ending a call to
+ WAIT_FOR_EXECUTED_GTID_SET or WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS.
+ */
+ void end_gtid_wait() {
+ DBUG_ENTER("Gtid_state::end_gtid_wait");
+ DBUG_ASSERT(get_gtid_mode(GTID_MODE_LOCK_NONE) != GTID_MODE_OFF);
+#ifndef DBUG_OFF
+ int32 new_value =
+#endif
+ --atomic_gtid_wait_count;
+ DBUG_PRINT("info", ("atomic_gtid_wait_count changed from %d to %d",
+ new_value + 1, new_value));
+ DBUG_ASSERT(new_value >= 0);
+ DBUG_VOID_RETURN;
+ }
+
+ /**
+ Return the number of clients that have an ongoing call to
+ WAIT_FOR_EXECUTED_GTID_SET or WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS.
+ */
+ int32 get_gtid_wait_count() { return atomic_gtid_wait_count; }
+
+#endif // ifdef MYSQL_SERVER
+ private:
+ /**
+ Computes the next available GNO.
+
+ @param sidno The GTID's SIDNO.
+
+ @retval -1 The range of GNOs was exhausted (i.e., more than 1<<63-1
+ GTIDs with the same UUID have been generated).
+ @retval >0 The GNO for the GTID.
+ */
+ rpl_gno get_automatic_gno(rpl_sidno sidno) const;
+ /**
+ The next_free_gno variable will be set with the supposed next free GNO
+ every time a new GNO is delivered automatically or when a transaction is
+ rolled back, releasing a GNO smaller than the last one delivered.
+ It was introduced in an optimization of Gtid_state::get_automatic_gno and
+ Gtid_state::generate_automatic_gtid functions.
+
+ Locking scheme
+
+ This variable can be read and modified in four places:
+ - During server startup, holding global_sid_lock.wrlock;
+ - By a client thread holding global_sid_lock.wrlock (doing a RESET MASTER);
+ - By a client thread calling MYSQL_BIN_LOG::write_gtid function (often the
+ group commit FLUSH stage leader). It will call
+ Gtid_state::generate_automatic_gtid, that will acquire
+ global_sid_lock.rdlock and lock_sidno(get_server_sidno()) when getting a
+ new automatically generated GTID;
+ - By a client thread rolling back, holding global_sid_lock.rdlock
+ and lock_sidno(get_server_sidno()).
+ */
+ rpl_gno next_free_gno;
+
+ public:
+ /**
+ Return the last executed GNO for a given SIDNO, e.g.
+ for the following set: UUID:1-10, UUID:12, UUID:15-20
+ 20 will be returned.
+
+ @param sidno The GTID's SIDNO.
+
+ @retval The GNO or 0 if set is empty.
+ */
+ rpl_gno get_last_executed_gno(rpl_sidno sidno) const;
+ /**
+ Generates the GTID (or ANONYMOUS, if GTID_MODE = OFF or
+ OFF_PERMISSIVE) for the THD, and acquires ownership.
+
+ @param thd The thread.
+ @param specified_sidno Externaly generated sidno.
+ @param specified_gno Externaly generated gno.
+ @param[in,out] locked_sidno This parameter should be used when there is
+ a need of generating many GTIDs without having
+ to acquire/release a sidno_lock many times.
+ The caller must hold global_sid_lock and unlock
+ the locked_sidno after invocation when
+ locked_sidno > 0 if locked_sidno!=NULL.
+ The caller must not hold global_sid_lock when
+ locked_sidno==NULL.
+ See comments on function code to more details.
+
+ @return RETURN_STATUS_OK or RETURN_STATUS_ERROR. Error can happen
+ in case of out of memory or if the range of GNOs was exhausted.
+ */
+ enum_return_status generate_automatic_gtid(THD *thd,
+ rpl_sidno specified_sidno = 0,
+ rpl_gno specified_gno = 0,
+ rpl_sidno *locked_sidno = nullptr);
+
+ /// Locks a mutex for the given SIDNO.
+ void lock_sidno(rpl_sidno sidno) { sid_locks.lock(sidno); }
+ /// Unlocks a mutex for the given SIDNO.
+ void unlock_sidno(rpl_sidno sidno) { sid_locks.unlock(sidno); }
+ /// Broadcasts updates for the given SIDNO.
+ void broadcast_sidno(rpl_sidno sidno) { sid_locks.broadcast(sidno); }
+ /// Assert that we own the given SIDNO.
+ void assert_sidno_lock_owner(rpl_sidno sidno) {
+ sid_locks.assert_owner(sidno);
+ }
+#ifdef MYSQL_SERVER
+ /**
+ Wait for a signal on the given SIDNO.
+
+ NOTE: This releases a lock!
+
+ This requires that the caller holds a read lock on sid_lock. It
+ will release the lock before waiting; neither global_sid_lock nor
+ the mutex lock on SIDNO will not be held when this function
+ returns.
+
+ @param thd THD object of the caller.
+ @param sidno Sidno to wait for.
+ @param[in] abstime The absolute point in time when the wait times
+ out and stops, or NULL to wait indefinitely.
+
+ @retval false Success.
+ @retval true Failure: either timeout or thread was killed. If
+ thread was killed, the error has been generated.
+ */
+ bool wait_for_sidno(THD *thd, rpl_sidno sidno, struct timespec *abstime);
+ /**
+ This is only a shorthand for wait_for_sidno, which contains
+ additional debug printouts and assertions for the case when the
+ caller waits for one specific GTID.
+ */
+ bool wait_for_gtid(THD *thd, const Gtid &gtid,
+ struct timespec *abstime = nullptr);
+ /**
+ Wait until the given Gtid_set is included in @@GLOBAL.GTID_EXECUTED.
+
+ @param thd The calling thread.
+ @param gtid_set Gtid_set to wait for.
+ @param[in] timeout The maximum number of milliseconds that the
+ function should wait, or 0 to wait indefinitely.
+
+ @retval false Success.
+ @retval true Failure: either timeout or thread was killed. If
+ thread was killed, the error has been generated.
+ */
+ bool wait_for_gtid_set(THD *thd, Gtid_set *gtid_set, double timeout);
+#endif // ifdef MYSQL_SERVER
+ /**
+ Locks one mutex for each SIDNO where the given Gtid_set has at
+ least one GTID. Locks are acquired in order of increasing SIDNO.
+ */
+ void lock_sidnos(const Gtid_set *set);
+ /**
+ Unlocks the mutex for each SIDNO where the given Gtid_set has at
+ least one GTID.
+ */
+ void unlock_sidnos(const Gtid_set *set);
+ /**
+ Broadcasts the condition variable for each SIDNO where the given
+ Gtid_set has at least one GTID.
+ */
+ void broadcast_sidnos(const Gtid_set *set);
+ /**
+ Ensure that owned_gtids, executed_gtids, lost_gtids, gtids_only_in_table,
+ previous_gtids_logged and sid_locks have room for at least as many SIDNOs
+ as sid_map.
+
+ This function must only be called in one place:
+ Sid_map::add_sid().
+
+ Requires that the write lock on sid_locks is held. If any object
+ needs to be resized, then the lock will be temporarily upgraded to
+ a write lock and then degraded to a read lock again; there will be
+ a short period when the lock is not held at all.
+
+ @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+ */
+ enum_return_status ensure_sidno();
+
+ /**
+ Adds the given Gtid_set to lost_gtids and executed_gtids.
+ lost_gtids must be a subset of executed_gtids.
+ purged_gtid and executed_gtid sets are appened with the argument set
+ provided the latter is disjoint with gtid_executed owned_gtids.
+
+ Requires that the caller holds global_sid_lock.wrlock.
+
+ @param[in,out] gtid_set The gtid_set to add. If the gtid_set
+ does not start with a plus sign (starts_with_plus is false),
+ @@GLOBAL.GTID_PURGED will be removed from the gtid_set.
+ @param starts_with_plus If true, the gtid_set passed is required to
+ be disjoint from @@GLOBAL.GTID_PURGED; if false, the gtid_set passed
+ is required to be a superset of @@GLOBAL.GTID_PURGED.
+ @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+ */
+ enum_return_status add_lost_gtids(Gtid_set *gtid_set, bool starts_with_plus);
+
+ /** Updates previously logged GTID set before writing to table. */
+ void update_prev_gtids(Gtid_set *write_gtid_set);
+
+ /// Return a pointer to the Gtid_set that contains the lost gtids.
+ const Gtid_set *get_lost_gtids() const { return &lost_gtids; }
+ /*
+ Return a pointer to the Gtid_set that contains the stored gtids
+ in gtid_executed table.
+ */
+ const Gtid_set *get_executed_gtids() const { return &executed_gtids; }
+ /*
+ Return a pointer to the Gtid_set that contains the stored gtids
+ only in gtid_executed table, not in binlog files.
+ */
+ const Gtid_set *get_gtids_only_in_table() const {
+ return &gtids_only_in_table;
+ }
+ /*
+ Return a pointer to the Gtid_set that contains the previous stored
+ gtids in the last binlog file.
+ */
+ const Gtid_set *get_previous_gtids_logged() const {
+ return &previous_gtids_logged;
+ }
+ /// Return a pointer to the Owned_gtids that contains the owned gtids.
+ const Owned_gtids *get_owned_gtids() const { return &owned_gtids; }
+ /// Return the server's SID's SIDNO
+ rpl_sidno get_server_sidno() const { return server_sidno; }
+ /// Return the server's SID
+ const rpl_sid &get_server_sid() const {
+ return global_sid_map->sidno_to_sid(server_sidno);
+ }
+#ifndef DBUG_OFF
+ /**
+ Debug only: Returns an upper bound on the length of the string
+ generated by to_string(), not counting '\0'. The actual length
+ may be shorter.
+ */
+ size_t get_max_string_length() const {
+ return owned_gtids.get_max_string_length() +
+ executed_gtids.get_string_length() + lost_gtids.get_string_length() +
+ gtids_only_in_table.get_string_length() +
+ previous_gtids_logged.get_string_length() + 150;
+ }
+ /// Debug only: Generate a string in the given buffer and return the length.
+ int to_string(char *buf) const {
+ char *p = buf;
+ p += sprintf(p, "Executed GTIDs:\n");
+ p += executed_gtids.to_string(p);
+ p += sprintf(p, "\nOwned GTIDs:\n");
+ p += owned_gtids.to_string(p);
+ p += sprintf(p, "\nLost GTIDs:\n");
+ p += lost_gtids.to_string(p);
+ p += sprintf(p, "\nGTIDs only_in_table:\n");
+ p += lost_gtids.to_string(p);
+ return (int)(p - buf);
+ }
+ /// Debug only: return a newly allocated string, or NULL on out-of-memory.
+ char *to_string() const {
+ char *str = (char *)my_malloc(key_memory_Gtid_state_to_string,
+ get_max_string_length(), MYF(MY_WME));
+ to_string(str);
+ return str;
+ }
+ /// Debug only: print this Gtid_state to stdout.
+ void print() const {
+ char *str = to_string();
+ printf("%s", str);
+ my_free(str);
+ }
+#endif
+ /**
+ Print this Gtid_state to the trace file if debug is enabled; no-op
+ otherwise.
+ */
+ void dbug_print(const char *text MY_ATTRIBUTE((unused)) = "") const {
+#ifndef DBUG_OFF
+ sid_lock->assert_some_wrlock();
+ char *str = to_string();
+ DBUG_PRINT("info", ("%s%s%s", text, *text ? ": " : "", str));
+ my_free(str);
+#endif
+ }
+ /**
+ Save gtid owned by the thd into executed_gtids variable
+ and gtid_executed table.
+
+ @param thd Session to commit
+ @retval
+ 0 OK
+ @retval
+ -1 Error
+ */
+ int save(THD *thd);
+ /**
+ Insert the gtid set into table.
+
+ @param gtid_set contains a set of gtid, which holds
+ the sidno and the gno.
+
+ @retval
+ 0 OK
+ @retval
+ -1 Error
+ */
+ int save(const Gtid_set *gtid_set);
+ /**
+ Save the set of gtids logged in the last binlog into gtid_executed table.
+
+ @retval
+ 0 OK
+ @retval
+ -1 Error
+ */
+ int save_gtids_of_last_binlog_into_table();
+ /**
+ Fetch gtids from gtid_executed table and store them into
+ gtid_executed set.
+
+ @retval
+ 0 OK
+ @retval
+ 1 The table was not found.
+ @retval
+ -1 Error
+ */
+ int read_gtid_executed_from_table();
+ /**
+ Compress the gtid_executed table, read each row by the PK(sid, gno_start)
+ in increasing order, compress the first consecutive gtids range
+ (delete consecutive gtids from the second consecutive gtid, then
+ update the first gtid) within a single transaction.
+
+ @param thd Thread requesting to compress the table
+
+ @retval
+ 0 OK
+ @retval
+ 1 The table was not found.
+ @retval
+ -1 Error
+ */
+ int compress(THD *thd);
+#ifdef MYSQL_SERVER
+ /**
+ Push a warning to client if user is modifying the gtid_executed
+ table explicitly by a non-XA transaction. Push an error to client
+ if user is modifying it explicitly by a XA transaction.
+
+ @param thd Thread requesting to access the table
+ @param table The table is being accessed.
+
+ @retval 0 No warning or error was pushed to the client.
+ @retval 1 Push a warning to client.
+ @retval 2 Push an error to client.
+ */
+ int warn_or_err_on_modify_gtid_table(THD *thd, TABLE_LIST *table);
+#endif
+
+ private:
+ /**
+ Remove the GTID owned by thread from owned GTIDs.
+
+ This will:
+
+ - Clean up the thread state if the thread owned GTIDs is empty.
+ - Release ownership of all GTIDs owned by the THD. This removes
+ the GTID from Owned_gtids and clears the ownership status in the
+ THD object.
+ - Add the owned GTID to executed_gtids if the is_commit flag is
+ set.
+ - Decrease counters of GTID-violating transactions.
+ - Send a broadcast on the condition variable for every sidno for
+ which we released ownership.
+
+ @param[in] thd Thread for which owned gtids are updated.
+ @param[in] is_commit If true, the update is for a commit (not a rollback).
+ */
+ void update_gtids_impl(THD *thd, bool is_commit);
+#ifdef HAVE_GTID_NEXT_LIST
+ /// Lock all SIDNOs owned by the given THD.
+ void lock_owned_sidnos(const THD *thd);
+#endif
+ /// Unlock all SIDNOs owned by the given THD.
+ void unlock_owned_sidnos(const THD *thd);
+ /// Broadcast the condition for all SIDNOs owned by the given THD.
+ void broadcast_owned_sidnos(const THD *thd);
+ /// Read-write lock that protects updates to the number of SIDs.
+ mutable Checkable_rwlock *sid_lock;
+ /// The Sid_map used by this Gtid_state.
+ mutable Sid_map *sid_map;
+ /// Contains one mutex/cond pair for every SIDNO.
+ Mutex_cond_array sid_locks;
+ /**
+ The set of GTIDs that existed in some previously purged binary log.
+ This is always a subset of executed_gtids.
+ */
+ Gtid_set lost_gtids;
+ /*
+ The set of GTIDs that has been executed and
+ stored into gtid_executed table.
+ */
+ Gtid_set executed_gtids;
+ /*
+ The set of GTIDs that exists only in gtid_executed table, not in
+ binlog files.
+ */
+ Gtid_set gtids_only_in_table;
+ /* The previous GTIDs in the last binlog. */
+ Gtid_set previous_gtids_logged;
+ /// The set of GTIDs that are owned by some thread.
+ Owned_gtids owned_gtids;
+ /// The SIDNO for this server.
+ rpl_sidno server_sidno;
+
+ /// The number of anonymous transactions owned by any client.
+ std::atomic<int32> atomic_anonymous_gtid_count{0};
+ /// The number of GTID-violating transactions that use GTID_NEXT=AUTOMATIC.
+ std::atomic<int32> atomic_automatic_gtid_violation_count{0};
+ /// The number of GTID-violating transactions that use GTID_NEXT=AUTOMATIC.
+ std::atomic<int32> atomic_anonymous_gtid_violation_count{0};
+ /// The number of clients that are executing
+ /// WAIT_FOR_EXECUTED_GTID_SET or WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS.
+ std::atomic<int32> atomic_gtid_wait_count{0};
+
+ /// Used by unit tests that need to access private members.
+#ifdef FRIEND_OF_GTID_STATE
+ friend FRIEND_OF_GTID_STATE;
+#endif
+
+ /**
+ This is a sub task of update_on_rollback responsible only to handle
+ the case of a thread that needs to skip GTID operations when it has
+ "failed to commit".
+
+ Administrative commands [CHECK|REPAIR|OPTIMIZE|ANALYZE] TABLE
+ are written to the binary log even when they fail. When the
+ commands fail, they will call update_on_rollback; later they will
+ write the binary log. But we must not do any of the things in
+ update_gtids_impl if we are going to write the binary log. So
+ these statements set the skip_gtid_rollback flag, which tells
+ update_on_rollback to return early. When the statements are
+ written to the binary log they will call update_on_commit as
+ usual.
+
+ @param[in] thd - Thread to be evaluated.
+
+ @retval true The transaction should skip the rollback, false otherwise.
+ */
+ bool update_gtids_impl_check_skip_gtid_rollback(THD *thd);
+ /**
+ This is a sub task of update_gtids_impl responsible only to handle
+ the case of a thread that owns nothing and does not violate GTID
+ consistency.
+
+ If the THD does not own anything, there is nothing to do, so we can do an
+ early return of the update process. Except if there is a GTID consistency
+ violation; then we need to decrease the counter, so then we can continue
+ executing inside update_gtids_impl.
+
+ @param[in] thd - Thread to be evaluated.
+ @retval true The transaction can be skipped because it owns nothing and
+ does not violate GTID consistency, false otherwise.
+ */
+ bool update_gtids_impl_do_nothing(THD *thd);
+ /**
+ This is a sub task of update_gtids_impl responsible only to evaluate
+ if the thread is committing in the middle of a statement by checking
+ THD's is_commit_in_middle_of_statement flag.
+
+ This flag is true for anonymous transactions, when the
+ 'transaction' has been split into multiple transactions in the
+ binlog, and the present transaction is not the last one.
+
+ This means two things:
+
+ - We should not release anonymous ownership in case
+ gtid_next=anonymous. If we did, it would be possible for user
+ to set GTID_MODE=ON from a concurrent transaction, making it
+ impossible to commit the current transaction.
+
+ - We should not decrease the counters for GTID-violating
+ statements. If we did, it would be possible for a concurrent
+ client to set ENFORCE_GTID_CONSISTENCY=ON despite there is an
+ ongoing transaction that violates GTID consistency.
+
+ The flag is set in two cases:
+
+ 1. We are committing the statement cache when there are more
+ changes in the transaction cache.
+
+ This happens either because a single statement in the
+ beginning of a transaction updates both transactional and
+ non-transactional tables, or because we are committing a
+ non-transactional update in the middle of a transaction when
+ binlog_direct_non_transactional_updates=1.
+
+ In this case, the flag is set further down in this function.
+
+ 2. The statement is one of the special statements that may
+ generate multiple transactions: CREATE...SELECT, DROP TABLE,
+ DROP DATABASE. See comment for THD::owned_gtid in
+ sql/sql_class.h.
+
+ In this case, the THD::is_commit_in_middle_of_statement flag
+ is set by the caller and the flag becomes true here.
+
+ @param[in] thd - Thread to be evaluated.
+ @return The value of thread's is_commit_in_middle_of_statement flag.
+ */
+ bool update_gtids_impl_begin(THD *thd);
+ /**
+ Handle the case that the thread own a set of GTIDs.
+
+ This is a sub task of update_gtids_impl responsible only to handle
+ the case of a thread with a set of GTIDs being updated.
+
+ - Release ownership of the GTIDs owned by the THD. This removes
+ the GTID from Owned_gtids and clears the ownership status in the
+ THD object.
+ - Add the owned GTIDs to executed_gtids if the is_commit flag is set.
+ - Send a broadcast on the condition variable for the sidno which we
+ released ownership.
+
+ @param[in] thd - Thread for which owned GTID set should be updated.
+ @param[in] is_commit - If the thread is being updated by a commit.
+ */
+ void update_gtids_impl_own_gtid_set(THD *thd, bool is_commit);
+ /**
+ Lock a given sidno of a transaction being updated.
+
+ This is a sub task of update_gtids_impl responsible only to lock the
+ sidno of the GTID being updated.
+
+ @param[in] sidno - The sidno to be locked.
+ */
+ void update_gtids_impl_lock_sidno(rpl_sidno sidno);
+ /**
+
+ Locks the sidnos of all the GTIDs of the commit group starting on the
+ transaction passed as parameter.
+
+ This is a sub task of update_commit_group responsible only to lock the
+ sidno(s) of the GTID(s) being updated.
+
+ The function should follow thd->next_to_commit to lock all sidnos of all
+ transactions being updated in a group.
+
+ @param[in] thd - Thread that owns the GTID(s) to be updated or leader
+ of the commit group in the case of a commit group
+ update.
+ */
+ void update_gtids_impl_lock_sidnos(THD *thd);
+ /**
+ Handle the case that the thread own a single non-anonymous GTID.
+
+ This is a sub task of update_gtids_impl responsible only to handle
+ the case of a thread with a single non-anonymous GTID being updated
+ either for commit or rollback.
+
+ - Release ownership of the GTID owned by the THD. This removes
+ the GTID from Owned_gtids and clears the ownership status in the
+ THD object.
+ - Add the owned GTID to executed_gtids if the is_commit flag is set.
+ - Send a broadcast on the condition variable for the sidno which we
+ released ownership.
+
+ @param[in] thd - Thread to be updated that owns single non-anonymous GTID.
+ @param[in] is_commit - If the thread is being updated by a commit.
+ */
+ void update_gtids_impl_own_gtid(THD *thd, bool is_commit);
+ /**
+ Unlock a given sidno after broadcasting its changes.
+
+ This is a sub task of update_gtids_impl responsible only to
+ unlock the sidno of the GTID being updated after broadcasting
+ its changes.
+
+ @param[in] sidno - The sidno to be broadcasted and unlocked.
+ */
+ void update_gtids_impl_broadcast_and_unlock_sidno(rpl_sidno sidno);
+ /**
+ Unlocks all locked sidnos after broadcasting their changes.
+
+ This is a sub task of update_commit_group responsible only to
+ unlock the sidno(s) of the GTID(s) being updated after broadcasting
+ their changes.
+ */
+ void update_gtids_impl_broadcast_and_unlock_sidnos();
+ /**
+ Handle the case that the thread owns ANONYMOUS GTID.
+
+ This is a sub task of update_gtids_impl responsible only to handle
+ the case of a thread with an ANONYMOUS GTID being updated.
+
+ - Release ownership of the anonymous GTID owned by the THD and clears
+ the ownership status in the THD object.
+ - Decrease counters of GTID-violating transactions.
+
+ @param[in] thd - Thread to be updated that owns anonymous GTID.
+ @param[in,out] more_trx - If the 'transaction' has been split into
+ multiple transactions in the binlog.
+ This is firstly assigned with the return of
+ Gtid_state::update_gtids_impl_begin function, and
+ its value can be set to true when
+ Gtid_state::update_gtids_impl_anonymous_gtid
+ detects more content on the transaction cache.
+ */
+ void update_gtids_impl_own_anonymous(THD *thd, bool *more_trx);
+ /**
+ Handle the case that the thread owns nothing.
+
+ This is a sub task of update_gtids_impl responsible only to handle
+ the case of a thread that owns nothing being updated.
+
+ There are two cases when this happens:
+ - Normally, it is a rollback of an automatic transaction, so
+ the is_commit is false and gtid_next=automatic.
+ - There is also a corner case. This case may happen for a transaction
+ that uses GTID_NEXT=AUTOMATIC, and violates GTID_CONSISTENCY, and
+ commits changes to the database, but does not write to the binary log,
+ so that no GTID is generated. An example is CREATE TEMPORARY TABLE
+ inside a transaction when binlog_format=row. Despite the thread does
+ not own anything, the GTID consistency violation makes it necessary to
+ call end_gtid_violating_transaction. Therefore
+ MYSQL_BIN_LOG::gtid_end_transaction will call
+ gtid_state->update_on_commit in this case, and subsequently we will
+ reach this case.
+
+ @param[in] thd - Thread to be updated that owns anonymous GTID.
+ */
+ void update_gtids_impl_own_nothing(THD *thd);
+ /**
+ Handle the final part of update_gtids_impl.
+
+ This is a sub task of update_gtids_impl responsible only to handle
+ the call to end_gtid_violating_transaction function when there is no
+ more transactions split after the current transaction.
+
+ @param[in] thd - Thread for which owned GTID is updated.
+ @param[in] more_trx - This is the value returned from
+ Gtid_state::update_gtids_impl_begin and can be
+ changed for transactions owning anonymous GTID at
+ Gtid_state::update_gtids_impl_own_anonymous.
+ */
+ void update_gtids_impl_end(THD *thd, bool more_trx);
+ /**
+ This array is used by Gtid_state_update_gtids_impl* functions.
+
+ The array items (one per sidno of the sid_map) will be set as true for
+ each sidno that requires to be locked when updating a set of GTIDs
+ (at Gtid_set::update_gtids_impl_lock_sidnos).
+
+ The array items will be set false at
+ Gtid_set::update_gtids_impl_broadcast_and_unlock_sidnos.
+
+ It is used to so that lock, unlock, and broadcast operations are only
+ called once per sidno per commit group, instead of once per transaction.
+
+ Its access is protected by:
+ - global_sid_lock->wrlock when growing and cleaning up;
+ - MYSQL_BIN_LOG::LOCK_commit when setting true/false on array items.
+ */
+ Prealloced_array<bool, 8> commit_group_sidnos;
+ /**
+ Ensure that commit_group_sidnos have room for the SIDNO passed as
+ parameter.
+
+ This function must only be called in one place:
+ Gtid_state::ensure_sidno().
+
+ @param sidno The SIDNO.
+ @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+ */
+ enum_return_status ensure_commit_group_sidnos(rpl_sidno sidno);
+};
+
+/*
+ BUG# #18089914 - REFACTORING: RENAME GROUP TO GTID
+ changed AUTOMATIC_GROUP to AUTOMATIC_GTID
+ changed ANONYMOUS_GROUP to ANONYMOUS_GTID
+ changed INVALID_GROUP to INVALID_GTID
+ changed UNDEFINED_GROUP to UNDEFINED_GTID
+ changed GTID_GROUPto ASSIGNED_GTID
+ changed NOT_YET_DETERMINED_GROUP to NOT_YET_DETERMINED_GTID
+*/
+
+/**
+ Enumeration of different types of values for Gtid_specification,
+ i.e, the different internal states that @@session.gtid_next can be in.
+*/
+enum enum_gtid_type {
+ /**
+ Specifies that the GTID has not been generated yet; it will be
+ generated on commit. It will depend on the GTID_MODE: if
+ GTID_MODE<=OFF_PERMISSIVE, then the transaction will be anonymous;
+ if GTID_MODE>=ON_PERMISSIVE, then the transaction will be assigned
+ a new GTID.
+
+ This is the default value: thd->variables.gtid_next has this state
+ when GTID_NEXT="AUTOMATIC".
+
+ It is important that AUTOMATIC_GTID==0 so that the default value
+ for thd->variables->gtid_next.type is AUTOMATIC_GTID.
+ */
+ AUTOMATIC_GTID = 0,
+ /**
+ Specifies that the transaction has been assigned a GTID (UUID:NUMBER).
+
+ thd->variables.gtid_next has this state when GTID_NEXT="UUID:NUMBER".
+
+ This is the state of GTID-transactions replicated to the slave.
+ */
+ ASSIGNED_GTID,
+ /**
+ Specifies that the transaction is anonymous, i.e., it does not
+ have a GTID and will never be assigned one.
+
+ thd->variables.gtid_next has this state when GTID_NEXT="ANONYMOUS".
+
+ This is the state of any transaction generated on a pre-GTID
+ server, or on a server with GTID_MODE==OFF.
+ */
+ ANONYMOUS_GTID,
+ /**
+ GTID_NEXT is set to this state after a transaction with
+ GTID_NEXT=='UUID:NUMBER' is committed.
+
+ This is used to protect against a special case of unsafe
+ non-transactional updates.
+
+ Background: Non-transactional updates are allowed as long as they
+ are sane. Non-transactional updates must be single-statement
+ transactions; they must not be mixed with transactional updates in
+ the same statement or in the same transaction. Since
+ non-transactional updates must be logged separately from
+ transactional updates, a single mixed statement would generate two
+ different transactions.
+
+ Problematic case: Consider a transaction, Tx1, that updates two
+ transactional tables on the master, t1 and t2. Then slave (s1) later
+ replays Tx1. However, t2 is a non-transactional table at s1. As such, s1
+ will report an error because it cannot split Tx1 into two different
+ transactions. Had no error been reported, then Tx1 would be split into Tx1
+ and Tx2, potentially causing severe harm in case some form of fail-over
+ procedure is later engaged by s1.
+
+ To detect this case on the slave and generate an appropriate error
+ message rather than causing an inconsistency in the GTID state, we
+ do as follows. When committing a transaction that has
+ GTID_NEXT==UUID:NUMBER, we set GTID_NEXT to UNDEFINED_GTID. When
+ the next part of the transaction is being processed, an error is
+ generated, because it is not allowed to execute a transaction when
+ GTID_NEXT==UNDEFINED. In the normal case, the error is not
+ generated, because there will always be a Gtid_log_event after the
+ next transaction.
+ */
+ UNDEFINED_GTID,
+ /*
+ GTID_NEXT is set to this state by the slave applier thread when it
+ reads a Format_description_log_event that does not originate from
+ this server.
+
+ Background: when the slave applier thread reads a relay log that
+ comes from a pre-GTID master, it must preserve the transactions as
+ anonymous transactions, even if GTID_MODE>=ON_PERMISSIVE. This
+ may happen, e.g., if the relay log was received when master and
+ slave had GTID_MODE=OFF or when master and slave were old, and the
+ relay log is applied when slave has GTID_MODE>=ON_PERMISSIVE.
+
+ So the slave thread should set GTID_NEXT=ANONYMOUS for the next
+ transaction when it starts to process an old binary log. However,
+ there is no way for the slave to tell if the binary log is old,
+ until it sees the first transaction. If the first transaction
+ begins with a Gtid_log_event, we have the GTID there; if it begins
+ with query_log_event, row events, etc, then this is an old binary
+log. So at the time the binary log begins, we just set
+ GTID_NEXT=NOT_YET_DETERMINED_GTID. If it remains
+ NOT_YET_DETERMINED when the next transaction begins,
+ gtid_pre_statement_checks will automatically turn it into an
+ anonymous transaction. If a Gtid_log_event comes across before
+ the next transaction starts, then the Gtid_log_event will just set
+ GTID_NEXT='UUID:NUMBER' accordingly.
+ */
+ NOT_YET_DETERMINED_GTID
+};
+/// Global state of GTIDs.
+extern Gtid_state *gtid_state;
+
+/**
+ This struct represents a specification of a GTID for a statement to
+ be executed: either "AUTOMATIC", "ANONYMOUS", or "SID:GNO".
+
+ This is a POD. It has to be a POD because it is used in THD::variables.
+*/
+struct Gtid_specification {
+ /// The type of this GTID
+ enum_gtid_type type;
+ /**
+ The GTID:
+ { SIDNO, GNO } if type == GTID;
+ { 0, 0 } if type == AUTOMATIC or ANONYMOUS.
+ */
+ Gtid gtid;
+ /// Set the type to ASSIGNED_GTID and SID, GNO to the given values.
+ void set(rpl_sidno sidno, rpl_gno gno) {
+ gtid.set(sidno, gno);
+ type = ASSIGNED_GTID;
+ }
+ /// Set the type to ASSIGNED_GTID and SID, GNO to the given Gtid.
+ void set(const Gtid &gtid_param) { set(gtid_param.sidno, gtid_param.gno); }
+ /// Set the type to AUTOMATIC_GTID.
+ void set_automatic() { type = AUTOMATIC_GTID; }
+ /// Set the type to ANONYMOUS_GTID.
+ void set_anonymous() { type = ANONYMOUS_GTID; }
+ /// Set the type to NOT_YET_DETERMINED_GTID.
+ void set_not_yet_determined() { type = NOT_YET_DETERMINED_GTID; }
+ /// Set to undefined. Must only be called if the type is ASSIGNED_GTID.
+ void set_undefined() {
+ DBUG_ASSERT(type == ASSIGNED_GTID);
+ type = UNDEFINED_GTID;
+ }
+ /// Return true if this Gtid_specification is equal to 'other'.
+ bool equals(const Gtid_specification &other) const {
+ return (type == other.type &&
+ (type != ASSIGNED_GTID || gtid.equals(other.gtid)));
+ }
+ /**
+ Return true if this Gtid_specification is a ASSIGNED_GTID with the
+ same SID, GNO as 'other_gtid'.
+ */
+ bool equals(const Gtid &other_gtid) const {
+ return type == ASSIGNED_GTID && gtid.equals(other_gtid);
+ }
+#ifdef MYSQL_SERVER
+ /**
+ Parses the given string and stores in this Gtid_specification.
+
+ @param sid_map sid_map to use when converting SID to a sidno.
+ @param text The text to parse
+ @return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
+ */
+ enum_return_status parse(Sid_map *sid_map, const char *text);
+ /// Returns true if the given string is a valid Gtid_specification.
+ static bool is_valid(const char *text);
+#endif
+ static const int MAX_TEXT_LENGTH = Gtid::MAX_TEXT_LENGTH;
+ /**
+ Writes this Gtid_specification to the given string buffer.
+
+ @param sid_map Sid_map to use if the type of this
+ Gtid_specification is ASSIGNED_GTID.
+ @param [out] buf The buffer
+ @param need_lock If true, this function acquires global_sid_lock
+ before looking up the sidno in sid_map, and then releases it. If
+ false, this function asserts that the lock is held by the caller.
+ @retval The number of characters written.
+ */
+ int to_string(const Sid_map *sid_map, char *buf,
+ bool need_lock = false) const;
+ /**
+ Writes this Gtid_specification to the given string buffer.
+
+ @param sid SID to use if the type of this Gtid_specification is
+ ASSIGNED_GTID. Can be NULL if this Gtid_specification is
+ ANONYMOUS_GTID or AUTOMATIC_GTID.
+ @param[out] buf The buffer
+ @retval The number of characters written.
+ */
+ int to_string(const rpl_sid *sid, char *buf) const;
+#ifndef DBUG_OFF
+ /// Debug only: print this Gtid_specification to stdout.
+ void print() const {
+ char buf[MAX_TEXT_LENGTH + 1];
+ to_string(global_sid_map, buf);
+ printf("%s\n", buf);
+ }
+#endif
+ /**
+ Print this Gtid_specification to the trace file if debug is
+ enabled; no-op otherwise.
+ */
+ void dbug_print(const char *text MY_ATTRIBUTE((unused)) = "",
+ bool need_lock MY_ATTRIBUTE((unused)) = false) const {
+#ifndef DBUG_OFF
+ char buf[MAX_TEXT_LENGTH + 1];
+ to_string(global_sid_map, buf, need_lock);
+ DBUG_PRINT("info", ("%s%s%s", text, *text ? ": " : "", buf));
+#endif
+ }
+};
+
+/**
+ Indicates if a statement should be skipped or not. Used as return
+ value from gtid_before_statement.
+*/
+enum enum_gtid_statement_status {
+ /// Statement can execute.
+ GTID_STATEMENT_EXECUTE,
+ /// Statement should be cancelled.
+ GTID_STATEMENT_CANCEL,
+ /**
+ Statement should be skipped, but there may be an implicit commit
+ after the statement if gtid_commit is set.
+ */
+ GTID_STATEMENT_SKIP
+};
+
+#ifdef MYSQL_SERVER
+/**
+ Perform GTID-related checks before executing a statement:
+
+ - Check that the current statement does not contradict
+ enforce_gtid_consistency.
+
+ - Check that there is no implicit commit in a transaction when
+ GTID_NEXT==UUID:NUMBER.
+
+ - Change thd->variables.gtid_next.type to ANONYMOUS_GTID if it is
+ currently NOT_YET_DETERMINED_GTID.
+
+ - Check whether the statement should be cancelled.
+
+ @param thd THD object for the session.
+
+ @retval GTID_STATEMENT_EXECUTE The normal case: the checks
+ succeeded, and statement can execute.
+
+ @retval GTID_STATEMENT_CANCEL The checks failed; an
+ error has be generated and the statement must stop.
+
+ @retval GTID_STATEMENT_SKIP The checks succeeded, but the GTID has
+ already been executed (exists in GTID_EXECUTED). So the statement
+ must not execute; however, if there are implicit commits, then the
+ implicit commits must execute.
+*/
+enum_gtid_statement_status gtid_pre_statement_checks(THD *thd);
+
+/**
+ Perform GTID-related checks before executing a statement, but after
+ executing an implicit commit before the statement, if any:
+
+ If gtid_next=anonymous, but the thread does not hold anonymous
+ ownership, then acquire anonymous ownership. (Do this only if this
+ is not an 'innocent' statement, i.e., SET/SHOW/DO/SELECT that does
+ not invoke a stored function.)
+
+ It is important that this is done after the implicit commit, because
+ the implicit commit may release anonymous ownership.
+
+ @param thd THD object for the session
+
+ @retval false Success.
+
+ @retval true Error. Error can happen if GTID_MODE=ON. The error has
+ been reported by (a function called by) this function.
+*/
+bool gtid_pre_statement_post_implicit_commit_checks(THD *thd);
+
+/**
+ Acquire ownership of the given Gtid_specification.
+
+ The Gtid_specification must be of type ASSIGNED_GTID or ANONYMOUS_GTID.
+
+ The caller must hold global_sid_lock (normally the rdlock). The
+ lock may be temporarily released and acquired again. In the end,
+ the lock will be released, so the caller should *not* release the
+ lock.
+
+ The function will try to acquire ownership of the GTID and update
+ both THD::gtid_next, Gtid_state::owned_gtids, and
+ THD::owned_gtid / THD::owned_sid.
+
+ @param thd The thread that acquires ownership.
+
+ @param spec The Gtid_specification.
+
+ @retval false Success: either we have acquired ownership of the
+ GTID, or it is already included in GTID_EXECUTED and will be
+ skipped.
+
+ @retval true Failure; the thread was killed or an error occurred.
+ The error has been reported using my_error.
+*/
+bool set_gtid_next(THD *thd, const Gtid_specification &spec);
+#ifdef HAVE_GTID_NEXT_LIST
+int gtid_acquire_ownership_multiple(THD *thd);
+#endif
+
+/**
+ Return sidno for a given sid, see Sid_map::add_sid() for details.
+*/
+rpl_sidno get_sidno_from_global_sid_map(rpl_sid sid);
+
+/**
+ Return last gno for a given sidno, see
+ Gtid_state::get_last_executed_gno() for details.
+*/
+rpl_gno get_last_executed_gno(rpl_sidno sidno);
+
+void gtid_set_performance_schema_values(const THD *thd);
+
+/**
+ If gtid_next=ANONYMOUS or NOT_YET_DETERMINED, but the thread does
+ not hold anonymous ownership, acquire anonymous ownership.
+
+ @param thd Thread.
+
+ @retval true Error (can happen if gtid_mode=ON and
+ gtid_next=anonymous). The error has already been reported using
+ my_error.
+
+ @retval false Success.
+*/
+bool gtid_reacquire_ownership_if_anonymous(THD *thd);
+
+/**
+ The function commits or rolls back the gtid state if it needs to.
+ It's supposed to be invoked at the end of transaction commit or
+ rollback, as well as as at the end of XA prepare.
+
+ @param thd Thread context
+ @param needs_to The actual work will be done when the parameter is true
+ @param do_commit When true the gtid state changes are committed, otherwise
+ they are rolled back.
+*/
+
+inline void gtid_state_commit_or_rollback(THD *thd, bool needs_to,
+ bool do_commit) {
+ if (needs_to) {
+ if (do_commit)
+ gtid_state->update_on_commit(thd);
+ else
+ gtid_state->update_on_rollback(thd);
+ }
+}
+
+#endif // ifdef MYSQL_SERVER
+
+/**
+ An optimized way of checking GTID_MODE without acquiring locks every time.
+
+ GTID_MODE is a global variable that should not be changed often, but the
+ access to it is protected by any of the four locks described at
+ enum_gtid_mode_lock.
+
+ Every time a channel receiver thread connects to a master, and every time
+ a Gtid_log_event or an Anonymous_gtid_log_event is queued by a receiver
+ thread, there must be checked if the current GTID_MODE is compatible with
+ the operation.
+
+ There are some places where the verification is performed while already
+ holding one of the above mentioned locks, but there are other places that
+ rely on no lock and will rely on the global_sid_lock, blocking any other
+ GTID operation relying on the global_sid_map.
+
+ In order to avoid acquiring lock to check a variable that is not changed
+ often, there is a global (atomic) counter of how many times the GTID_MODE
+ was changed since the server startup.
+
+ This class holds a copy of the last GTID_MODE to be returned without the
+ need of acquiring locks if the local GTID mode counter has the same value
+ as the global atomic counter.
+*/
+class Gtid_mode_copy {
+ public:
+ /**
+ Return the current server GTID_MODE without acquiring locks if possible.
+
+ @param have_lock The lock type held by the caller.
+ */
+ enum_gtid_mode get_gtid_mode_from_copy(enum_gtid_mode_lock have_lock) {
+ ulong current_gtid_mode_counter = gtid_mode_counter;
+ // Update out copy of GTID_MODE if needed
+ if (m_gtid_mode_counter != current_gtid_mode_counter) {
+ m_gtid_mode = get_gtid_mode(have_lock);
+ m_gtid_mode_counter = current_gtid_mode_counter;
+ }
+ return m_gtid_mode;
+ }
+
+ private:
+ /// The copy of the atomic counter of the last time we copied the GTID_MODE
+ ulong m_gtid_mode_counter = 0;
+ /// Local copy of the GTID_MODE
+ enum_gtid_mode m_gtid_mode = DEFAULT_GTID_MODE;
+};
+
+#endif /* RPL_GTID_H_INCLUDED */
diff --git a/contrib/libs/libmysql_r/sql/rpl_reporting.h b/contrib/libs/libmysql_r/sql/rpl_reporting.h
new file mode 100644
index 0000000000..46f4b319bc
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/rpl_reporting.h
@@ -0,0 +1,175 @@
+/* Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef RPL_REPORTING_H
+#define RPL_REPORTING_H
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include "my_compiler.h"
+#include "my_inttypes.h"
+#include "my_loglevel.h"
+#include "my_systime.h" //my_getsystime
+#include "mysql/components/services/mysql_mutex_bits.h"
+#include "mysql/psi/mysql_mutex.h"
+
+/**
+ Maximum size of an error message from a slave thread.
+ */
+#define MAX_SLAVE_ERRMSG 1024
+
+class THD;
+
+/**
+ Mix-in to handle the message logging and reporting for relay log
+ info and master log info structures.
+
+ By inheriting from this class, the class is imbued with
+ capabilities to do slave reporting.
+ */
+class Slave_reporting_capability {
+ public:
+ /** lock used to synchronize m_last_error on 'SHOW SLAVE STATUS' **/
+ mutable mysql_mutex_t err_lock;
+ /**
+ Constructor.
+
+ @param thread_name Printable name of the slave thread that is reporting.
+ */
+ Slave_reporting_capability(char const *thread_name);
+
+ /**
+ Writes a message and, if it's an error message, to Last_Error
+ (which will be displayed by SHOW SLAVE STATUS).
+
+ @param level The severity level
+ @param err_code The error code
+ @param msg The message (usually related to the error
+ code, but can contain more information), in
+ printf() format.
+ */
+ virtual void report(loglevel level, int err_code, const char *msg, ...) const
+ MY_ATTRIBUTE((format(printf, 4, 5)));
+ void va_report(loglevel level, int err_code, const char *prefix_msg,
+ const char *msg, va_list v_args) const
+ MY_ATTRIBUTE((format(printf, 5, 0)));
+
+ /**
+ Clear errors. They will not show up under <code>SHOW SLAVE
+ STATUS</code>.
+ */
+ void clear_error() {
+ mysql_mutex_lock(&err_lock);
+ m_last_error.clear();
+ mysql_mutex_unlock(&err_lock);
+ }
+
+ /**
+ Check if the current error is of temporary nature or not.
+ */
+ int has_temporary_error(THD *thd, uint error_arg = 0,
+ bool *silent = nullptr) const;
+
+ /**
+ Error information structure.
+ */
+ class Error {
+ friend class Slave_reporting_capability;
+
+ public:
+ Error() { clear(); }
+
+ void clear() {
+ number = 0;
+ message[0] = '\0';
+ timestamp[0] = '\0';
+ }
+
+ void update_timestamp() {
+ struct tm tm_tmp;
+ struct tm *start;
+ time_t tt_tmp;
+
+ skr = my_getsystime() / 10;
+ tt_tmp = skr / 1000000;
+ localtime_r(&tt_tmp, &tm_tmp);
+ start = &tm_tmp;
+
+ snprintf(timestamp, sizeof(timestamp), "%02d%02d%02d %02d:%02d:%02d",
+ start->tm_year % 100, start->tm_mon + 1, start->tm_mday,
+ start->tm_hour, start->tm_min, start->tm_sec);
+ timestamp[15] = '\0';
+ }
+
+ /** Error code */
+ uint32 number;
+ /** Error message */
+ char message[MAX_SLAVE_ERRMSG];
+ /** Error timestamp as string */
+ char timestamp[64];
+ /** Error timestamp in microseconds. Used in performance_schema */
+ ulonglong skr;
+ };
+
+ Error const &last_error() const { return m_last_error; }
+ bool is_error() const { return last_error().number != 0; }
+
+ /*
+ For MSR, there is a need to introduce error messages per channel.
+ Instead of changing the error messages in share/errmsg-utf8.txt to
+ introduce the clause, FOR CHANNEL "%s", we construct a string like this.
+ There might be problem with a client applications which could print
+ error messages and see no %s.
+ @TODO: fix this.
+ */
+ virtual const char *get_for_channel_str(bool upper_case) const = 0;
+
+ virtual ~Slave_reporting_capability() = 0;
+
+ protected:
+ virtual void do_report(loglevel level, int err_code, const char *msg,
+ va_list v_args) const
+ MY_ATTRIBUTE((format(printf, 4, 0)));
+
+ /**
+ Last error produced by the I/O or SQL thread respectively.
+ */
+ mutable Error m_last_error;
+
+ private:
+ char const *const m_thread_name;
+
+ // not implemented
+ Slave_reporting_capability(const Slave_reporting_capability &rhs);
+ Slave_reporting_capability &operator=(const Slave_reporting_capability &rhs);
+};
+
+inline void Slave_reporting_capability::do_report(loglevel level, int err_code,
+ const char *msg,
+ va_list v_args) const {
+ va_report(level, err_code, nullptr, msg, v_args);
+}
+
+#endif // RPL_REPORTING_H
diff --git a/contrib/libs/libmysql_r/sql/rpl_tblmap.h b/contrib/libs/libmysql_r/sql/rpl_tblmap.h
new file mode 100644
index 0000000000..518a77f197
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/rpl_tblmap.h
@@ -0,0 +1,99 @@
+/* Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef TABLE_MAPPING_H
+#define TABLE_MAPPING_H
+
+#include <sys/types.h>
+
+#include "map_helpers.h"
+#include "my_alloc.h"
+#include "my_inttypes.h"
+
+/* Forward declarations */
+#ifdef MYSQL_SERVER
+struct TABLE;
+
+typedef TABLE Mapped_table;
+#else
+class Table_map_log_event;
+
+typedef Table_map_log_event Mapped_table;
+#endif
+
+/**
+ Maps table id's (integers) to table pointers.
+
+ In mysqlbinlog, "table pointer" means Table_map_log_event*.
+
+ In the server, "table pointer" means TABLE*.
+*/
+class table_mapping {
+ private:
+ MEM_ROOT m_mem_root;
+
+ public:
+ enum enum_error {
+ ERR_NO_ERROR = 0,
+ ERR_LIMIT_EXCEEDED,
+ ERR_MEMORY_ALLOCATION
+ };
+
+ table_mapping();
+ ~table_mapping();
+
+ Mapped_table *get_table(ulonglong table_id);
+
+ int set_table(ulonglong table_id, Mapped_table *table);
+ int remove_table(ulonglong table_id);
+ void clear_tables();
+ ulong count() const { return static_cast<ulong>(m_table_ids.size()); }
+
+ private:
+ struct entry {
+ ulonglong table_id;
+ union {
+ Mapped_table *table;
+ entry *next;
+ };
+ };
+
+ int expand();
+
+ /*
+ Head of the list of free entries; "free" in the sense that it's an
+ allocated entry free for use, NOT in the sense that it's freed
+ memory.
+ */
+ entry *m_free;
+
+ /*
+ Map from table ids (numbers) to Mapped_table objects.
+
+ No destructor for entries passed here, as the entries are allocated in a
+ MEM_ROOT (freed as a whole in the destructor), they cannot be freed one by
+ one.
+ */
+ malloc_unordered_map<ulonglong, entry *> m_table_ids;
+};
+
+#endif
diff --git a/contrib/libs/libmysql_r/sql/rpl_utility.h b/contrib/libs/libmysql_r/sql/rpl_utility.h
new file mode 100644
index 0000000000..57b5c6efa2
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/rpl_utility.h
@@ -0,0 +1,531 @@
+/* Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef RPL_UTILITY_H
+#define RPL_UTILITY_H
+
+#ifndef __cplusplus
+#error "Don't include this C++ header file from a non-C++ file!"
+#endif
+
+#include <sys/types.h>
+#include <unordered_map>
+
+#include "field_types.h" // enum_field_types
+#include "my_dbug.h"
+#include "my_inttypes.h"
+#include "my_macros.h"
+#include "sql/psi_memory_key.h"
+
+struct MY_BITMAP;
+
+#ifdef MYSQL_SERVER
+#include <memory>
+
+#include "map_helpers.h"
+#include "prealloced_array.h" // Prealloced_array
+#error #include "sql/table.h" // TABLE_LIST
+
+class Log_event;
+class Relay_log_info;
+class THD;
+
+/**
+ Hash table used when applying row events on the slave and there is
+ no index on the slave's table.
+ */
+
+struct HASH_ROW_POS {
+ /**
+ Points at the position where the row starts in the
+ event buffer (ie, area in memory before unpacking takes
+ place).
+ */
+ const uchar *bi_start;
+ const uchar *bi_ends;
+};
+
+struct HASH_ROW_ENTRY;
+
+struct hash_slave_rows_free_entry {
+ void operator()(HASH_ROW_ENTRY *entry) const;
+};
+
+/**
+ Internal structure that acts as a preamble for HASH_ROW_POS
+ in memory structure.
+
+ Allocation is done in Hash_slave_rows::make_entry as part of
+ the entry allocation.
+ */
+struct HASH_ROW_PREAMBLE {
+ HASH_ROW_PREAMBLE() = default;
+ /*
+ The actual key.
+ */
+ uint hash_value;
+
+ /**
+ The search state used to iterate over multiple entries for a
+ given key.
+ */
+ malloc_unordered_multimap<
+ uint, std::unique_ptr<HASH_ROW_ENTRY, hash_slave_rows_free_entry>>::
+ const_iterator search_state;
+
+ /**
+ Wether this search_state is usable or not.
+ */
+ bool is_search_state_inited;
+};
+
+struct HASH_ROW_ENTRY {
+ HASH_ROW_PREAMBLE *preamble;
+ HASH_ROW_POS *positions;
+};
+
+class Hash_slave_rows {
+ public:
+ /**
+ Allocates an empty entry to be added to the hash table.
+ It should be called before calling member function @c put.
+
+ @returns NULL if a problem occurred, a valid pointer otherwise.
+ */
+ HASH_ROW_ENTRY *make_entry();
+
+ /**
+ Allocates an entry to be added to the hash table. It should be
+ called before calling member function @c put.
+
+ @param bi_start the position to where in the rows buffer the
+ before image begins.
+ @param bi_ends the position to where in the rows buffer the
+ before image ends.
+ @returns NULL if a problem occurred, a valid pointer otherwise.
+ */
+ HASH_ROW_ENTRY *make_entry(const uchar *bi_start, const uchar *bi_ends);
+
+ /**
+ Puts data into the hash table. It calculates the key taking
+ the data on @c TABLE::record as the input for hash computation.
+
+ @param table The table holding the buffer used to calculate the
+ key, ie, table->record[0].
+ @param cols The read_set bitmap signaling which columns are used.
+ @param entry The entry with the values to store.
+
+ @returns true if something went wrong, false otherwise.
+ */
+ bool put(TABLE *table, MY_BITMAP *cols, HASH_ROW_ENTRY *entry);
+
+ /**
+ Gets the entry, from the hash table, that matches the data in
+ table->record[0] and signaled using cols.
+
+ @param table The table holding the buffer containing data used to
+ make the entry lookup.
+ @param cols Bitmap signaling which columns, from
+ table->record[0], should be used.
+
+ @returns a pointer that will hold a reference to the entry
+ found. If the entry is not found then NULL shall be
+ returned.
+ */
+ HASH_ROW_ENTRY *get(TABLE *table, MY_BITMAP *cols);
+
+ /**
+ Gets the entry that stands next to the one pointed to by
+ *entry. Before calling this member function, the entry that one
+ uses as parameter must have: 1. been obtained through get() or
+ next() invocations; and 2. must have not been used before in a
+ next() operation.
+
+ @param[in,out] entry contains a pointer to an entry that we can
+ use to search for another adjacent entry
+ (ie, that shares the same key).
+
+ @returns true if something went wrong, false otherwise. In the
+ case that this entry was already used in a next()
+ operation this member function returns true and does not
+ update the pointer.
+ */
+ bool next(HASH_ROW_ENTRY **entry);
+
+ /**
+ Deletes the entry pointed by entry. It also frees memory used
+ holding entry contents. This is the way to release memeory
+ used for entry, freeing it explicitly with my_free will cause
+ undefined behavior.
+
+ @param entry Pointer to the entry to be deleted.
+ @returns true if something went wrong, false otherwise.
+ */
+ bool del(HASH_ROW_ENTRY *entry);
+
+ /**
+ Initializes the hash table.
+
+ @returns true if something went wrong, false otherwise.
+ */
+ bool init(void);
+
+ /**
+ De-initializes the hash table.
+
+ @returns true if something went wrong, false otherwise.
+ */
+ bool deinit(void);
+
+ /**
+ Checks if the hash table is empty or not.
+
+ @returns true if the hash table has zero entries, false otherwise.
+ */
+ bool is_empty(void);
+
+ /**
+ Returns the number of entries in the hash table.
+
+ @returns the number of entries in the hash table.
+ */
+ int size();
+
+ private:
+ /**
+ The hashtable itself.
+ */
+ malloc_unordered_multimap<
+ uint, std::unique_ptr<HASH_ROW_ENTRY, hash_slave_rows_free_entry>>
+ m_hash{key_memory_HASH_ROW_ENTRY};
+
+ /**
+ Auxiliary and internal method used to create an hash key, based on
+ the data in table->record[0] buffer and signaled as used in cols.
+
+ @param table The table that is being scanned
+ @param cols The read_set bitmap signaling which columns are used.
+
+ @returns the hash key created.
+ */
+ uint make_hash_key(TABLE *table, MY_BITMAP *cols);
+};
+
+#endif
+
+/**
+ A table definition from the master.
+
+ The responsibilities of this class is:
+ - Extract and decode table definition data from the table map event
+ - Check if table definition in table map is compatible with table
+ definition on slave
+ - expose the type information so that it can be used when encoding
+ or decoding row event data.
+*/
+class table_def {
+ public:
+ /**
+ No-op constructor. Instances of RPL_TABLE_LIST are created by first
+ allocating memory, then placement-new-ing an RPL_TABLE_LIST object
+ containing an uninitialized table_def object which is only conditionally
+ initialized. See Table_map_log_event::do_apply_event().
+ */
+ table_def() {}
+
+ /**
+ Constructor.
+
+ @param types Array of types, each stored as a byte
+ @param size Number of elements in array 'types'
+ @param field_metadata Array of extra information about fields
+ @param metadata_size Size of the field_metadata array
+ @param null_bitmap The bitmap of fields that can be null
+ @param flags Table flags
+ */
+ table_def(unsigned char *types, ulong size, uchar *field_metadata,
+ int metadata_size, uchar *null_bitmap, uint16 flags);
+
+ ~table_def();
+
+ /**
+ Return the number of fields there is type data for.
+
+ @return The number of fields that there is type data for.
+ */
+ ulong size() const { return m_size; }
+
+ /*
+ Returns internal binlog type code for one field,
+ without translation to real types.
+ */
+ enum_field_types binlog_type(ulong index) const {
+ return static_cast<enum_field_types>(m_type[index]);
+ }
+
+ /// Return the number of JSON columns in this table.
+ int json_column_count() const {
+ // Cache in member field to make successive calls faster.
+ if (m_json_column_count == -1) {
+ int c = 0;
+ for (uint i = 0; i < size(); i++)
+ if (type(i) == MYSQL_TYPE_JSON) c++;
+ m_json_column_count = c;
+ }
+ return m_json_column_count;
+ }
+
+ /*
+ Return a representation of the type data for one field.
+
+ @param index Field index to return data for
+
+ @return Will return a representation of the type data for field
+ <code>index</code>. Currently, only the type identifier is
+ returned.
+ */
+ enum_field_types type(ulong index) const {
+ DBUG_ASSERT(index < m_size);
+ /*
+ If the source type is MYSQL_TYPE_STRING, it can in reality be
+ either MYSQL_TYPE_STRING, MYSQL_TYPE_ENUM, or MYSQL_TYPE_SET, so
+ we might need to modify the type to get the real type.
+ */
+ enum_field_types source_type = binlog_type(index);
+ uint source_metadata = m_field_metadata[index];
+ switch (source_type) {
+ case MYSQL_TYPE_STRING: {
+ int real_type = source_metadata >> 8;
+ if (real_type == MYSQL_TYPE_ENUM || real_type == MYSQL_TYPE_SET)
+ source_type = static_cast<enum_field_types>(real_type);
+ break;
+ }
+
+ /*
+ This type has not been used since before row-based replication,
+ so we can safely assume that it really is MYSQL_TYPE_NEWDATE.
+ */
+ case MYSQL_TYPE_DATE:
+ source_type = MYSQL_TYPE_NEWDATE;
+ break;
+
+ default:
+ /* Do nothing */
+ break;
+ }
+
+ return source_type;
+ }
+
+ /*
+ This function allows callers to get the extra field data from the
+ table map for a given field. If there is no metadata for that field
+ or there is no extra metadata at all, the function returns 0.
+
+ The function returns the value for the field metadata for column at
+ position indicated by index. As mentioned, if the field was a type
+ that stores field metadata, that value is returned else zero (0) is
+ returned. This method is used in the unpack() methods of the
+ corresponding fields to properly extract the data from the binary log
+ in the event that the master's field is smaller than the slave.
+ */
+ uint field_metadata(uint index) const {
+ DBUG_ASSERT(index < m_size);
+ if (m_field_metadata_size)
+ return m_field_metadata[index];
+ else
+ return 0;
+ }
+
+ /**
+ Returns whether or not the field at `index` is a typed array.
+ */
+ bool is_array(uint index) const {
+ DBUG_ASSERT(index < m_size);
+ if (m_field_metadata_size)
+ return m_is_array[index];
+ else
+ return false;
+ }
+
+ /*
+ This function returns whether the field on the master can be null.
+ This value is derived from field->maybe_null().
+ */
+ bool maybe_null(uint index) const {
+ DBUG_ASSERT(index < m_size);
+ return ((m_null_bits[(index / 8)] & (1 << (index % 8))) ==
+ (1 << (index % 8)));
+ }
+
+ /*
+ This function returns the field size in raw bytes based on the type
+ and the encoded field data from the master's raw data. This method can
+ be used for situations where the slave needs to skip a column (e.g.,
+ WL#3915) or needs to advance the pointer for the fields in the raw
+ data from the master to a specific column.
+ */
+ uint32 calc_field_size(uint col, const uchar *master_data) const;
+
+#ifdef MYSQL_SERVER
+ /**
+ Decide if the table definition is compatible with a table.
+
+ Compare the definition with a table to see if it is compatible
+ with it.
+
+ A table definition is compatible with a table if:
+ - The columns types of the table definition is a (not
+ necessarily proper) prefix of the column type of the table.
+
+ - The other way around.
+
+ - Each column on the master that also exists on the slave can be
+ converted according to the current settings of @c
+ SLAVE_TYPE_CONVERSIONS.
+
+ @param thd Current thread
+ @param rli Pointer to relay log info
+ @param table Pointer to table to compare with.
+
+ @param[out] conv_table_var Pointer to temporary table for holding
+ conversion table.
+
+ @retval 1 if the table definition is not compatible with @c table
+ @retval 0 if the table definition is compatible with @c table
+ */
+ bool compatible_with(THD *thd, Relay_log_info *rli, TABLE *table,
+ TABLE **conv_table_var) const;
+
+ /**
+ Create a virtual in-memory temporary table structure.
+
+ The table structure has records and field array so that a row can
+ be unpacked into the record for further processing.
+
+ In the virtual table, each field that requires conversion will
+ have a non-NULL value, while fields that do not require
+ conversion will have a NULL value.
+
+ Some information that is missing in the events, such as the
+ character set for string types, are taken from the table that the
+ field is going to be pushed into, so the target table that the data
+ eventually need to be pushed into need to be supplied.
+
+ @param thd Thread to allocate memory from.
+ @param rli Relay log info structure, for error reporting.
+ @param target_table Target table for fields.
+
+ @return A pointer to a temporary table with memory allocated in the
+ thread's memroot, NULL if the table could not be created
+ */
+ TABLE *create_conversion_table(THD *thd, Relay_log_info *rli,
+ TABLE *target_table) const;
+#endif
+
+ private:
+ ulong m_size; // Number of elements in the types array
+ unsigned char *m_type; // Array of type descriptors
+ uint m_field_metadata_size;
+ uint *m_field_metadata;
+ uchar *m_null_bits;
+ uint16 m_flags; // Table flags
+ uchar *m_memory;
+ mutable int m_json_column_count; // Number of JSON columns
+ bool *m_is_array;
+};
+
+#ifdef MYSQL_SERVER
+/**
+ Extend the normal table list with a few new fields needed by the
+ slave thread, but nowhere else.
+ */
+struct RPL_TABLE_LIST : public TABLE_LIST {
+ RPL_TABLE_LIST(const char *db_name_arg, size_t db_length_arg,
+ const char *table_name_arg, size_t table_name_length_arg,
+ const char *alias_arg, enum thr_lock_type lock_type_arg)
+ : TABLE_LIST(nullptr, db_name_arg, db_length_arg, table_name_arg,
+ table_name_length_arg, alias_arg, lock_type_arg) {}
+
+ bool m_tabledef_valid;
+ table_def m_tabledef;
+ TABLE *m_conv_table;
+};
+
+class Deferred_log_events {
+ private:
+ Prealloced_array<Log_event *, 32> m_array;
+
+ public:
+ Deferred_log_events();
+ ~Deferred_log_events();
+ /* queue for exection at Query-log-event time prior the Query */
+ int add(Log_event *ev);
+ bool is_empty();
+ bool execute(Relay_log_info *rli);
+ void rewind();
+};
+
+#endif
+
+/**
+ Decode field metadata from a char buffer (serialized form) into an int
+ (packed form).
+
+ @note On little-endian platforms (e.g Intel) this function effectively
+ inverts order of bytes compared to what Field::save_field_metadata()
+ writes. E.g for MYSQL_TYPE_NEWDECIMAL save_field_metadata writes precision
+ into the first byte and decimals into the second, this function puts
+ precision into the second byte and decimals into the first. This layout
+ is expected by replication code that reads metadata in the uint form.
+ Due to this design feature show_sql_type() can't correctly print
+ immediate output of save_field_metadata(), this function have to be used
+ as translator.
+
+ @param buffer Field metadata, in the character stream form produced by
+ save_field_metadata.
+ @param binlog_type The type of the field, in the form returned by
+ Field::binlog_type and stored in Table_map_log_event.
+ @retval pair where:
+ - the first component is the length of the metadata within 'buffer',
+ i.e., how much the buffer pointer should move forward in order to skip it.
+ - the second component is pair containing:
+ - the metadata, encoded as an 'uint', in the form required by e.g.
+ show_sql_type.
+ - bool indicating whether the field is array (true) or a scalar (false)
+*/
+
+std::pair<my_off_t, std::pair<uint, bool>> read_field_metadata(
+ const uchar *metadata_ptr, enum_field_types type);
+
+// NB. number of printed bit values is limited to sizeof(buf) - 1
+#define DBUG_PRINT_BITSET(N, FRM, BS) \
+ do { \
+ char buf[256]; \
+ uint i; \
+ for (i = 0; i < MY_MIN(sizeof(buf) - 1, (BS)->n_bits); i++) \
+ buf[i] = bitmap_is_set((BS), i) ? '1' : '0'; \
+ buf[i] = '\0'; \
+ DBUG_PRINT((N), ((FRM), buf)); \
+ } while (0)
+
+#endif /* RPL_UTILITY_H */
diff --git a/contrib/libs/libmysql_r/sql/select_lex_visitor.h b/contrib/libs/libmysql_r/sql/select_lex_visitor.h
new file mode 100644
index 0000000000..c40f6ac861
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/select_lex_visitor.h
@@ -0,0 +1,57 @@
+#ifndef SELECT_LEX_VISITOR_INCLUDED
+#define SELECT_LEX_VISITOR_INCLUDED
+/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+ @file select_lex_visitor.h
+ Visitor interface for parse trees.
+*/
+
+class SELECT_LEX_UNIT;
+class SELECT_LEX;
+class Item;
+
+/**
+ Abstract base class for traversing the SELECT_LEX tree. In order to use it,
+ a client defines a subclass, overriding the member functions that visit the
+ objects of interest. If a function returns true, traversal is aborted.
+*/
+class Select_lex_visitor {
+ public:
+ virtual bool visits_in_prefix_order() const { return true; }
+
+ bool visit(SELECT_LEX_UNIT *unit) { return visit_union(unit); }
+ bool visit(SELECT_LEX *select_lex) { return visit_query_block(select_lex); }
+
+ /// Called for all nodes of all expression trees (i.e. Item trees).
+ bool visit(Item *item) { return visit_item(item); }
+
+ virtual ~Select_lex_visitor() = 0;
+
+ protected:
+ virtual bool visit_union(SELECT_LEX_UNIT *) { return false; }
+ virtual bool visit_query_block(SELECT_LEX *) { return false; }
+ virtual bool visit_item(Item *) { return false; }
+};
+
+#endif // SELECT_LEX_VISITOR_INCLUDED
diff --git a/contrib/libs/libmysql_r/sql/sql_bitmap.h b/contrib/libs/libmysql_r/sql/sql_bitmap.h
new file mode 100644
index 0000000000..d8ff5780c0
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/sql_bitmap.h
@@ -0,0 +1,228 @@
+/* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*
+ Implementation of a bitmap type.
+ The idea with this is to be able to handle any constant number of bits but
+ also be able to use 32 or 64 bits bitmaps very efficiently
+*/
+
+#ifndef SQL_BITMAP_INCLUDED
+#define SQL_BITMAP_INCLUDED
+
+#include "m_string.h" // longlong2str
+#include "my_bitmap.h" // MY_BITMAP
+#include "my_byteorder.h" // int8store
+#include "my_dbug.h"
+
+template <uint default_width>
+class Bitmap {
+ MY_BITMAP map;
+ uint32 buffer[(default_width + 31) / 32];
+
+ public:
+ enum { ALL_BITS = default_width };
+ Bitmap() { init(); }
+ Bitmap(const Bitmap &from) { *this = from; }
+ explicit Bitmap(uint prefix_to_set) { init(prefix_to_set); }
+ void init() { bitmap_init(&map, buffer, default_width, 0); }
+ void init(uint prefix_to_set) {
+ init();
+ set_prefix(prefix_to_set);
+ }
+ uint length() const { return default_width; }
+ Bitmap &operator=(const Bitmap &map2) {
+ init();
+ memcpy(buffer, map2.buffer, sizeof(buffer));
+ return *this;
+ }
+ void set_bit(uint n) { bitmap_set_bit(&map, n); }
+ void clear_bit(uint n) { bitmap_clear_bit(&map, n); }
+ void set_prefix(uint n) { bitmap_set_prefix(&map, n); }
+ void set_all() { bitmap_set_all(&map); }
+ void clear_all() { bitmap_clear_all(&map); }
+ void intersect(const Bitmap &map2) { bitmap_intersect(&map, &map2.map); }
+ void intersect(ulonglong map2buff) {
+ // Use a spearate temporary buffer, as bitmap_init() clears all the bits.
+ ulonglong buf2;
+ MY_BITMAP map2;
+
+ bitmap_init(&map2, (uint32 *)&buf2, sizeof(ulonglong) * 8, 0);
+
+ // Store the original bits.
+ if (sizeof(ulonglong) >= 8) {
+ int8store(
+ const_cast<uchar *>(static_cast<uchar *>(static_cast<void *>(&buf2))),
+ map2buff);
+ } else {
+ DBUG_ASSERT(sizeof(buffer) >= 4);
+ int4store(
+ const_cast<uchar *>(static_cast<uchar *>(static_cast<void *>(&buf2))),
+ static_cast<uint32>(map2buff));
+ }
+
+ bitmap_intersect(&map, &map2);
+ }
+ /* Use highest bit for all bits above sizeof(ulonglong)*8. */
+ void intersect_extended(ulonglong map2buff) {
+ intersect(map2buff);
+ if (map.n_bits > sizeof(ulonglong) * 8)
+ bitmap_set_above(
+ &map, sizeof(ulonglong),
+ MY_TEST(map2buff & (1LL << (sizeof(ulonglong) * 8 - 1))));
+ }
+ void subtract(const Bitmap &map2) { bitmap_subtract(&map, &map2.map); }
+ void merge(const Bitmap &map2) { bitmap_union(&map, &map2.map); }
+ bool is_set(uint n) const { return bitmap_is_set(&map, n); }
+ bool is_prefix(uint n) const { return bitmap_is_prefix(&map, n); }
+ bool is_clear_all() const { return bitmap_is_clear_all(&map); }
+ bool is_set_all() const { return bitmap_is_set_all(&map); }
+ bool is_subset(const Bitmap &map2) const {
+ return bitmap_is_subset(&map, &map2.map);
+ }
+ bool is_overlapping(const Bitmap &map2) const {
+ return bitmap_is_overlapping(&map, &map2.map);
+ }
+ bool operator==(const Bitmap &map2) const {
+ return bitmap_cmp(&map, &map2.map);
+ }
+ bool operator!=(const Bitmap &map2) const { return !(*this == map2); }
+ char *print(char *buf) const {
+ char *s = buf;
+ const uchar *e = (uchar *)buffer, *b = e + sizeof(buffer) - 1;
+ while (!*b && b > e) b--;
+ if ((*s = _dig_vec_upper[*b >> 4]) != '0') s++;
+ *s++ = _dig_vec_upper[*b & 15];
+ while (--b >= e) {
+ *s++ = _dig_vec_upper[*b >> 4];
+ *s++ = _dig_vec_upper[*b & 15];
+ }
+ *s = 0;
+ return buf;
+ }
+ ulonglong to_ulonglong() const {
+ if (sizeof(buffer) >= 8)
+ return uint8korr(
+ static_cast<const uchar *>(static_cast<const void *>(buffer)));
+ DBUG_ASSERT(sizeof(buffer) >= 4);
+ return (ulonglong)uint4korr(
+ static_cast<const uchar *>(static_cast<const void *>(buffer)));
+ }
+ uint bits_set() const { return bitmap_bits_set(&map); }
+ uint get_first_set() { return bitmap_get_first_set(&map); }
+};
+
+template <>
+class Bitmap<64> {
+ ulonglong map;
+
+ public:
+ Bitmap<64>() { init(); }
+ enum { ALL_BITS = 64 };
+
+ explicit Bitmap<64>(uint prefix_to_set) { set_prefix(prefix_to_set); }
+ void init() { clear_all(); }
+ void init(uint prefix_to_set) { set_prefix(prefix_to_set); }
+ uint length() const { return 64; }
+ void set_bit(uint n) {
+ DBUG_ASSERT(n < 64);
+ map |= ((ulonglong)1) << n;
+ }
+ void clear_bit(uint n) {
+ DBUG_ASSERT(n < 64);
+ map &= ~(((ulonglong)1) << n);
+ }
+ void set_prefix(uint n) {
+ if (n >= length())
+ set_all();
+ else
+ map = (((ulonglong)1) << n) - 1;
+ }
+ void set_all() { map = ~(ulonglong)0; }
+ void clear_all() { map = (ulonglong)0; }
+ void intersect(const Bitmap<64> &map2) { map &= map2.map; }
+ void intersect(ulonglong map2) { map &= map2; }
+ void intersect_extended(ulonglong map2) { map &= map2; }
+ void subtract(const Bitmap<64> &map2) { map &= ~map2.map; }
+ void merge(const Bitmap<64> &map2) { map |= map2.map; }
+ bool is_set(uint n) const {
+ DBUG_ASSERT(n < 64);
+ return (map & (((ulonglong)1) << n));
+ }
+ bool is_prefix(uint n) const {
+ DBUG_ASSERT(n <= 64);
+ if (n < 64)
+ return map == (((ulonglong)1) << n) - 1;
+ else
+ return map == ~(ulonglong)1;
+ }
+ bool is_clear_all() const { return map == (ulonglong)0; }
+ bool is_set_all() const { return map == ~(ulonglong)0; }
+ bool is_subset(const Bitmap<64> &map2) const { return !(map & ~map2.map); }
+ bool is_overlapping(const Bitmap<64> &map2) const {
+ return (map & map2.map) != 0;
+ }
+ bool operator==(const Bitmap<64> &map2) const { return map == map2.map; }
+ bool operator!=(const Bitmap<64> &map2) const { return !(*this == map2); }
+ char *print(char *buf) const {
+ longlong2str(map, buf, 16);
+ return buf;
+ }
+ ulonglong to_ulonglong() const { return map; }
+ uint get_first_set() {
+ for (uint i = 0; i < ALL_BITS; i++)
+ if (map & (1ULL << i)) return i;
+ return MY_BIT_NONE;
+ }
+};
+
+/* An iterator to quickly walk over bits in unlonglong bitmap. */
+class Table_map_iterator {
+ ulonglong bmp;
+ uint no;
+
+ public:
+ Table_map_iterator(ulonglong t) : bmp(t), no(0) {}
+ int next_bit() {
+ static const char last_bit[16] = {32, 0, 1, 0, 2, 0, 1, 0,
+ 3, 0, 1, 0, 2, 0, 1, 0};
+ uint bit;
+ while ((bit = last_bit[bmp & 0xF]) == 32) {
+ no += 4;
+ bmp = bmp >> 4;
+ if (!bmp) return BITMAP_END;
+ }
+ bmp &= ~(1LL << bit);
+ return no + bit;
+ }
+ enum { BITMAP_END = 64 };
+};
+
+#if MAX_INDEXES <= 64
+typedef Bitmap<64> Key_map; /* Used for finding keys */
+#elif MAX_INDEXES > 255
+#error "MAX_INDEXES values greater than 255 is not supported."
+#else
+typedef Bitmap<((MAX_INDEXES + 7) / 8 * 8)> Key_map; /* Used for finding keys */
+#endif
+
+#endif /* SQL_BITMAP_INCLUDED */
diff --git a/contrib/libs/libmysql_r/sql/sql_cmd.h b/contrib/libs/libmysql_r/sql/sql_cmd.h
new file mode 100644
index 0000000000..064b8836fc
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/sql_cmd.h
@@ -0,0 +1,247 @@
+/* Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+ @file sql/sql_cmd.h
+ Representation of an SQL command.
+*/
+
+#ifndef SQL_CMD_INCLUDED
+#define SQL_CMD_INCLUDED
+
+#include "my_dbug.h"
+#include "my_sqlcommand.h"
+#include "sql/select_lex_visitor.h"
+
+class THD;
+class Prepared_statement;
+struct handlerton;
+struct MYSQL_LEX_STRING;
+struct MYSQL_LEX_CSTRING;
+
+/**
+ Representation of an SQL command.
+
+ This class is an interface between the parser and the runtime.
+ The parser builds the appropriate derived classes of Sql_cmd
+ to represent a SQL statement in the parsed tree.
+ The execute() method in the derived classes of Sql_cmd contain the runtime
+ implementation.
+ Note that this interface is used for SQL statements recently implemented,
+ the code for older statements tend to load the LEX structure with more
+ attributes instead.
+ Implement new statements by sub-classing Sql_cmd, as this improves
+ code modularity (see the 'big switch' in dispatch_command()), and decreases
+ the total size of the LEX structure (therefore saving memory in stored
+ programs).
+ The recommended name of a derived class of Sql_cmd is Sql_cmd_<derived>.
+
+ Notice that the Sql_cmd class should not be confused with the Statement class.
+ Statement is a class that is used to manage an SQL command or a set
+ of SQL commands. When the SQL statement text is analyzed, the parser will
+ create one or more Sql_cmd objects to represent the actual SQL commands.
+*/
+class Sql_cmd {
+ private:
+ Sql_cmd(const Sql_cmd &); // No copy constructor wanted
+ void operator=(Sql_cmd &); // No assignment operator wanted
+
+ public:
+ /**
+ @brief Return the command code for this statement
+ */
+ virtual enum_sql_command sql_command_code() const = 0;
+
+ /// @return true if this statement is prepared
+ bool is_prepared() const { return m_prepared; }
+
+ /**
+ Prepare this SQL statement.
+
+ @param thd the current thread
+
+ @returns false if success, true if error
+ */
+ virtual bool prepare(THD *thd MY_ATTRIBUTE((unused))) {
+ // Default behavior for a statement is to have no preparation code.
+ /* purecov: begin inspected */
+ DBUG_ASSERT(!is_prepared());
+ set_prepared();
+ return false;
+ /* purecov: end */
+ }
+
+ /**
+ Execute this SQL statement.
+ @param thd the current thread.
+ @returns false if success, true if error
+ */
+ virtual bool execute(THD *thd) = 0;
+
+ /**
+ Command-specific reinitialization before execution of prepared statement
+
+ @see reinit_stmt_before_use()
+
+ @note Currently this function is overloaded for INSERT/REPLACE stmts only.
+
+ @param thd Current THD.
+ */
+ virtual void cleanup(THD *thd MY_ATTRIBUTE((unused))) {
+ m_secondary_engine = nullptr;
+ }
+
+ /// Set the owning prepared statement
+ void set_owner(Prepared_statement *stmt) { m_owner = stmt; }
+
+ /// Get the owning prepared statement
+ Prepared_statement *get_owner() { return m_owner; }
+
+ /// @return true if SQL command is a DML statement
+ virtual bool is_dml() const { return false; }
+
+ /// @return true if implemented as single table plan, DML statement only
+ virtual bool is_single_table_plan() const {
+ /* purecov: begin inspected */
+ DBUG_ASSERT(is_dml());
+ return false;
+ /* purecov: end */
+ }
+
+ /**
+ Temporary function used to "unprepare" a prepared statement after
+ preparation, so that a subsequent execute statement will reprepare it.
+ This is done because UNIT::cleanup() will un-resolve all resolved QBs.
+ */
+ virtual void unprepare(THD *thd MY_ATTRIBUTE((unused))) {
+ DBUG_ASSERT(is_prepared());
+ m_prepared = false;
+ }
+
+ virtual bool accept(THD *thd MY_ATTRIBUTE((unused)),
+ Select_lex_visitor *visitor MY_ATTRIBUTE((unused))) {
+ return false;
+ }
+
+ /**
+ Is this statement of a type and on a form that makes it eligible
+ for execution in a secondary storage engine?
+
+ @return the name of the secondary storage engine, or nullptr if
+ the statement is not eligible for execution in a secondary storage
+ engine
+ */
+ virtual const MYSQL_LEX_CSTRING *eligible_secondary_storage_engine() const {
+ return nullptr;
+ }
+
+ /**
+ Disable use of secondary storage engines in this statement. After
+ a call to this function, the statement will not try to use a
+ secondary storage engine until it is reprepared.
+ */
+ void disable_secondary_storage_engine() {
+ DBUG_ASSERT(m_secondary_engine == nullptr);
+ m_secondary_engine_enabled = false;
+ }
+
+ /**
+ Has use of secondary storage engines been disabled for this statement?
+ */
+ bool secondary_storage_engine_disabled() const {
+ return !m_secondary_engine_enabled;
+ }
+
+ /**
+ Mark the current statement as using a secondary storage engine.
+ This function must be called before the statement starts opening
+ tables in a secondary engine.
+ */
+ void use_secondary_storage_engine(const handlerton *hton) {
+ DBUG_ASSERT(m_secondary_engine_enabled);
+ m_secondary_engine = hton;
+ }
+
+ /**
+ Is this statement using a secondary storage engine?
+ */
+ bool using_secondary_storage_engine() const {
+ return m_secondary_engine != nullptr;
+ }
+
+ /**
+ Get the handlerton of the secondary engine that is used for
+ executing this statement, or nullptr if a secondary engine is not
+ used.
+ */
+ const handlerton *secondary_engine() const { return m_secondary_engine; }
+
+ protected:
+ Sql_cmd() : m_owner(nullptr), m_prepared(false), prepare_only(true) {}
+
+ virtual ~Sql_cmd() {
+ /*
+ Sql_cmd objects are allocated in thd->mem_root.
+ In MySQL, the C++ destructor is never called, the underlying MEM_ROOT is
+ simply destroyed instead.
+ Do not rely on the destructor for any cleanup.
+ */
+ DBUG_ASSERT(false);
+ }
+
+ /**
+ @return true if object represents a preparable statement, ie. a query
+ that is prepared with a PREPARE statement and executed with an EXECUTE
+ statement. False is returned for regular statements (non-preparable
+ statements) that are executed directly.
+ @todo replace with "m_owner != nullptr" when prepare-once is implemented
+ */
+ bool needs_explicit_preparation() const { return prepare_only; }
+
+ /// Set this statement as prepared
+ void set_prepared() { m_prepared = true; }
+
+ private:
+ Prepared_statement
+ *m_owner; /// Owning prepared statement, nullptr if non-prep.
+ bool m_prepared; /// True when statement has been prepared
+
+ /**
+ Tells if a secondary storage engine can be used for this
+ statement. If it is false, use of a secondary storage engine will
+ not be considered for executing this statement.
+ */
+ bool m_secondary_engine_enabled{true};
+
+ /**
+ The secondary storage engine to use for execution of this
+ statement, if any, or nullptr if the primary engine is used.
+ This property is reset at the start of each execution.
+ */
+ const handlerton *m_secondary_engine{nullptr};
+
+ protected:
+ bool prepare_only; /// @see needs_explicit_preparation
+ /// @todo remove when prepare-once is implemented
+};
+
+#endif // SQL_CMD_INCLUDED
diff --git a/contrib/libs/libmysql_r/sql/sql_const.h b/contrib/libs/libmysql_r/sql/sql_const.h
new file mode 100644
index 0000000000..6beae64297
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/sql_const.h
@@ -0,0 +1,454 @@
+/* Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+ @file
+ File containing constants that can be used throughout the server.
+
+ @note This file shall not contain any includes of any kinds.
+*/
+
+#ifndef SQL_CONST_INCLUDED
+#define SQL_CONST_INCLUDED
+
+#include "my_inttypes.h"
+
+#define LIBLEN FN_REFLEN - FN_LEN /* Max l{ngd p} dev */
+/**
+ The maximum length of a key in the table definition cache.
+
+ The key consists of the schema name, a '\0' character, the table
+ name and a '\0' character. Hence NAME_LEN * 2 + 1 + 1.
+
+ Additionally, the key can be suffixed with either 4 + 4 extra bytes
+ for slave tmp tables, or with a single extra byte for tables in a
+ secondary storage engine. Add 4 + 4 to account for either of these
+ suffixes.
+*/
+#define MAX_DBKEY_LENGTH (NAME_LEN * 2 + 1 + 1 + 4 + 4)
+#define MAX_ALIAS_NAME 256
+#define MAX_FIELD_NAME 34 /* Max colum name length +2 */
+#define MAX_SYS_VAR_LENGTH 32
+#define MAX_KEY MAX_INDEXES /* Max used keys */
+#define MAX_REF_PARTS 16U /* Max parts used as ref */
+#define MAX_KEY_LENGTH 3072U /* max possible key */
+#if SIZEOF_OFF_T > 4
+#define MAX_REFLENGTH 8 /* Max length for record ref */
+#else
+#define MAX_REFLENGTH 4 /* Max length for record ref */
+#endif
+
+#define MAX_MBWIDTH 3 /* Max multibyte sequence */
+#define MAX_FIELD_CHARLENGTH 255
+#define MAX_FIELD_VARCHARLENGTH 65535
+#define MAX_FIELD_BLOBLENGTH UINT_MAX32 /* cf field_blob::get_length() */
+/**
+ CHAR and VARCHAR fields longer than this number of characters are converted
+ to BLOB.
+ Non-character fields longer than this number of bytes are converted to BLOB.
+ Comparisons should be '>' or '<='.
+*/
+#define CONVERT_IF_BIGGER_TO_BLOB 512 /* Used for CREATE ... SELECT */
+
+/* Max column width +1 */
+#define MAX_FIELD_WIDTH (MAX_FIELD_CHARLENGTH * MAX_MBWIDTH + 1)
+
+#define MAX_BIT_FIELD_LENGTH 64 /* Max length in bits for bit fields */
+
+#define MAX_DATE_WIDTH 10 /* YYYY-MM-DD */
+#define MAX_TIME_WIDTH 10 /* -838:59:59 */
+#define MAX_TIME_FULL_WIDTH 23 /* -DDDDDD HH:MM:SS.###### */
+#define MAX_DATETIME_FULL_WIDTH 29 /* YYYY-MM-DD HH:MM:SS.###### AM */
+#define MAX_DATETIME_WIDTH 19 /* YYYY-MM-DD HH:MM:SS */
+#define MAX_DATETIME_COMPRESSED_WIDTH 14 /* YYYYMMDDHHMMSS */
+
+#define DATE_INT_DIGITS 8 /* YYYYMMDD */
+#define TIME_INT_DIGITS 7 /* hhhmmss */
+#define DATETIME_INT_DIGITS 14 /* YYYYMMDDhhmmss */
+
+/**
+ MAX_TABLES and xxx_TABLE_BIT are used in optimization of table factors and
+ expressions, and in join plan generation.
+ MAX_TABLES counts the maximum number of tables that can be handled in a
+ join operation. It is the number of bits in the table_map, minus the
+ number of pseudo table bits (bits that do not represent actual tables, but
+ still need to be handled by our algorithms). The pseudo table bits are:
+ INNER_TABLE_BIT is set for all expressions that contain a parameter,
+ a subquery that accesses tables, or a function that accesses tables.
+ An expression that has only INNER_TABLE_BIT is constant for the duration
+ of a query expression, but must be evaluated at least once during execution.
+ OUTER_REF_TABLE_BIT is set for expressions that contain a column that
+ is resolved as an outer reference. Also notice that all subquery items
+ between the column reference and the query block where the column is
+ resolved, have this bit set. Expressions that are represented by this bit
+ are constant for the duration of the subquery they are defined in.
+ RAND_TABLE_BIT is set for expressions containing a non-deterministic
+ element, such as a random function or a non-deterministic function.
+ Expressions containing this bit cannot be evaluated once and then cached,
+ they must be evaluated at latest possible point.
+ MAX_TABLES_FOR_SIZE adds the pseudo bits and is used for sizing purposes only.
+*/
+#define MAX_TABLES_FOR_SIZE (sizeof(table_map) * 8) ///< Use for sizing ONLY
+#define MAX_TABLES (MAX_TABLES_FOR_SIZE - 3) ///< Max tables in join
+#define INNER_TABLE_BIT (((table_map)1) << (MAX_TABLES + 0))
+#define OUTER_REF_TABLE_BIT (((table_map)1) << (MAX_TABLES + 1))
+#define RAND_TABLE_BIT (((table_map)1) << (MAX_TABLES + 2))
+#define PSEUDO_TABLE_BITS \
+ (INNER_TABLE_BIT | OUTER_REF_TABLE_BIT | RAND_TABLE_BIT)
+#define MAX_FIELDS 4096 /* Maximum number of columns */
+#define MAX_PARTITIONS 8192
+
+#define MAX_ENUM_VALUES 65535 /* Max number of enumeration values */
+#define MAX_INTERVAL_VALUE_LENGTH 255 /* Max length of enum/set values */
+
+#define MAX_SELECT_NESTING (sizeof(nesting_map) * 8 - 1)
+
+#define DEFAULT_SORT_MEMORY (256UL * 1024UL)
+#define MIN_SORT_MEMORY (32UL * 1024UL)
+
+/* Some portable defines */
+
+#define STRING_BUFFER_USUAL_SIZE 80
+
+/* Memory allocated when parsing a statement / saving a statement */
+#define MEM_ROOT_BLOCK_SIZE 8192
+#define MEM_ROOT_PREALLOC 8192
+#define TRANS_MEM_ROOT_BLOCK_SIZE 4096
+#define TRANS_MEM_ROOT_PREALLOC 4096
+
+#define DEFAULT_ERROR_COUNT 1024
+#define EXTRA_RECORDS 10 /* Extra records in sort */
+#define SCROLL_EXTRA 5 /* Extra scroll-rows. */
+#define FERR -1 /* Error from my_functions */
+#define CREATE_MODE 0 /* Default mode on new files */
+#define NAMES_SEP_CHAR '\377' /* Char to sep. names */
+
+#define READ_RECORD_BUFFER (uint)(IO_SIZE * 8) /* Pointer_buffer_size */
+#define DISK_BUFFER_SIZE (uint)(IO_SIZE * 16) /* Size of diskbuffer */
+
+/***************************************************************************
+ Configuration parameters
+****************************************************************************/
+
+#define ACL_CACHE_SIZE 256
+#define MAX_PASSWORD_LENGTH 32
+#define HOST_CACHE_SIZE 128
+#define MAX_ACCEPT_RETRY 10 // Test accept this many times
+#define MAX_FIELDS_BEFORE_HASH 32
+#define USER_VARS_HASH_SIZE 16
+#define TABLE_OPEN_CACHE_MIN 400
+#define TABLE_OPEN_CACHE_DEFAULT 4000
+static const ulong TABLE_DEF_CACHE_DEFAULT = 400;
+static const ulong SCHEMA_DEF_CACHE_DEFAULT = 256;
+static const ulong STORED_PROGRAM_DEF_CACHE_DEFAULT = 256;
+static const ulong TABLESPACE_DEF_CACHE_DEFAULT = 256;
+static const ulong EVENT_DEF_CACHE_DEFAULT = 256;
+
+/**
+ Maximum number of connections default value.
+ 151 is larger than Apache's default max children,
+ to avoid "too many connections" error in a common setup.
+*/
+#define MAX_CONNECTIONS_DEFAULT 151
+/**
+ We must have room for at least 400 table definitions in the table
+ cache, since otherwise there is no chance prepared
+ statements that use these many tables can work.
+ Prepared statements use table definition cache ids (table_map_id)
+ as table version identifiers. If the table definition
+ cache size is less than the number of tables used in a statement,
+ the contents of the table definition cache is guaranteed to rotate
+ between a prepare and execute. This leads to stable validation
+ errors. In future we shall use more stable version identifiers,
+ for now the only solution is to ensure that the table definition
+ cache can contain at least all tables of a given statement.
+*/
+static const ulong TABLE_DEF_CACHE_MIN = 400;
+static const ulong SCHEMA_DEF_CACHE_MIN = 256;
+static const ulong STORED_PROGRAM_DEF_CACHE_MIN = 256;
+static const ulong TABLESPACE_DEF_CACHE_MIN = 256;
+static const ulong EVENT_DEF_CACHE_MIN = 256;
+
+/*
+ Stack reservation.
+ Feel free to raise this by the smallest amount you can to get the
+ "execution_constants" test to pass.
+*/
+#if defined HAVE_UBSAN && SIZEOF_CHARP == 4
+#define STACK_MIN_SIZE 30000 // Abort if less stack during eval.
+#else
+#define STACK_MIN_SIZE 20000 // Abort if less stack during eval.
+#endif
+
+#define STACK_MIN_SIZE_FOR_OPEN 1024 * 80
+
+#if defined(__SUNPRO_CC)
+#define STACK_BUFF_ALLOC 352 * 2 ///< For stack overrun checks
+#else
+#define STACK_BUFF_ALLOC 352 ///< For stack overrun checks
+#endif
+
+#ifndef MYSQLD_NET_RETRY_COUNT
+#define MYSQLD_NET_RETRY_COUNT 10 ///< Abort read after this many int.
+#endif
+
+#define QUERY_ALLOC_BLOCK_SIZE 8192
+#define QUERY_ALLOC_PREALLOC_SIZE 8192
+#define TRANS_ALLOC_BLOCK_SIZE 4096
+#define TRANS_ALLOC_PREALLOC_SIZE 4096
+#define RANGE_ALLOC_BLOCK_SIZE 4096
+#define ACL_ALLOC_BLOCK_SIZE 1024
+#define UDF_ALLOC_BLOCK_SIZE 1024
+#define TABLE_ALLOC_BLOCK_SIZE 1024
+#define WARN_ALLOC_BLOCK_SIZE 2048
+
+/*
+ The following parameters is to decide when to use an extra cache to
+ optimise seeks when reading a big table in sorted order
+*/
+#define MIN_FILE_LENGTH_TO_USE_ROW_CACHE (10L * 1024 * 1024)
+#define MIN_ROWS_TO_USE_TABLE_CACHE 100
+#define MIN_ROWS_TO_USE_BULK_INSERT 100
+
+/*
+ For sequential disk seeks the cost formula is:
+ DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST * #blocks_to_skip
+
+ The cost of average seek
+ DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST*BLOCKS_IN_AVG_SEEK =1.0.
+*/
+#define DISK_SEEK_BASE_COST (0.9)
+
+#define BLOCKS_IN_AVG_SEEK 128
+
+#define DISK_SEEK_PROP_COST (0.1 / BLOCKS_IN_AVG_SEEK)
+
+/**
+ Number of rows in a reference table when refereed through a not unique key.
+ This value is only used when we don't know anything about the key
+ distribution.
+*/
+#define MATCHING_ROWS_IN_OTHER_TABLE 10
+
+#define MY_CHARSET_BIN_MB_MAXLEN 1
+
+/** Don't pack string keys shorter than this (if PACK_KEYS=1 isn't used). */
+#define KEY_DEFAULT_PACK_LENGTH 8
+
+/** Characters shown for the command in 'show processlist'. */
+#define PROCESS_LIST_WIDTH 100
+/* Characters shown for the command in 'information_schema.processlist' */
+#define PROCESS_LIST_INFO_WIDTH 65535
+
+#define PRECISION_FOR_DOUBLE 53
+#define PRECISION_FOR_FLOAT 24
+
+/* -[digits].E+## */
+#define MAX_FLOAT_STR_LENGTH (FLT_DIG + 6)
+/* -[digits].E+### */
+#define MAX_DOUBLE_STR_LENGTH (DBL_DIG + 7)
+
+/*
+ Default time to wait before aborting a new client connection
+ that does not respond to "initial server greeting" timely
+*/
+#define CONNECT_TIMEOUT 10
+
+/* The following can also be changed from the command line */
+#define DEFAULT_CONCURRENCY 10
+#define DELAYED_LIMIT 100 /**< pause after xxx inserts */
+#define DELAYED_QUEUE_SIZE 1000
+#define DELAYED_WAIT_TIMEOUT 5 * 60 /**< Wait for delayed insert */
+
+#define LONG_TIMEOUT ((ulong)3600L * 24L * 365L)
+
+/**
+ Maximum length of time zone name that we support (Time zone name is
+ char(64) in db). mysqlbinlog needs it.
+*/
+#define MAX_TIME_ZONE_NAME_LENGTH (NAME_LEN + 1)
+
+#if defined(_WIN32)
+#define INTERRUPT_PRIOR -2
+#define CONNECT_PRIOR -1
+#define WAIT_PRIOR 0
+#define QUERY_PRIOR 2
+#else
+#define INTERRUPT_PRIOR 10
+#define CONNECT_PRIOR 9
+#define WAIT_PRIOR 8
+#define QUERY_PRIOR 6
+#endif /* _WIN32 */
+
+/*
+ Flags below are set when we perform
+ context analysis of the statement and make
+ subqueries non-const. It prevents subquery
+ evaluation at context analysis stage.
+*/
+
+/*
+ Don't evaluate this subquery during statement prepare even if
+ it's a constant one. The flag is switched off in the end of
+ mysqld_stmt_prepare.
+*/
+#define CONTEXT_ANALYSIS_ONLY_PREPARE 1
+/*
+ Special SELECT_LEX::prepare mode: changing of query is prohibited.
+ When creating a view, we need to just check its syntax omitting
+ any optimizations: afterwards definition of the view will be
+ reconstructed by means of ::print() methods and written to
+ to an .frm file. We need this definition to stay untouched.
+*/
+#define CONTEXT_ANALYSIS_ONLY_VIEW 2
+/*
+ Don't evaluate this subquery during derived table prepare even if
+ it's a constant one.
+*/
+#define CONTEXT_ANALYSIS_ONLY_DERIVED 4
+
+/* @@optimizer_switch flags. These must be in sync with optimizer_switch_typelib
+ */
+#define OPTIMIZER_SWITCH_INDEX_MERGE (1ULL << 0)
+#define OPTIMIZER_SWITCH_INDEX_MERGE_UNION (1ULL << 1)
+#define OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNION (1ULL << 2)
+#define OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT (1ULL << 3)
+#define OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN (1ULL << 4)
+#define OPTIMIZER_SWITCH_INDEX_CONDITION_PUSHDOWN (1ULL << 5)
+/** If this is off, MRR is never used. */
+#define OPTIMIZER_SWITCH_MRR (1ULL << 6)
+/**
+ If OPTIMIZER_SWITCH_MRR is on and this is on, MRR is used depending on a
+ cost-based choice ("automatic"). If OPTIMIZER_SWITCH_MRR is on and this is
+ off, MRR is "forced" (i.e. used as long as the storage engine is capable of
+ doing it).
+*/
+#define OPTIMIZER_SWITCH_MRR_COST_BASED (1ULL << 7)
+#define OPTIMIZER_SWITCH_BNL (1ULL << 8)
+#define OPTIMIZER_SWITCH_BKA (1ULL << 9)
+#define OPTIMIZER_SWITCH_MATERIALIZATION (1ULL << 10)
+#define OPTIMIZER_SWITCH_SEMIJOIN (1ULL << 11)
+#define OPTIMIZER_SWITCH_LOOSE_SCAN (1ULL << 12)
+#define OPTIMIZER_SWITCH_FIRSTMATCH (1ULL << 13)
+#define OPTIMIZER_SWITCH_DUPSWEEDOUT (1ULL << 14)
+#define OPTIMIZER_SWITCH_SUBQ_MAT_COST_BASED (1ULL << 15)
+#define OPTIMIZER_SWITCH_USE_INDEX_EXTENSIONS (1ULL << 16)
+#define OPTIMIZER_SWITCH_COND_FANOUT_FILTER (1ULL << 17)
+#define OPTIMIZER_SWITCH_DERIVED_MERGE (1ULL << 18)
+#define OPTIMIZER_SWITCH_USE_INVISIBLE_INDEXES (1ULL << 19)
+#define OPTIMIZER_SKIP_SCAN (1ULL << 20)
+#define OPTIMIZER_SWITCH_LAST (1ULL << 21)
+
+#define OPTIMIZER_SWITCH_DEFAULT \
+ (OPTIMIZER_SWITCH_INDEX_MERGE | OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \
+ OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNION | \
+ OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT | \
+ OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN | \
+ OPTIMIZER_SWITCH_INDEX_CONDITION_PUSHDOWN | OPTIMIZER_SWITCH_MRR | \
+ OPTIMIZER_SWITCH_MRR_COST_BASED | OPTIMIZER_SWITCH_BNL | \
+ OPTIMIZER_SWITCH_MATERIALIZATION | OPTIMIZER_SWITCH_SEMIJOIN | \
+ OPTIMIZER_SWITCH_LOOSE_SCAN | OPTIMIZER_SWITCH_FIRSTMATCH | \
+ OPTIMIZER_SWITCH_DUPSWEEDOUT | OPTIMIZER_SWITCH_SUBQ_MAT_COST_BASED | \
+ OPTIMIZER_SWITCH_USE_INDEX_EXTENSIONS | \
+ OPTIMIZER_SWITCH_COND_FANOUT_FILTER | OPTIMIZER_SWITCH_DERIVED_MERGE | \
+ OPTIMIZER_SKIP_SCAN)
+
+enum SHOW_COMP_OPTION { SHOW_OPTION_YES, SHOW_OPTION_NO, SHOW_OPTION_DISABLED };
+
+enum enum_mark_columns {
+ MARK_COLUMNS_NONE,
+ MARK_COLUMNS_READ,
+ MARK_COLUMNS_WRITE,
+ MARK_COLUMNS_TEMP
+};
+
+/*
+ Exit code used by mysqld_exit, exit and _exit function
+ to indicate successful termination of mysqld.
+*/
+#define MYSQLD_SUCCESS_EXIT 0
+/*
+ Exit code used by mysqld_exit, exit and _exit function to
+ signify unsuccessful termination of mysqld. The exit
+ code signifies the server should NOT BE RESTARTED AUTOMATICALLY
+ by init systems like systemd.
+*/
+#define MYSQLD_ABORT_EXIT 1
+/*
+ Exit code used by mysqld_exit, exit and _exit function to
+ signify unsuccessful termination of mysqld. The exit code
+ signifies the server should be RESTARTED AUTOMATICALLY by
+ init systems like systemd.
+*/
+#define MYSQLD_FAILURE_EXIT 2
+/*
+ Exit code used by mysqld_exit, my_thread_exit function which allows
+ for external programs like systemd, mysqld_safe to restart mysqld
+ server. The exit code 16 is choosen so it is safe as InnoDB code
+ exit directly with values like 3.
+*/
+#define MYSQLD_RESTART_EXIT 16
+
+#define UUID_LENGTH (8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12)
+
+/*
+ This enumeration type is used only by the function find_item_in_list
+ to return the info on how an item has been resolved against a list
+ of possibly aliased items.
+ The item can be resolved:
+ - against an alias name of the list's element (RESOLVED_AGAINST_ALIAS)
+ - against non-aliased field name of the list (RESOLVED_WITH_NO_ALIAS)
+ - against an aliased field name of the list (RESOLVED_BEHIND_ALIAS)
+ - ignoring the alias name in cases when SQL requires to ignore aliases
+ (e.g. when the resolved field reference contains a table name or
+ when the resolved item is an expression) (RESOLVED_IGNORING_ALIAS)
+*/
+enum enum_resolution_type {
+ NOT_RESOLVED = 0,
+ RESOLVED_BEHIND_ALIAS,
+ RESOLVED_AGAINST_ALIAS,
+ RESOLVED_WITH_NO_ALIAS,
+ RESOLVED_IGNORING_ALIAS
+};
+
+/// Enumeration for {Item,SELECT_LEX[_UNIT],Table_function}::walk
+enum class enum_walk {
+ PREFIX = 0x01,
+ POSTFIX = 0x02,
+ SUBQUERY = 0x04,
+ SUBQUERY_PREFIX = 0x05, // Combine prefix and subquery traversal
+ SUBQUERY_POSTFIX = 0x06 // Combine postfix and subquery traversal
+};
+
+inline enum_walk operator|(enum_walk lhs, enum_walk rhs) {
+ return enum_walk(int(lhs) | int(rhs));
+}
+
+inline bool operator&(enum_walk lhs, enum_walk rhs) {
+ return (int(lhs) & int(rhs)) != 0;
+}
+
+class Item;
+/// Processor type for {Item,SELECT_LEX[_UNIT],Table_function}::walk
+typedef bool (Item::*Item_processor)(uchar *arg);
+
+#endif /* SQL_CONST_INCLUDED */
diff --git a/contrib/libs/libmysql_r/sql/sql_data_change.h b/contrib/libs/libmysql_r/sql/sql_data_change.h
new file mode 100644
index 0000000000..cc30fab137
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/sql_data_change.h
@@ -0,0 +1,327 @@
+#ifndef SQL_DATA_CHANGE_INCLUDED
+#define SQL_DATA_CHANGE_INCLUDED
+/* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+ @file sql_data_change.h
+
+ Contains classes representing SQL-data change statements. The
+ actual implementions of the functionality are found in files
+ sql_{insert, update}.{h,cc}
+*/
+
+#include <stddef.h>
+#include <sys/types.h>
+
+#include "my_base.h" // ha_rows
+#include "my_bitmap.h" // MY_BITMAP
+#include "my_dbug.h"
+
+class Item;
+struct TABLE;
+template <class T>
+class List;
+
+enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_UPDATE };
+
+/**
+ This class encapsulates a data change operation. There are three such
+ operations.
+
+ -# Insert statements, i.e. INSERT INTO .. VALUES
+
+ -# Update statements. UPDATE @<table@> SET ...
+
+ -# Delete statements. Currently this class is not used for delete statements
+ and thus has not yet been adapted to handle it.
+
+ @todo Rename this class.
+
+ The COPY_INFO structure is used by INSERT/REPLACE code.
+ The schema of the row counting by the INSERT/INSERT ... ON DUPLICATE KEY
+ UPDATE code:
+ If a row is inserted then the copied variable is incremented.
+ If a row is updated by the INSERT ... ON DUPLICATE KEY UPDATE and the
+ new data differs from the old one then the copied and the updated
+ variables are incremented.
+ The touched variable is incremented if a row was touched by the update part
+ of the INSERT ... ON DUPLICATE KEY UPDATE no matter whether the row
+ was actually changed or not.
+*/
+class COPY_INFO {
+ public:
+ class Statistics {
+ public:
+ Statistics()
+ : records(0),
+ deleted(0),
+ updated(0),
+ copied(0),
+ error_count(0),
+ touched(0) {}
+
+ ha_rows records; /**< Number of processed records */
+ ha_rows deleted; /**< Number of deleted records */
+ ha_rows updated; /**< Number of updated records */
+ ha_rows copied; /**< Number of copied records */
+ ha_rows error_count;
+ ha_rows touched; /* Number of touched records */
+ };
+
+ enum operation_type { INSERT_OPERATION, UPDATE_OPERATION };
+
+ private:
+ COPY_INFO(const COPY_INFO &other); ///< undefined
+ void operator=(COPY_INFO &); ///< undefined
+
+ /// Describes the data change operation that this object represents.
+ const operation_type m_optype;
+
+ /**
+ List of columns of the target table which the statement will explicitely
+ fill; and thus we must not set a function default for them.
+ NULL means "empty list".
+ */
+ List<Item> *m_changed_columns;
+
+ /**
+ A second list of columns like m_changed_columns. See the constructor
+ specific of LOAD DATA INFILE, below.
+ */
+ List<Item> *m_changed_columns2;
+
+ /** Whether this object must manage function defaults */
+ const bool m_manage_defaults;
+ /** Bitmap: bit is set if we should set column number i to its function
+ * default */
+ MY_BITMAP *m_function_default_columns;
+
+ /// Policy for handling insertion of duplicate values.
+ const enum enum_duplicates handle_duplicates;
+
+ protected:
+ /**
+ This function will, unless done already, calculate and keep the set of
+ function default columns.
+
+ Function default columns are those columns declared DEFAULT @<function@>
+ and/or ON UPDATE @<function@>. These will store the return value of
+ @<function@> when the relevant operation is applied on the table.
+
+ Calling this function, without error, is a prerequisite for calling
+ COPY_INFO::set_function_defaults().
+
+ @param table The table to be used for instantiating the column set.
+
+ @retval false Success.
+ @retval true Memory allocation error.
+ */
+ bool get_function_default_columns(TABLE *table);
+
+ /**
+ The column bitmap which has been cached for this data change operation.
+ @see COPY_INFO::get_function_default_columns()
+
+ @return The cached bitmap, or NULL if no bitmap was cached.
+ */
+ MY_BITMAP *get_cached_bitmap() const { return m_function_default_columns; }
+
+ public:
+ Statistics stats;
+ int escape_char, last_errno;
+ /** Values for UPDATE; needed by write_record() if INSERT with DUP_UPDATE */
+ List<Item> *update_values;
+
+ /**
+ Initializes this data change operation as an SQL @c INSERT (with all
+ possible syntaxes and variants).
+
+ @param optype The data change operation type.
+ @param inserted_columns List of columns of the target table which
+ the statement will explicitely fill; COPY_INFO
+ must not set a function default for them. NULL
+ means "empty list".
+ @param manage_defaults Whether this object should manage function
+ defaults.
+ @param duplicate_handling The policy for handling duplicates.
+
+ */
+ COPY_INFO(operation_type optype, List<Item> *inserted_columns,
+ bool manage_defaults, enum_duplicates duplicate_handling)
+ : m_optype(optype),
+ m_changed_columns(inserted_columns),
+ m_changed_columns2(NULL),
+ m_manage_defaults(manage_defaults),
+ m_function_default_columns(NULL),
+ handle_duplicates(duplicate_handling),
+ stats(),
+ escape_char(0),
+ last_errno(0),
+ update_values(NULL) {
+ DBUG_ASSERT(optype == INSERT_OPERATION);
+ }
+
+ /**
+ Initializes this data change operation as an SQL @c LOAD @c DATA @c
+ INFILE.
+ Note that this statement has its inserted columns spread over two
+ lists:
+@verbatim
+ LOAD DATA INFILE a_file
+ INTO TABLE a_table (col1, col2) < first list (col1, col2)
+ SET col3=val; < second list (col3)
+@endverbatim
+
+ @param optype The data change operation type.
+ @param inserted_columns List of columns of the target table which
+ the statement will explicitely fill; COPY_INFO
+ must not set a function default for them. NULL
+ means "empty list".
+ @param inserted_columns2 A second list like inserted_columns
+ @param manage_defaults Whether this object should manage function
+ defaults.
+ @param duplicates_handling How to handle duplicates.
+ @param escape_character The escape character.
+ */
+ COPY_INFO(operation_type optype, List<Item> *inserted_columns,
+ List<Item> *inserted_columns2, bool manage_defaults,
+ enum_duplicates duplicates_handling, int escape_character)
+ : m_optype(optype),
+ m_changed_columns(inserted_columns),
+ m_changed_columns2(inserted_columns2),
+ m_manage_defaults(manage_defaults),
+ m_function_default_columns(NULL),
+ handle_duplicates(duplicates_handling),
+ stats(),
+ escape_char(escape_character),
+ last_errno(0),
+ update_values(NULL) {
+ DBUG_ASSERT(optype == INSERT_OPERATION);
+ }
+
+ /**
+ Initializes this data change operation as an SQL @c UPDATE (multi- or
+ not).
+
+ @param optype The data change operation type.
+ @param fields The column objects that are to be updated.
+ @param values The values to be assigned to the fields.
+ @note that UPDATE always lists columns, so non-listed columns may need a
+ default thus m_manage_defaults is always true.
+ */
+ COPY_INFO(operation_type optype, List<Item> *fields, List<Item> *values)
+ : m_optype(optype),
+ m_changed_columns(fields),
+ m_changed_columns2(NULL),
+ m_manage_defaults(true),
+ m_function_default_columns(NULL),
+ handle_duplicates(DUP_ERROR),
+ stats(),
+ escape_char(0),
+ last_errno(0),
+ update_values(values) {
+ DBUG_ASSERT(optype == UPDATE_OPERATION);
+ }
+
+ operation_type get_operation_type() const { return m_optype; }
+
+ List<Item> *get_changed_columns() const { return m_changed_columns; }
+
+ const List<Item> *get_changed_columns2() const { return m_changed_columns2; }
+
+ bool get_manage_defaults() const { return m_manage_defaults; }
+
+ enum_duplicates get_duplicate_handling() const { return handle_duplicates; }
+
+ /**
+ Assigns function default values to columns of the supplied table.
+
+ @note COPY_INFO::get_function_default_columns() and
+ COPY_INFO::add_function_default_columns() must be called prior to invoking
+ this function.
+
+ @param table The table to which columns belong.
+
+ @note It is assumed that all columns in this COPY_INFO are resolved to the
+ table.
+
+ @retval false Success.
+ @retval true Some error happened while executing the default expression.
+ my_error has already been called so the calling function
+ only needs to bail out.
+ */
+ bool set_function_defaults(TABLE *table);
+
+ /**
+ Adds the columns that are bound to receive default values from a function
+ (e.g. CURRENT_TIMESTAMP) to the set columns. Uses lazy instantiation of the
+ set of function default columns.
+
+ @param table The table on which the operation is performed.
+ @param[out] columns The function default columns are added to this set.
+
+ @retval false Success.
+ @retval true Memory allocation error during lazy instantiation.
+ */
+ bool add_function_default_columns(TABLE *table, MY_BITMAP *columns) {
+ if (get_function_default_columns(table)) return true;
+ bitmap_union(columns, m_function_default_columns);
+ return false;
+ }
+
+ /**
+ True if this operation will set some fields to function default result
+ values when invoked on the table.
+
+ @note COPY_INFO::add_function_default_columns() must be called prior to
+ invoking this function.
+ */
+ bool function_defaults_apply(const TABLE *) const {
+ DBUG_ASSERT(m_function_default_columns != NULL);
+ return !bitmap_is_clear_all(m_function_default_columns);
+ }
+
+ /**
+ True if any of the columns set in the bitmap have default functions
+ that may set the column.
+ */
+ bool function_defaults_apply_on_columns(MY_BITMAP *map) {
+ DBUG_ASSERT(m_function_default_columns != NULL);
+ return bitmap_is_overlapping(m_function_default_columns, map);
+ }
+
+ /**
+ Tells the object to not manage function defaults for the last 'count'
+ columns of 'table'.
+ @retval false if success
+ */
+ bool ignore_last_columns(TABLE *table, uint count);
+
+ /**
+ This class allocates its memory in a MEM_ROOT, so there's nothing to
+ delete.
+ */
+ virtual ~COPY_INFO() {}
+};
+
+#endif // SQL_DATA_CHANGE_INCLUDED
diff --git a/contrib/libs/libmysql_r/sql/sql_list.h b/contrib/libs/libmysql_r/sql/sql_list.h
new file mode 100644
index 0000000000..fbdd358060
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/sql_list.h
@@ -0,0 +1,828 @@
+#ifndef INCLUDES_MYSQL_SQL_LIST_H
+#define INCLUDES_MYSQL_SQL_LIST_H
+/* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <stddef.h>
+#include <sys/types.h>
+#include <algorithm>
+#include <iterator>
+#include <type_traits>
+
+#include "my_alloc.h"
+#include "my_compiler.h"
+#include "my_dbug.h"
+#include "my_sharedlib.h"
+#include "sql/thr_malloc.h"
+
+/**
+ Simple intrusive linked list.
+
+ @remark Similar in nature to base_list, but intrusive. It keeps a
+ a pointer to the first element in the list and a indirect
+ reference to the last element.
+*/
+template <typename T>
+class SQL_I_List {
+ public:
+ uint elements;
+ /** The first element in the list. */
+ T *first;
+ /** A reference to the next element in the list. */
+ T **next;
+
+ SQL_I_List() { empty(); }
+
+ SQL_I_List(const SQL_I_List &tmp)
+ : elements(tmp.elements),
+ first(tmp.first),
+ next(elements ? tmp.next : &first) {}
+
+ SQL_I_List(SQL_I_List &&) = default;
+
+ inline void empty() {
+ elements = 0;
+ first = NULL;
+ next = &first;
+ }
+
+ inline void link_in_list(T *element, T **next_ptr) {
+ elements++;
+ (*next) = element;
+ next = next_ptr;
+ *next = NULL;
+ }
+
+ inline void save_and_clear(SQL_I_List<T> *save) {
+ *save = *this;
+ empty();
+ }
+
+ inline void push_front(SQL_I_List<T> *save) {
+ /* link current list last */
+ *save->next = first;
+ first = save->first;
+ elements += save->elements;
+ }
+
+ inline void push_back(SQL_I_List<T> *save) {
+ if (save->first) {
+ *next = save->first;
+ next = save->next;
+ elements += save->elements;
+ }
+ }
+
+ inline uint size() const { return elements; }
+
+ SQL_I_List &operator=(SQL_I_List &) = default;
+ SQL_I_List &operator=(SQL_I_List &&) = default;
+};
+
+/*
+ Basic single linked list
+ Used for item and item_buffs.
+ All list ends with a pointer to the 'end_of_list' element, which
+ data pointer is a null pointer and the next pointer points to itself.
+ This makes it very fast to traverse lists as we don't have to
+ test for a specialend condition for list that can't contain a null
+ pointer.
+*/
+
+/**
+ list_node - a node of a single-linked list.
+ @note We never call a destructor for instances of this class.
+*/
+
+struct list_node {
+ list_node *next;
+ void *info;
+ list_node(void *info_par, list_node *next_par)
+ : next(next_par), info(info_par) {}
+ list_node() /* For end_of_list */
+ {
+ info = 0;
+ next = this;
+ }
+};
+
+extern MYSQL_PLUGIN_IMPORT list_node end_of_list;
+
+class base_list {
+ protected:
+ list_node *first, **last;
+
+ public:
+ uint elements;
+
+ bool operator==(const base_list &rhs) const {
+ return elements == rhs.elements && first == rhs.first && last == rhs.last;
+ }
+
+ inline void empty() {
+ elements = 0;
+ first = &end_of_list;
+ last = &first;
+ }
+ inline base_list() { empty(); }
+ /**
+ This is a shallow copy constructor that implicitly passes the ownership
+ from the source list to the new instance. The old instance is not
+ updated, so both objects end up sharing the same nodes. If one of
+ the instances then adds or removes a node, the other becomes out of
+ sync ('last' pointer), while still operational. Some old code uses and
+ relies on this behaviour. This logic is quite tricky: please do not use
+ it in any new code.
+ */
+ base_list(const base_list &tmp)
+ : first(tmp.first),
+ last(tmp.elements ? tmp.last : &first),
+ elements(tmp.elements) {}
+ base_list &operator=(const base_list &tmp) {
+ elements = tmp.elements;
+ first = tmp.first;
+ last = elements ? tmp.last : &first;
+ return *this;
+ }
+ /**
+ Construct a deep copy of the argument in memory root mem_root.
+ The elements themselves are copied by pointer.
+ */
+ base_list(const base_list &rhs, MEM_ROOT *mem_root);
+ inline bool push_back(void *info) {
+ if (((*last) = new (*THR_MALLOC) list_node(info, &end_of_list))) {
+ last = &(*last)->next;
+ elements++;
+ return 0;
+ }
+ return 1;
+ }
+ inline bool push_back(void *info, MEM_ROOT *mem_root) {
+ if (((*last) = new (mem_root) list_node(info, &end_of_list))) {
+ last = &(*last)->next;
+ elements++;
+ return 0;
+ }
+ return 1;
+ }
+ inline bool push_front(void *info) {
+ list_node *node = new (*THR_MALLOC) list_node(info, first);
+ if (node) {
+ if (last == &first) last = &node->next;
+ first = node;
+ elements++;
+ return 0;
+ }
+ return 1;
+ }
+ inline bool push_front(void *info, MEM_ROOT *mem_root) {
+ list_node *node = new (mem_root) list_node(info, first);
+ if (node) {
+ if (last == &first) last = &node->next;
+ first = node;
+ elements++;
+ return false;
+ }
+ return true;
+ }
+
+ void remove(list_node **prev) {
+ list_node *node = (*prev)->next;
+ if (!--elements)
+ last = &first;
+ else if (last == &(*prev)->next)
+ last = prev;
+ destroy(*prev);
+ *prev = node;
+ }
+ inline void concat(base_list *list) {
+ if (!list->is_empty()) {
+ *last = list->first;
+ last = list->last;
+ elements += list->elements;
+ }
+ }
+ inline void *pop(void) {
+ if (first == &end_of_list) return 0;
+ list_node *tmp = first;
+ first = first->next;
+ if (!--elements) last = &first;
+ return tmp->info;
+ }
+ inline void disjoin(base_list *list) {
+ list_node **prev = &first;
+ list_node *node = first;
+ list_node *list_first = list->first;
+ elements = 0;
+ while (node && node != list_first) {
+ prev = &node->next;
+ node = node->next;
+ elements++;
+ }
+ *prev = *last;
+ last = prev;
+ }
+ inline void prepend(base_list *list) {
+ if (!list->is_empty()) {
+ *list->last = first;
+ if (is_empty()) last = list->last;
+ first = list->first;
+ elements += list->elements;
+ }
+ }
+ /**
+ Swap two lists.
+ */
+ inline void swap(base_list &rhs) {
+ std::swap(first, rhs.first);
+ std::swap(last, rhs.last);
+ std::swap(elements, rhs.elements);
+ }
+ inline list_node *last_node() { return *last; }
+ inline list_node *first_node() { return first; }
+ inline void *head() { return first->info; }
+ inline const void *head() const { return first->info; }
+ inline void **head_ref() { return first != &end_of_list ? &first->info : 0; }
+ inline bool is_empty() const { return first == &end_of_list; }
+ inline list_node *last_ref() { return &end_of_list; }
+ inline uint size() const { return elements; }
+ friend class base_list_iterator;
+ friend class error_list;
+ friend class error_list_iterator;
+
+#ifdef LIST_EXTRA_DEBUG
+ /*
+ Check list invariants and print results into trace. Invariants are:
+ - (*last) points to end_of_list
+ - There are no NULLs in the list.
+ - base_list::elements is the number of elements in the list.
+
+ SYNOPSIS
+ check_list()
+ name Name to print to trace file
+
+ RETURN
+ 1 The list is Ok.
+ 0 List invariants are not met.
+ */
+
+ bool check_list(const char *name) {
+ base_list *list = this;
+ list_node *node = first;
+ uint cnt = 0;
+
+ while (node->next != &end_of_list) {
+ if (!node->info) {
+ DBUG_PRINT("list_invariants",
+ ("%s: error: NULL element in the list", name));
+ return false;
+ }
+ node = node->next;
+ cnt++;
+ }
+ if (last != &(node->next)) {
+ DBUG_PRINT("list_invariants", ("%s: error: wrong last pointer", name));
+ return false;
+ }
+ if (cnt + 1 != elements) {
+ DBUG_PRINT("list_invariants", ("%s: error: wrong element count", name));
+ return false;
+ }
+ DBUG_PRINT("list_invariants", ("%s: list is ok", name));
+ return true;
+ }
+#endif // LIST_EXTRA_DEBUG
+
+ protected:
+ void after(void *info, list_node *node) {
+ list_node *new_node = new (*THR_MALLOC) list_node(info, node->next);
+ node->next = new_node;
+ elements++;
+ if (last == &(node->next)) last = &new_node->next;
+ }
+ bool after(void *info, list_node *node, MEM_ROOT *mem_root) {
+ list_node *new_node = new (mem_root) list_node(info, node->next);
+ if (!new_node) return true; // OOM
+
+ node->next = new_node;
+ elements++;
+ if (last == &(node->next)) last = &new_node->next;
+
+ return false;
+ }
+};
+
+class base_list_iterator {
+ protected:
+ base_list *list;
+ list_node **el, **prev, *current;
+ void sublist(base_list &ls, uint elm) {
+ ls.first = *el;
+ ls.last = list->last;
+ ls.elements = elm;
+ }
+
+ public:
+ base_list_iterator() : list(0), el(0), prev(0), current(0) {}
+
+ base_list_iterator(base_list &list_par) { init(list_par); }
+
+ inline void init(base_list &list_par) {
+ list = &list_par;
+ el = &list_par.first;
+ prev = 0;
+ current = 0;
+ }
+
+ inline void *next(void) {
+ prev = el;
+ current = *el;
+ el = &current->next;
+ return current->info;
+ }
+ inline void *next_fast(void) {
+ list_node *tmp;
+ tmp = *el;
+ el = &tmp->next;
+ return tmp->info;
+ }
+ inline void rewind(void) { el = &list->first; }
+ inline void *replace(void *element) { // Return old element
+ void *tmp = current->info;
+ DBUG_ASSERT(current->info != 0);
+ current->info = element;
+ return tmp;
+ }
+ void *replace(base_list &new_list) {
+ void *ret_value = current->info;
+ if (!new_list.is_empty()) {
+ *new_list.last = current->next;
+ current->info = new_list.first->info;
+ current->next = new_list.first->next;
+ if ((list->last == &current->next) && (new_list.elements > 1))
+ list->last = new_list.last;
+ list->elements += new_list.elements - 1;
+ }
+ return ret_value; // return old element
+ }
+ inline void remove(void) // Remove current
+ {
+ list->remove(prev);
+ el = prev;
+ current = 0; // Safeguard
+ }
+ void after(void *element) // Insert element after current
+ {
+ list->after(element, current);
+ current = current->next;
+ el = &current->next;
+ }
+ bool after(void *a, MEM_ROOT *mem_root) {
+ if (list->after(a, current, mem_root)) return true;
+
+ current = current->next;
+ el = &current->next;
+ return false;
+ }
+ inline void **ref(void) // Get reference pointer
+ {
+ return &current->info;
+ }
+ inline bool is_last(void) { return el == list->last; }
+ inline bool is_before_first() const { return current == NULL; }
+ bool prepend(void *a, MEM_ROOT *mem_root) {
+ if (list->push_front(a, mem_root)) return true;
+
+ el = &list->first;
+ prev = el;
+ el = &(*el)->next;
+
+ return false;
+ }
+ friend class error_list_iterator;
+};
+
+template <class T>
+class List_STL_Iterator;
+
+template <class T>
+class List : public base_list {
+ public:
+ List() : base_list() {}
+ inline List(const List<T> &tmp) : base_list(tmp) {}
+ List &operator=(const List &tmp) {
+ return static_cast<List &>(base_list::operator=(tmp));
+ }
+ inline List(const List<T> &tmp, MEM_ROOT *mem_root)
+ : base_list(tmp, mem_root) {}
+ /*
+ Typecasting to (void *) it's necessary if we want to declare List<T> with
+ constant T parameter (like List<const char>), since the untyped storage
+ is "void *", and assignment of const pointer to "void *" is a syntax error.
+ */
+ inline bool push_back(T *a) { return base_list::push_back((void *)a); }
+ inline bool push_back(T *a, MEM_ROOT *mem_root) {
+ return base_list::push_back((void *)a, mem_root);
+ }
+ inline bool push_front(T *a) { return base_list::push_front((void *)a); }
+ inline bool push_front(T *a, MEM_ROOT *mem_root) {
+ return base_list::push_front((void *)a, mem_root);
+ }
+ inline T *head() { return static_cast<T *>(base_list::head()); }
+ inline const T *head() const {
+ return static_cast<const T *>(base_list::head());
+ }
+ inline T **head_ref() { return (T **)base_list::head_ref(); }
+ inline T *pop() { return (T *)base_list::pop(); }
+ inline void concat(List<T> *list) { base_list::concat(list); }
+ inline void disjoin(List<T> *list) { base_list::disjoin(list); }
+ inline void prepend(List<T> *list) { base_list::prepend(list); }
+ void delete_elements(void) {
+ list_node *element, *next;
+ for (element = first; element != &end_of_list; element = next) {
+ next = element->next;
+ delete (T *)element->info;
+ }
+ empty();
+ }
+
+ void destroy_elements(void) {
+ list_node *element, *next;
+ for (element = first; element != &end_of_list; element = next) {
+ next = element->next;
+ destroy((T *)element->info);
+ }
+ empty();
+ }
+
+ T *operator[](uint index) const {
+ DBUG_ASSERT(index < elements);
+ list_node *current = first;
+ for (uint i = 0; i < index; ++i) current = current->next;
+ return static_cast<T *>(current->info);
+ }
+
+ void replace(uint index, T *new_value) {
+ DBUG_ASSERT(index < elements);
+ list_node *current = first;
+ for (uint i = 0; i < index; ++i) current = current->next;
+ current->info = new_value;
+ }
+
+ bool swap_elts(uint index1, uint index2) {
+ if (index1 == index2) return false;
+
+ if (index1 >= elements || index2 >= elements) return true; // error
+
+ if (index2 < index1) std::swap(index1, index2);
+
+ list_node *current1 = first;
+ for (uint i = 0; i < index1; ++i) current1 = current1->next;
+
+ list_node *current2 = current1;
+ for (uint i = 0; i < index2 - index1; ++i) current2 = current2->next;
+
+ std::swap(current1->info, current2->info);
+
+ return false;
+ }
+
+ /**
+ @brief
+ Sort the list
+
+ @param cmp node comparison function
+
+ @details
+ The function sorts list nodes by an exchange sort algorithm.
+ The order of list nodes isn't changed, values of info fields are
+ swapped instead. Due to this, list iterators that are initialized before
+ sort could be safely used after sort, i.e they wouldn't cause a crash.
+ As this isn't an effective algorithm the list to be sorted is supposed to
+ be short.
+ */
+ template <typename Node_cmp_func>
+ void sort(Node_cmp_func cmp) {
+ if (elements < 2) return;
+ for (list_node *n1 = first; n1 && n1 != &end_of_list; n1 = n1->next) {
+ for (list_node *n2 = n1->next; n2 && n2 != &end_of_list; n2 = n2->next) {
+ if (cmp(static_cast<T *>(n1->info), static_cast<T *>(n2->info)) > 0) {
+ void *tmp = n1->info;
+ n1->info = n2->info;
+ n2->info = tmp;
+ }
+ }
+ }
+ }
+
+ // For C++11 range-based for loops.
+ using iterator = List_STL_Iterator<T>;
+ iterator begin() { return iterator(first); }
+ iterator end() {
+ // If the list overlaps another list, last isn't actually
+ // the last element, and if so, we'd give a different result from
+ // List_iterator_fast.
+ DBUG_ASSERT((*last)->next == &end_of_list);
+
+ return iterator(*last);
+ }
+
+ using const_iterator = List_STL_Iterator<const T>;
+ const_iterator begin() const { return const_iterator(first); }
+ const_iterator end() const {
+ DBUG_ASSERT((*last)->next == &end_of_list);
+ return const_iterator(*last);
+ }
+ const_iterator cbegin() const { return const_iterator(first); }
+ const_iterator cend() const {
+ DBUG_ASSERT((*last)->next == &end_of_list);
+ return const_iterator(*last);
+ }
+};
+
+template <class T>
+class List_iterator : public base_list_iterator {
+ public:
+ List_iterator(List<T> &a) : base_list_iterator(a) {}
+ List_iterator() : base_list_iterator() {}
+ inline void init(List<T> &a) { base_list_iterator::init(a); }
+ inline T *operator++(int) { return (T *)base_list_iterator::next(); }
+ inline T *replace(T *a) { return (T *)base_list_iterator::replace(a); }
+ inline T *replace(List<T> &a) { return (T *)base_list_iterator::replace(a); }
+ inline void rewind(void) { base_list_iterator::rewind(); }
+ inline void remove() { base_list_iterator::remove(); }
+ inline void after(T *a) { base_list_iterator::after(a); }
+ inline bool after(T *a, MEM_ROOT *mem_root) {
+ return base_list_iterator::after(a, mem_root);
+ }
+ inline T **ref(void) { return (T **)base_list_iterator::ref(); }
+};
+
+template <class T>
+class List_iterator_fast : public base_list_iterator {
+ protected:
+ inline T *replace(T *) { return (T *)0; }
+ inline T *replace(List<T> &) { return (T *)0; }
+ inline void remove(void) {}
+ inline void after(T *) {}
+ inline T **ref(void) { return (T **)0; }
+
+ public:
+ inline List_iterator_fast(List<T> &a) : base_list_iterator(a) {}
+ inline List_iterator_fast() : base_list_iterator() {}
+ inline void init(List<T> &a) { base_list_iterator::init(a); }
+ inline T *operator++(int) { return (T *)base_list_iterator::next_fast(); }
+ inline void rewind(void) { base_list_iterator::rewind(); }
+ void sublist(List<T> &list_arg, uint el_arg) {
+ base_list_iterator::sublist(list_arg, el_arg);
+ }
+};
+
+/*
+ Like List_iterator<T>, but with an STL-compatible interface
+ (ForwardIterator), so that you can use it in range-based for loops.
+ Prefer this to List_iterator<T> wherever possible, but also prefer
+ std::vector<T> or std::list<T> to List<T> wherever possible.
+ */
+template <class T>
+class List_STL_Iterator {
+ public:
+ explicit List_STL_Iterator(list_node *node) : m_current(node) {}
+
+ // Iterator (required for InputIterator).
+ T &operator*() const { return *static_cast<T *>(m_current->info); }
+
+ List_STL_Iterator &operator++() {
+ m_current = m_current->next;
+ return *this;
+ }
+
+ using difference_type = ptrdiff_t;
+ using value_type = T; // NOTE: std::remove_cv_t<T> from C++20.
+ using pointer = T *;
+ using reference = T &;
+ using iterator_category = std::forward_iterator_tag;
+
+ // EqualityComparable (required for InputIterator).
+ bool operator==(const List_STL_Iterator &other) const {
+ return m_current == other.m_current;
+ }
+
+ // InputIterator (required for ForwardIterator).
+ bool operator!=(const List_STL_Iterator &other) const {
+ return !(*this == other);
+ }
+
+ T *operator->() const { return static_cast<T *>(m_current->info); }
+
+ // DefaultConstructible (required for ForwardIterator).
+ List_STL_Iterator() {}
+
+ // ForwardIterator.
+ List_STL_Iterator operator++(int) {
+ List_STL_Iterator copy = *this;
+ m_current = m_current->next;
+ return copy;
+ }
+
+ private:
+ list_node *m_current;
+};
+
+template <typename T>
+class base_ilist;
+template <typename T>
+class base_ilist_iterator;
+
+/*
+ A simple intrusive list.
+
+ NOTE: this inherently unsafe, since we rely on <T> to have
+ the same layout as ilink<T> (see base_ilist::sentinel).
+ Please consider using a different strategy for linking objects.
+*/
+
+template <typename T>
+class ilink {
+ T **prev, *next;
+
+ public:
+ ilink() : prev(NULL), next(NULL) {}
+
+ void unlink() {
+ /* Extra tests because element doesn't have to be linked */
+ if (prev) *prev = next;
+ if (next) next->prev = prev;
+ prev = NULL;
+ next = NULL;
+ }
+
+ friend class base_ilist<T>;
+ friend class base_ilist_iterator<T>;
+};
+
+/* Needed to be able to have an I_List of char* strings in mysqld.cc. */
+
+class i_string : public ilink<i_string> {
+ public:
+ const char *ptr;
+ i_string() : ptr(0) {}
+ i_string(const char *s) : ptr(s) {}
+};
+
+/* needed for linked list of two strings for replicate-rewrite-db */
+class i_string_pair : public ilink<i_string_pair> {
+ public:
+ const char *key;
+ const char *val;
+ i_string_pair() : key(0), val(0) {}
+ i_string_pair(const char *key_arg, const char *val_arg)
+ : key(key_arg), val(val_arg) {}
+};
+
+template <class T>
+class I_List_iterator;
+
+template <typename T>
+class base_ilist {
+ T *first;
+ ilink<T> sentinel;
+
+ static_assert(!std::is_polymorphic<T>::value,
+ "Do not use this for classes with virtual members");
+
+ public:
+ // The sentinel is not a T, but at least it is a POD
+ void empty() SUPPRESS_UBSAN {
+ first = static_cast<T *>(&sentinel);
+ sentinel.prev = &first;
+ }
+ base_ilist() { empty(); }
+
+ // The sentinel is not a T, but at least it is a POD
+ bool is_empty() const SUPPRESS_UBSAN {
+ return first == static_cast<const T *>(&sentinel);
+ }
+
+ /// Pushes new element in front of list.
+ void push_front(T *a) {
+ first->prev = &a->next;
+ a->next = first;
+ a->prev = &first;
+ first = a;
+ }
+
+ /// Pushes new element to the end of the list, i.e. in front of the sentinel.
+ void push_back(T *a) {
+ *sentinel.prev = a;
+ a->next = static_cast<T *>(&sentinel);
+ a->prev = sentinel.prev;
+ sentinel.prev = &a->next;
+ }
+
+ // Unlink first element, and return it.
+ T *get() {
+ if (is_empty()) return NULL;
+ T *first_link = first;
+ first_link->unlink();
+ return first_link;
+ }
+
+ T *head() { return is_empty() ? NULL : first; }
+
+ /**
+ Moves list elements to new owner, and empties current owner (i.e. this).
+
+ @param[in,out] new_owner The new owner of the list elements.
+ Should be empty in input.
+ */
+
+ void move_elements_to(base_ilist *new_owner) {
+ DBUG_ASSERT(new_owner->is_empty());
+ new_owner->first = first;
+ new_owner->sentinel = sentinel;
+ empty();
+ }
+
+ friend class base_ilist_iterator<T>;
+
+ private:
+ /*
+ We don't want to allow copying of this class, as that would give us
+ two list heads containing the same elements.
+ So we declare, but don't define copy CTOR and assignment operator.
+ */
+ base_ilist(const base_ilist &);
+ void operator=(const base_ilist &);
+};
+
+template <typename T>
+class base_ilist_iterator {
+ base_ilist<T> *list;
+ T **el, *current;
+
+ public:
+ base_ilist_iterator(base_ilist<T> &list_par)
+ : list(&list_par), el(&list_par.first), current(NULL) {}
+
+ // The sentinel is not a T, but at least it is a POD
+ T *next(void) SUPPRESS_UBSAN {
+ /* This is coded to allow push_back() while iterating */
+ current = *el;
+ if (current == static_cast<T *>(&list->sentinel)) return NULL;
+ el = &current->next;
+ return current;
+ }
+};
+
+template <class T>
+class I_List : private base_ilist<T> {
+ public:
+ using base_ilist<T>::empty;
+ using base_ilist<T>::is_empty;
+ using base_ilist<T>::get;
+ using base_ilist<T>::push_front;
+ using base_ilist<T>::push_back;
+ using base_ilist<T>::head;
+ void move_elements_to(I_List<T> *new_owner) {
+ base_ilist<T>::move_elements_to(new_owner);
+ }
+ friend class I_List_iterator<T>;
+};
+
+template <class T>
+class I_List_iterator : public base_ilist_iterator<T> {
+ public:
+ I_List_iterator(I_List<T> &a) : base_ilist_iterator<T>(a) {}
+ inline T *operator++(int) { return base_ilist_iterator<T>::next(); }
+};
+
+void free_list(I_List<i_string_pair> *list);
+void free_list(I_List<i_string> *list);
+
+template <class T>
+List<T> *List_merge(T *head, List<T> *tail) {
+ tail->push_front(head);
+ return tail;
+}
+
+#endif // INCLUDES_MYSQL_SQL_LIST_H
diff --git a/contrib/libs/libmysql_r/sql/sql_plist.h b/contrib/libs/libmysql_r/sql/sql_plist.h
new file mode 100644
index 0000000000..6f97826116
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/sql_plist.h
@@ -0,0 +1,269 @@
+#ifndef SQL_PLIST_H
+#define SQL_PLIST_H
+/* Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <algorithm>
+
+#include "my_inttypes.h"
+
+template <typename T, typename L>
+class I_P_List_iterator;
+class I_P_List_null_counter;
+template <typename T>
+class I_P_List_no_push_back;
+
+/**
+ Intrusive parameterized list.
+
+ Unlike I_List does not require its elements to be descendant of ilink
+ class and therefore allows them to participate in several such lists
+ simultaneously.
+
+ Unlike List is doubly-linked list and thus supports efficient deletion
+ of element without iterator.
+
+ @tparam T Type of elements which will belong to list.
+ @tparam B Class which via its methods specifies which members
+ of T should be used for participating in this list.
+ Here is typical layout of such class:
+
+ struct B
+ {
+ static inline T **next_ptr(T *el)
+ {
+ return &el->next;
+ }
+ static inline T ***prev_ptr(T *el)
+ {
+ return &el->prev;
+ }
+ };
+ @tparam C Policy class specifying how counting of elements in the list
+ should be done. Instance of this class is also used as a place
+ where information about number of list elements is stored.
+ @sa I_P_List_null_counter, I_P_List_counter
+ @tparam I Policy class specifying whether I_P_List should support
+ efficient push_back() operation. Instance of this class
+ is used as place where we store information to support
+ this operation.
+ @sa I_P_List_no_push_back, I_P_List_fast_push_back.
+*/
+
+template <typename T, typename B, typename C = I_P_List_null_counter,
+ typename I = I_P_List_no_push_back<T>>
+class I_P_List : public C, public I {
+ T *m_first;
+
+ /*
+ Do not prohibit copying of I_P_List object to simplify their usage in
+ backup/restore scenarios. Note that performing any operations on such
+ is a bad idea.
+ */
+ public:
+ I_P_List() : I(&m_first), m_first(NULL) {}
+ inline void empty() {
+ m_first = NULL;
+ C::reset();
+ I::set_last(&m_first);
+ }
+ inline bool is_empty() const { return (m_first == NULL); }
+ inline void push_front(T *a) {
+ *B::next_ptr(a) = m_first;
+ if (m_first)
+ *B::prev_ptr(m_first) = B::next_ptr(a);
+ else
+ I::set_last(B::next_ptr(a));
+ m_first = a;
+ *B::prev_ptr(a) = &m_first;
+ C::inc();
+ }
+ inline void push_back(T *a) {
+ T **last = I::get_last();
+ *B::next_ptr(a) = *last;
+ *last = a;
+ *B::prev_ptr(a) = last;
+ I::set_last(B::next_ptr(a));
+ C::inc();
+ }
+ inline void insert_after(T *pos, T *a) {
+ if (pos == NULL)
+ push_front(a);
+ else {
+ *B::next_ptr(a) = *B::next_ptr(pos);
+ *B::prev_ptr(a) = B::next_ptr(pos);
+ *B::next_ptr(pos) = a;
+ if (*B::next_ptr(a)) {
+ T *old_next = *B::next_ptr(a);
+ *B::prev_ptr(old_next) = B::next_ptr(a);
+ } else
+ I::set_last(B::next_ptr(a));
+ C::inc();
+ }
+ }
+ inline void remove(T *a) {
+ T *next = *B::next_ptr(a);
+ if (next)
+ *B::prev_ptr(next) = *B::prev_ptr(a);
+ else
+ I::set_last(*B::prev_ptr(a));
+ **B::prev_ptr(a) = next;
+ C::dec();
+ }
+ inline T *front() { return m_first; }
+ inline const T *front() const { return m_first; }
+ inline T *pop_front() {
+ T *result = front();
+
+ if (result) remove(result);
+
+ return result;
+ }
+ void swap(I_P_List<T, B, C> &rhs) {
+ std::swap(m_first, rhs.m_first);
+ I::swap(rhs);
+ if (m_first)
+ *B::prev_ptr(m_first) = &m_first;
+ else
+ I::set_last(&m_first);
+ if (rhs.m_first)
+ *B::prev_ptr(rhs.m_first) = &rhs.m_first;
+ else
+ I::set_last(&rhs.m_first);
+ C::swap(rhs);
+ }
+ typedef B Adapter;
+ typedef I_P_List<T, B, C, I> Base;
+ typedef I_P_List_iterator<T, Base> Iterator;
+ typedef I_P_List_iterator<const T, Base> Const_Iterator;
+ friend class I_P_List_iterator<T, Base>;
+ friend class I_P_List_iterator<const T, Base>;
+};
+
+/**
+ Iterator for I_P_List.
+*/
+
+template <typename T, typename L>
+class I_P_List_iterator {
+ const L *list;
+ T *current;
+
+ public:
+ I_P_List_iterator(const L &a) : list(&a), current(a.m_first) {}
+ I_P_List_iterator(const L &a, T *current_arg)
+ : list(&a), current(current_arg) {}
+ inline void init(const L &a) {
+ list = &a;
+ current = a.m_first;
+ }
+ inline T *operator++(int) {
+ T *result = current;
+ if (result) current = *L::Adapter::next_ptr(current);
+ return result;
+ }
+ inline T *operator++() {
+ current = *L::Adapter::next_ptr(current);
+ return current;
+ }
+ inline void rewind() { current = list->m_first; }
+};
+
+/**
+ Hook class which via its methods specifies which members
+ of T should be used for participating in a intrusive list.
+*/
+
+template <typename T, T *T::*next, T **T::*prev>
+struct I_P_List_adapter {
+ static inline T **next_ptr(T *el) { return &(el->*next); }
+ static inline const T *const *next_ptr(const T *el) { return &(el->*next); }
+ static inline T ***prev_ptr(T *el) { return &(el->*prev); }
+};
+
+/**
+ Element counting policy class for I_P_List to be used in
+ cases when no element counting should be done.
+*/
+
+class I_P_List_null_counter {
+ protected:
+ void reset() {}
+ void inc() {}
+ void dec() {}
+ void swap(I_P_List_null_counter &) {}
+};
+
+/**
+ Element counting policy class for I_P_List which provides
+ basic element counting.
+*/
+
+class I_P_List_counter {
+ uint m_counter;
+
+ protected:
+ I_P_List_counter() : m_counter(0) {}
+ void reset() { m_counter = 0; }
+ void inc() { m_counter++; }
+ void dec() { m_counter--; }
+ void swap(I_P_List_counter &rhs) { std::swap(m_counter, rhs.m_counter); }
+
+ public:
+ uint elements() const { return m_counter; }
+};
+
+/**
+ A null insertion policy class for I_P_List to be used
+ in cases when push_back() operation is not necessary.
+*/
+
+template <typename T>
+class I_P_List_no_push_back {
+ protected:
+ I_P_List_no_push_back(T **) {}
+ void set_last(T **) {}
+ /*
+ T** get_last() const method is intentionally left unimplemented
+ in order to prohibit usage of push_back() method in lists which
+ use this policy.
+ */
+ void swap(I_P_List_no_push_back<T> &) {}
+};
+
+/**
+ An insertion policy class for I_P_List which can
+ be used when fast push_back() operation is required.
+*/
+
+template <typename T>
+class I_P_List_fast_push_back {
+ T **m_last;
+
+ protected:
+ I_P_List_fast_push_back(T **a) : m_last(a) {}
+ void set_last(T **a) { m_last = a; }
+ T **get_last() const { return m_last; }
+ void swap(I_P_List_fast_push_back<T> &rhs) { std::swap(m_last, rhs.m_last); }
+};
+
+#endif
diff --git a/contrib/libs/libmysql_r/sql/sql_plugin.h b/contrib/libs/libmysql_r/sql/sql_plugin.h
new file mode 100644
index 0000000000..a194edaabd
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/sql_plugin.h
@@ -0,0 +1,197 @@
+/* Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef _sql_plugin_h
+#define _sql_plugin_h
+
+#include <stddef.h>
+#include <sys/types.h>
+#include <vector>
+
+#include "lex_string.h"
+#include "my_io.h"
+#include "my_sqlcommand.h" // enum_sql_command
+#include "mysql/components/services/mysql_mutex_bits.h"
+#include "sql/sql_cmd.h" // Sql_cmd
+#include "sql/sql_plugin_ref.h" // plugin_ref
+
+class THD;
+class i_string;
+struct MEM_ROOT;
+struct SYS_VAR;
+struct my_option;
+template <class T>
+class I_List;
+
+extern const char *global_plugin_typelib_names[];
+extern mysql_mutex_t LOCK_plugin;
+extern mysql_mutex_t LOCK_plugin_delete;
+
+#ifdef DBUG_OFF
+#define plugin_ref_to_int(A) A
+#define plugin_int_to_ref(A) A
+#else
+#define plugin_ref_to_int(A) (A ? A[0] : NULL)
+#define plugin_int_to_ref(A) &(A)
+#endif
+
+/*
+ the following flags are valid for plugin_init()
+*/
+#define PLUGIN_INIT_SKIP_DYNAMIC_LOADING 1
+#define PLUGIN_INIT_SKIP_PLUGIN_TABLE 2
+#define PLUGIN_INIT_SKIP_INITIALIZATION 4
+
+#define MYSQL_ANY_PLUGIN -1
+
+/*
+ different values of st_plugin_int::state
+ though they look like a bitmap, plugin may only
+ be in one of those eigenstates, not in a superposition of them :)
+ It's a bitmap, because it makes it easier to test
+ "whether the state is one of those..."
+*/
+#define PLUGIN_IS_FREED 1
+#define PLUGIN_IS_DELETED 2
+#define PLUGIN_IS_UNINITIALIZED 4
+#define PLUGIN_IS_READY 8
+#define PLUGIN_IS_DYING 16
+#define PLUGIN_IS_DISABLED 32
+
+/* A handle for the dynamic library containing a plugin or plugins. */
+
+struct st_plugin_dl {
+ LEX_STRING dl;
+ void *handle;
+ struct st_mysql_plugin *plugins;
+ int version;
+ uint ref_count; /* number of plugins loaded from the library */
+};
+
+/**
+ This class implements the INSTALL PLUGIN statement.
+*/
+
+class Sql_cmd_install_plugin : public Sql_cmd {
+ public:
+ Sql_cmd_install_plugin(const LEX_STRING &comment, const LEX_STRING &ident)
+ : m_comment(comment), m_ident(ident) {}
+
+ virtual enum_sql_command sql_command_code() const {
+ return SQLCOM_INSTALL_PLUGIN;
+ }
+
+ /**
+ Install a new plugin by inserting a row into the
+ mysql.plugin table, creating a cache entry and
+ initializing plugin's internal data.
+
+ @param thd Thread context
+
+ @returns false if success, true otherwise
+ */
+ virtual bool execute(THD *thd);
+
+ private:
+ LEX_STRING m_comment;
+ LEX_STRING m_ident;
+};
+
+/**
+ This class implements the UNINSTALL PLUGIN statement.
+*/
+
+class Sql_cmd_uninstall_plugin : public Sql_cmd {
+ public:
+ explicit Sql_cmd_uninstall_plugin(const LEX_STRING &comment)
+ : m_comment(comment) {}
+
+ virtual enum_sql_command sql_command_code() const {
+ return SQLCOM_UNINSTALL_PLUGIN;
+ }
+
+ /**
+ Uninstall a plugin by removing a row from the
+ mysql.plugin table, deleting a cache entry and
+ deinitializing plugin's internal data.
+
+ @param thd Thread context
+
+ @returns false if success, true otherwise
+ */
+ virtual bool execute(THD *thd);
+
+ private:
+ LEX_STRING m_comment;
+};
+
+typedef int (*plugin_type_init)(struct st_plugin_int *);
+
+extern I_List<i_string> *opt_plugin_load_list_ptr;
+extern I_List<i_string> *opt_early_plugin_load_list_ptr;
+extern char *opt_plugin_dir_ptr;
+extern char opt_plugin_dir[FN_REFLEN];
+extern const LEX_STRING plugin_type_names[];
+
+extern bool plugin_register_early_plugins(int *argc, char **argv, int flags);
+extern bool plugin_register_builtin_and_init_core_se(int *argc, char **argv);
+extern bool plugin_register_dynamic_and_init_all(int *argc, char **argv,
+ int init_flags);
+extern bool is_builtin_and_core_se_initialized();
+extern void plugin_shutdown(void);
+extern void memcached_shutdown(void);
+void add_plugin_options(std::vector<my_option> *options, MEM_ROOT *mem_root);
+extern bool plugin_is_ready(const LEX_CSTRING &name, int type);
+#define my_plugin_lock_by_name(A, B, C) plugin_lock_by_name(A, B, C)
+#define my_plugin_lock_by_name_ci(A, B, C) plugin_lock_by_name(A, B, C)
+#define my_plugin_lock(A, B) plugin_lock(A, B)
+#define my_plugin_lock_ci(A, B) plugin_lock(A, B)
+extern plugin_ref plugin_lock(THD *thd, plugin_ref *ptr);
+extern plugin_ref plugin_lock_by_name(THD *thd, const LEX_CSTRING &name,
+ int type);
+extern void plugin_unlock(THD *thd, plugin_ref plugin);
+extern void plugin_unlock_list(THD *thd, plugin_ref *list, size_t count);
+extern void plugin_thdvar_init(THD *thd, bool enable_plugins);
+extern void plugin_thdvar_cleanup(THD *thd, bool enable_plugins);
+extern void plugin_thdvar_safe_update(THD *thd, SYS_VAR *var, char **dest,
+ const char *value);
+extern bool check_valid_path(const char *path, size_t length);
+extern void alloc_and_copy_thd_dynamic_variables(THD *thd, bool global_lock);
+
+typedef bool(plugin_foreach_func)(THD *thd, plugin_ref plugin, void *arg);
+#define plugin_foreach(A, B, C, D) \
+ plugin_foreach_with_mask(A, B, C, PLUGIN_IS_READY, D)
+extern bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
+ int type, uint state_mask, void *arg);
+extern bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func **funcs,
+ int type, uint state_mask, void *arg);
+int lock_plugin_data();
+int unlock_plugin_data();
+
+bool end_transaction(THD *thd, bool error);
+
+/**
+ Initialize one plugin.
+*/
+bool plugin_early_load_one(int *argc, char **argv, const char *plugin);
+
+#endif
diff --git a/contrib/libs/libmysql_r/sql/sql_plugin_ref.h b/contrib/libs/libmysql_r/sql/sql_plugin_ref.h
new file mode 100644
index 0000000000..26106fbe38
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/sql_plugin_ref.h
@@ -0,0 +1,138 @@
+/* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_PLUGIN_REF_INCLUDED
+#define SQL_PLUGIN_REF_INCLUDED
+
+#include "lex_string.h"
+#include "my_alloc.h"
+#include "mysql/mysql_lex_string.h"
+#include "prealloced_array.h"
+
+class sys_var;
+struct st_mysql_plugin;
+struct st_plugin_dl;
+
+enum enum_plugin_load_option {
+ PLUGIN_OFF,
+ PLUGIN_ON,
+ PLUGIN_FORCE,
+ PLUGIN_FORCE_PLUS_PERMANENT
+};
+
+/* A handle of a plugin */
+
+struct st_plugin_int {
+ LEX_STRING name{nullptr, 0};
+ st_mysql_plugin *plugin{nullptr};
+ st_plugin_dl *plugin_dl{nullptr};
+ uint state{0};
+ uint ref_count{0}; /* number of threads using the plugin */
+ void *data{nullptr}; /* plugin type specific, e.g. handlerton */
+ MEM_ROOT mem_root; /* memory for dynamic plugin structures */
+ sys_var *system_vars{nullptr}; /* server variables for this plugin */
+ enum_plugin_load_option load_option{
+ PLUGIN_OFF}; /* OFF, ON, FORCE, F+PERMANENT */
+};
+
+/*
+ See intern_plugin_lock() for the explanation for the
+ conditionally defined plugin_ref type
+*/
+
+#ifdef DBUG_OFF
+typedef struct st_plugin_int *plugin_ref;
+
+inline st_mysql_plugin *plugin_decl(st_plugin_int *ref) { return ref->plugin; }
+inline st_plugin_dl *plugin_dlib(st_plugin_int *ref) { return ref->plugin_dl; }
+template <typename T>
+inline T plugin_data(st_plugin_int *ref) {
+ return static_cast<T>(ref->data);
+}
+inline LEX_STRING *plugin_name(st_plugin_int *ref) { return &(ref->name); }
+inline uint plugin_state(st_plugin_int *ref) { return ref->state; }
+inline enum_plugin_load_option plugin_load_option(st_plugin_int *ref) {
+ return ref->load_option;
+}
+inline bool plugin_equals(st_plugin_int *ref1, st_plugin_int *ref2) {
+ return ref1 == ref2;
+}
+
+#else
+
+typedef struct st_plugin_int **plugin_ref;
+
+inline st_mysql_plugin *plugin_decl(st_plugin_int **ref) {
+ return ref[0]->plugin;
+}
+inline st_plugin_dl *plugin_dlib(st_plugin_int **ref) {
+ return ref[0]->plugin_dl;
+}
+template <typename T>
+inline T plugin_data(st_plugin_int **ref) {
+ return static_cast<T>(ref[0]->data);
+}
+inline LEX_STRING *plugin_name(st_plugin_int **ref) { return &(ref[0]->name); }
+inline uint plugin_state(st_plugin_int **ref) { return ref[0]->state; }
+inline enum_plugin_load_option plugin_load_option(st_plugin_int **ref) {
+ return ref[0]->load_option;
+}
+inline bool plugin_equals(st_plugin_int **ref1, st_plugin_int **ref2) {
+ return ref1 && ref2 && (ref1[0] == ref2[0]);
+}
+#endif
+
+/**
+ @class Plugin_array
+
+ @brief Plugin array helper class.
+*/
+class Plugin_array : public Prealloced_array<plugin_ref, 2> {
+ public:
+ /**
+ Class construction.
+
+ @param psi_key PSI key.
+ */
+ explicit Plugin_array(PSI_memory_key psi_key)
+ : Prealloced_array<plugin_ref, 2>(psi_key) {}
+
+ /**
+ Check, whether the plugin specified by the plugin argument has been
+ already added into the array.
+
+ @param plugin Plugin to check.
+
+ @retval true Plugin has been already added.
+ @retval false There is no plugin in the array.
+ */
+ bool exists(plugin_ref plugin) {
+ Plugin_array::iterator i;
+
+ for (i = begin(); i != end(); ++i)
+ if (plugin_equals(*i, plugin)) return true;
+
+ return false;
+ }
+};
+
+#endif // SQL_PLUGIN_REF_INCLUDED
diff --git a/contrib/libs/libmysql_r/sql/stateless_allocator.h b/contrib/libs/libmysql_r/sql/stateless_allocator.h
new file mode 100644
index 0000000000..fab48d5c11
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/stateless_allocator.h
@@ -0,0 +1,170 @@
+/* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef STATELESS_ALLOCATOR_INCLUDED
+#define STATELESS_ALLOCATOR_INCLUDED
+
+#include <stddef.h>
+#include <limits>
+#include <new>
+#include <utility> // std::forward
+
+#include "my_compiler.h"
+#include "my_dbug.h"
+
+/**
+ Functor struct which invokes my_free. Declared here as it is used as the
+ defalt value for Stateless_allocator's DEALLOC_FUN template parameter.
+*/
+struct My_free_functor {
+ void operator()(void *p, size_t) const;
+};
+
+/**
+ Stateless_allocator is a C++ STL memory allocator skeleton based on
+ Malloc_allocator, which assumes that a global free function can be
+ used to allocate and deallocate memory, so that no state need to be
+ kept by the allocator object.
+
+ The allocation and deallocation functions must be provided as
+ callable types (aka functors) which have no state and can be default
+ constructed.
+
+ Example usage:
+
+ @verbatim
+ struct My_psi_key_alloc
+ {
+ void* operator(size_t s)()
+ {
+ return my_malloc(My_psi_key, s, MYF(MY_WME | ME_FATALERROR));
+ }
+ };
+
+ template <class T>
+ using My_psi_key_allocator =
+ Stateless_allocator<T, My_psi_key_alloc>;
+
+ template < template<class T> class Allocator >
+ using default_string=
+ std::basic_string<char, std::char_traits<char>, Allocator<char> >;
+
+
+ typedef default_string<My_psi_key_allocator> My_psi_key_str;
+
+ My_psi_key_str x("foobar");
+ @endverbatim
+
+ Since a Stateless_allocator instance is always
+ default-constructible, it can also be used to create instances of
+ std::basic_string, even with compilers that have this libstd++ bug:
+ http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437 "basic_string
+ assumes that allocators are default-constructible".
+
+ @note allocate() throws std::bad_alloc() similarly to the default
+ STL memory allocator. This is necessary - STL functions which allocate
+ memory expect it. Otherwise these functions will try to use the memory,
+ leading to seg faults if memory allocation was not successful.
+
+*/
+
+template <class T, class ALLOC_FUN, class DEALLOC_FUN = My_free_functor>
+class Stateless_allocator {
+ public:
+ typedef T value_type;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+
+ typedef T *pointer;
+ typedef const T *const_pointer;
+
+ typedef T &reference;
+ typedef const T &const_reference;
+
+ template <class T_>
+ using Stateless_allocator_type =
+ Stateless_allocator<T_, ALLOC_FUN, DEALLOC_FUN>;
+
+ Stateless_allocator() = default;
+
+ pointer address(reference r) const { return &r; }
+ const_pointer address(const_reference r) const { return &r; }
+
+ template <class U>
+ Stateless_allocator(const Stateless_allocator_type<U> &) {}
+
+ template <class U>
+ Stateless_allocator &operator=(const Stateless_allocator_type<U> &) {}
+
+ pointer allocate(size_type n, const_pointer hint MY_ATTRIBUTE((unused)) = 0) {
+ if (n == 0) return NULL;
+ if (n > max_size()) throw std::bad_alloc();
+
+ pointer p = static_cast<pointer>(ALLOC_FUN()(n * sizeof(T)));
+ if (p == NULL) throw std::bad_alloc();
+ return p;
+ }
+
+ void deallocate(pointer p, size_type n) { DEALLOC_FUN()(p, n); }
+
+ template <class U, class... Args>
+ void construct(U *p, Args &&... args) {
+ DBUG_ASSERT(p != NULL);
+ try {
+ ::new ((void *)p) U(std::forward<Args>(args)...);
+ } catch (...) {
+ DBUG_ASSERT(false); // Constructor should not throw an exception.
+ }
+ }
+
+ void destroy(pointer p) {
+ DBUG_ASSERT(p != NULL);
+ try {
+ p->~T();
+ } catch (...) {
+ DBUG_ASSERT(false); // Destructor should not throw an exception
+ }
+ }
+
+ size_type max_size() const {
+ return std::numeric_limits<size_t>::max() / sizeof(T);
+ }
+
+ template <class U>
+ struct rebind {
+ typedef Stateless_allocator<U, ALLOC_FUN, DEALLOC_FUN> other;
+ };
+};
+
+template <class T, class ALLOC_FUN, class DEALLOC_FUN>
+bool operator==(const Stateless_allocator<T, ALLOC_FUN, DEALLOC_FUN> &,
+ const Stateless_allocator<T, ALLOC_FUN, DEALLOC_FUN> &) {
+ return true;
+}
+
+template <class T, class ALLOC_FUN, class DEALLOC_FUN>
+bool operator!=(const Stateless_allocator<T, ALLOC_FUN, DEALLOC_FUN> &,
+ const Stateless_allocator<T, ALLOC_FUN, DEALLOC_FUN> &) {
+ return false;
+}
+
+#endif // STATELESS_ALLOCATOR_INCLUDED
diff --git a/contrib/libs/libmysql_r/sql/stream_cipher.h b/contrib/libs/libmysql_r/sql/stream_cipher.h
new file mode 100644
index 0000000000..07c34a2eca
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/stream_cipher.h
@@ -0,0 +1,231 @@
+/* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef STREAM_CIPHER_INCLUDED
+#define STREAM_CIPHER_INCLUDED
+
+#include <openssl/evp.h>
+#include <memory>
+#include <string>
+
+/**
+ @file stream_cipher.h
+
+ @brief This file includes core components for encrypting/decrypting
+ binary log files.
+*/
+
+typedef std::basic_string<unsigned char> Key_string;
+
+/**
+ @class Stream_cipher
+
+ This abstract class represents the interface of a replication logs encryption
+ cipher that can be used to encrypt/decrypt a given stream content in both
+ sequential and random way.
+
+ - Sequential means encrypting/decrypting a stream from the begin to end
+ in order. For sequential encrypting/decrypting, you just need to call
+ it like:
+
+ open();
+ encrypt();
+ ...
+ encrypt(); // call it again and again
+ ...
+ close();
+
+ - Random means encrypting/decrypting a stream data without order. For
+ example:
+
+ - It first encrypts the data of a stream at the offset from 100 to 200.
+
+ - And then encrypts the data of the stream at the offset from 0 to 99.
+
+ For random encrypting/decrypting, you need to call set_stream_offset()
+ before calling encrypt(). Example:
+
+ open();
+
+ set_stream_offset(100);
+ encrypt(...);
+ ...
+ set_stream_offset(0);
+ encrypt(...)
+
+ close();
+*/
+class Stream_cipher {
+ public:
+ virtual ~Stream_cipher() {}
+
+ /**
+ Open the cipher with given password.
+
+ @param[in] password The password which is used to initialize the cipher.
+ @param[in] header_size The encrypted stream offset wrt the down stream.
+
+ @retval false Success.
+ @retval true Error.
+ */
+ virtual bool open(const Key_string &password, int header_size) = 0;
+
+ /** Close the cipher. */
+ virtual void close() = 0;
+
+ /**
+ Encrypt data.
+
+ @param[in] dest The buffer for storing encrypted data. It should be
+ at least 'length' bytes.
+ @param[in] src The data which will be encrypted.
+ @param[in] length Length of the data.
+
+ @retval false Success.
+ @retval true Error.
+ */
+ virtual bool encrypt(unsigned char *dest, const unsigned char *src,
+ int length) = 0;
+
+ /**
+ Decrypt data.
+
+ @param[in] dest The buffer for storing decrypted data. It should be
+ at least 'length' bytes.
+ @param[in] src The data which will be decrypted.
+ @param[in] length Length of the data.
+
+ @retval false Success.
+ @retval true Error.
+ */
+ virtual bool decrypt(unsigned char *dest, const unsigned char *src,
+ int length) = 0;
+
+ /**
+ Support encrypting/decrypting data at random position of a stream.
+
+ @param[in] offset The stream offset of the data which will be encrypted/
+ decrypted in next encrypt()/decrypt() call.
+
+ @retval false Success.
+ @retval true Error.
+ */
+ virtual bool set_stream_offset(uint64_t offset) = 0;
+
+ /**
+ Returns the size of the header of the stream being encrypted/decrypted.
+
+ @return the size of the header of the stream being encrypted/decrypted.
+ */
+ int get_header_size();
+
+ protected:
+ int m_header_size = 0;
+};
+
+/**
+ @class Aes_ctr
+
+ The class provides standards to be used by the Aes_ctr ciphers.
+*/
+class Aes_ctr {
+ public:
+ static const int PASSWORD_LENGTH = 32;
+ static const int AES_BLOCK_SIZE = 16;
+ static const int FILE_KEY_LENGTH = 32;
+ /**
+ Returns the message digest function to be uses when opening the cipher.
+
+ @return SHA-512 message digest.
+ */
+ static const EVP_MD *get_evp_md() { return EVP_sha512(); }
+ /**
+ Returns the cipher to be uses when using the cipher.
+
+ @return AES-256-CTR.
+ */
+ static const EVP_CIPHER *get_evp_cipher() { return EVP_aes_256_ctr(); }
+ /**
+ Returns a new unique Stream_cipher encryptor.
+
+ @return A new Stream_cipher encryptor.
+ */
+ static std::unique_ptr<Stream_cipher> get_encryptor();
+ /**
+ Returns a new unique Stream_cipher decryptor.
+
+ @return A new Stream_cipher decryptor.
+ */
+ static std::unique_ptr<Stream_cipher> get_decryptor();
+};
+
+enum class Cipher_type : int { ENCRYPT = 0, DECRYPT = 1 };
+
+/**
+ @class Aes_ctr_cipher
+
+ The class implements AES-CTR encryption/decryption. It supports to
+ encrypt/decrypt a stream in both sequential and random way.
+*/
+template <Cipher_type TYPE>
+class Aes_ctr_cipher : public Stream_cipher {
+ public:
+ static const int PASSWORD_LENGTH = Aes_ctr::PASSWORD_LENGTH;
+ static const int AES_BLOCK_SIZE = Aes_ctr::AES_BLOCK_SIZE;
+ static const int FILE_KEY_LENGTH = Aes_ctr::FILE_KEY_LENGTH;
+
+ virtual ~Aes_ctr_cipher() override;
+
+ bool open(const Key_string &password, int header_size) override;
+ void close() override;
+ bool encrypt(unsigned char *dest, const unsigned char *src,
+ int length) override;
+ bool decrypt(unsigned char *dest, const unsigned char *src,
+ int length) override;
+ bool set_stream_offset(uint64_t offset) override;
+
+ private:
+ /* Cipher context */
+ EVP_CIPHER_CTX *m_ctx = nullptr;
+ /* The file key to encrypt/decrypt data. */
+ unsigned char m_file_key[FILE_KEY_LENGTH];
+ /* The initialization vector (IV) used to encrypt/decrypt data. */
+ unsigned char m_iv[AES_BLOCK_SIZE];
+
+ /**
+ Initialize OpenSSL cipher related context and IV.
+
+ @param[in] offset The stream offset to compute the AES-CTR counter which
+ will be set into IV.
+
+ @retval false Success.
+ @retval true Error.
+ */
+ bool init_cipher(uint64_t offset);
+
+ /** Destroy OpenSSL cipher related context. */
+ void deinit_cipher();
+};
+
+typedef class Aes_ctr_cipher<Cipher_type::ENCRYPT> Aes_ctr_encryptor;
+typedef class Aes_ctr_cipher<Cipher_type::DECRYPT> Aes_ctr_decryptor;
+#endif // STREAM_CIPHER_INCLUDED
diff --git a/contrib/libs/libmysql_r/sql/thr_malloc.h b/contrib/libs/libmysql_r/sql/thr_malloc.h
new file mode 100644
index 0000000000..2577d93d0a
--- /dev/null
+++ b/contrib/libs/libmysql_r/sql/thr_malloc.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2.0,
+ as published by the Free Software Foundation.
+
+ This program is also distributed with certain software (including
+ but not limited to OpenSSL) that is licensed under separate terms,
+ as designated in a particular file or component or in included license
+ documentation. The authors of MySQL hereby grant you an additional
+ permission to link the program and your derivative works with the
+ separately licensed software that they have included with MySQL.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License, version 2.0, for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef THR_MALLOC_INCLUDED
+#define THR_MALLOC_INCLUDED
+
+#include <stddef.h>
+
+struct CHARSET_INFO;
+struct MEM_ROOT;
+typedef unsigned int PSI_memory_key;
+extern thread_local MEM_ROOT **THR_MALLOC;
+
+void init_sql_alloc(PSI_memory_key key, MEM_ROOT *root, size_t block_size,
+ size_t pre_alloc_size);
+
+void *sql_calloc(size_t);
+char *sql_strdup(const char *str);
+char *sql_strmake(const char *str, size_t len);
+void *sql_memdup(const void *ptr, size_t size);
+char *sql_strmake_with_convert(const char *str, size_t arg_length,
+ const CHARSET_INFO *from_cs,
+ size_t max_res_length, const CHARSET_INFO *to_cs,
+ size_t *result_length);
+
+#endif /* THR_MALLOC_INCLUDED */