diff options
author | hcpp <hcpp@ydb.tech> | 2023-11-08 12:09:41 +0300 |
---|---|---|
committer | hcpp <hcpp@ydb.tech> | 2023-11-08 12:56:14 +0300 |
commit | a361f5b98b98b44ea510d274f6769164640dd5e1 (patch) | |
tree | c47c80962c6e2e7b06798238752fd3da0191a3f6 /contrib/libs/libmysql_r/sql | |
parent | 9478806fde1f4d40bd5a45e7cbe77237dab613e9 (diff) | |
download | ydb-a361f5b98b98b44ea510d274f6769164640dd5e1.tar.gz |
metrics have been added
Diffstat (limited to 'contrib/libs/libmysql_r/sql')
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, >id_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 >id) { _add_gtid(gtid.sidno, gtid.gno); } + /** + Removes the given GTID from this Gtid_set. + + @param gtid Gtid to remove. + */ + void _remove_gtid(const Gtid >id) { _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 >id) 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 *>(>id_set->free_intervals); + } + /// Reset this iterator. + inline void init(Gtid_set_p gtid_set, rpl_sidno sidno) { + p = const_cast<Interval_p *>(>id_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(>id_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(>id_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 >id, 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 >id_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 >id, 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 >id, 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 >id) 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 >id) 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 >id) 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 >id); + /** + 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 >id, + 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 >ids_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 >id_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 = ¤t->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 == ¤t->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 = ¤t->next; + } + bool after(void *a, MEM_ROOT *mem_root) { + if (list->after(a, current, mem_root)) return true; + + current = current->next; + el = ¤t->next; + return false; + } + inline void **ref(void) // Get reference pointer + { + return ¤t->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 = ¤t->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 */ |