polardbxengine/storage/innobase/dict/dict0sdi.cc

491 lines
16 KiB
C++

/*****************************************************************************
Copyright (c) 2017, 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 <algorithm>
#include <current_thd.h>
#include <sql_class.h>
#include <sql_show.h>
#include <sql_table.h>
#include <sql_tablespace.h>
#include "api0api.h"
#include "api0misc.h"
#include "dict0boot.h"
#include "dict0crea.h"
#include "dict0dd.h"
#include "dict0dict.h"
#include "dict0sdi-decompress.h"
#include "dict0sdi.h"
#include "fsp0fsp.h"
#include "ha_innodb.h"
#include "trx0trx.h"
/** Check if SDI Index exists in a tablespace
@param[in] tablespace tablespace object
@param[in,out] space_id space_id from tablespace object
@return DB_SUCCESS if SDI exists, else return DB_ERROR,
DB_TABLESPACE_NOT_FOUND */
static dberr_t dict_sdi_exists(const dd::Tablespace &tablespace,
uint32 *space_id) {
if (tablespace.se_private_data().get(dd_space_key_strings[DD_SPACE_ID],
space_id)) {
/* error, attribute not found */
ut_ad(0);
return (DB_ERROR);
}
ut_ad(check_trx_exists(current_thd) != NULL);
if (fsp_is_undo_tablespace(*space_id) || fsp_is_system_temporary(*space_id)) {
/* Claim Success */
return (DB_SUCCESS);
}
return (fsp_has_sdi(*space_id));
}
/** Report error on failure
@param[in] operation SDI set or delete
@param[in] table table object for which SDI is serialized
@param[in] tablespace tablespace where SDI is stored */
static void dict_sdi_report_error(const char *operation, const dd::Table *table,
const dd::Tablespace &tablespace) {
THD *thd = current_thd;
const char *schema_name = nullptr;
const dd::Schema *schema = nullptr;
const char *table_name = nullptr;
if (thd != nullptr && table != nullptr) {
table_name = table->name().c_str();
if (thd->dd_client()->acquire(table->schema_id(), &schema)) {
schema_name = nullptr;
} else {
schema_name = schema->name().c_str();
}
}
if (schema_name == nullptr) {
schema_name = "<no schema>";
}
if (table_name == nullptr) {
table_name = "<no table>";
}
my_error(ER_SDI_OPERATION_FAILED, MYF(0), operation, schema_name, table_name,
tablespace.name().c_str());
}
/** Create SDI in a tablespace. This API should be used when
upgrading a tablespace with no SDI.
@param[in,out] tablespace tablespace object
@retval false success
@retval true failure */
bool dict_sdi_create(dd::Tablespace *tablespace) {
DBUG_EXECUTE_IF("ib_sdi", ib::info(ER_IB_MSG_213)
<< "SDI_CREATE: dict_sdi_create("
<< tablespace->name() << "," << tablespace->id()
<< ")";);
uint32 space_id = 0;
if (tablespace->se_private_data().get("id", &space_id)) {
/* error, attribute not found */
ut_ad(0);
return (true);
}
if (fsp_is_undo_tablespace(space_id) || fsp_is_system_temporary(space_id)) {
/* Upgrade calls sdi_create on dd::Tablespaces
registered. We shouldn't create SDI for
undo and temporary tablespace. */
return (false);
}
dberr_t err = ib_sdi_create(space_id);
/* If SDI index is created, update the tablespace flags in
dictionary */
if (err == DB_SUCCESS) {
fil_space_t *space = fil_space_acquire(space_id);
ut_ad(space != nullptr);
dd::Properties &p = tablespace->se_private_data();
p.set(dd_space_key_strings[DD_SPACE_FLAGS],
static_cast<uint32>(space->flags));
fil_space_release(space);
}
return (err != DB_SUCCESS);
}
/** Drop SDI in a tablespace. This API should be used only
when SDI is corrupted.
@param[in,out] tablespace tablespace object
@retval false success
@retval true failure */
bool dict_sdi_drop(dd::Tablespace *tablespace) {
#if 0 /* TODO: Enable in WL#9761 */
uint32 space_id;
if (dict_sdi_exists(tablespace, &space_id)
!= DB_SUCCESS) {
return(true);
}
dberr_t err = ib_sdi_drop(space_id);
return(err != DB_SUCCESS);
#endif /* TODO: Enable in WL#9761 */
ut_ad(0);
return (false);
}
/** Get the SDI keys in a tablespace into the vector provided.
@param[in] tablespace tablespace object
@param[in,out] vector vector to hold SDI keys
@retval false success
@retval true failure */
bool dict_sdi_get_keys(const dd::Tablespace &tablespace, sdi_vector_t &vector) {
#if 0 /* TODO: Enable in WL#9761 */
uint32 space_id;
if (dd_tablespace_is_discarded(&tablespace)) {
/* sdi_get_keys shouldn't be called on discarded tablespaces.*/
ut_ad(0);
}
if (dict_sdi_exists(tablespace, &space_id)
!= DB_SUCCESS) {
return(true);
}
#ifdef UNIV_DEBUG
if (fsp_is_undo_tablespace(space_id)
|| fsp_is_system_temporary(space_id)) {
/* There shouldn't be access for SDI on these tabelspaces as
SDI doesn't exist. */
ut_ad(0);
}
#endif /* UNIV_DEBUG */
ib_sdi_vector ib_vector;
ib_vector.sdi_vector = &vector;
trx_t* trx = check_trx_exists(current_thd);
trx_start_if_not_started(trx, true);
dberr_t err = ib_sdi_get_keys(space_id, &ib_vector, trx);
return(err != DB_SUCCESS);
#endif /* TODO: Enable in WL#9761 */
ut_ad(0);
return (false);
}
/** Retrieve SDI from tablespace
@param[in] tablespace tablespace object
@param[in] sdi_key SDI key
@param[in,out] sdi SDI retrieved from tablespace
@param[in,out] sdi_len in: size of memory allocated
out: actual length of SDI
@retval false success
@retval true failure */
bool dict_sdi_get(const dd::Tablespace &tablespace, const sdi_key_t *sdi_key,
void *sdi, uint64 *sdi_len) {
#if 0 /* TODO: Enable in WL#9761 */
DBUG_EXECUTE_IF("ib_sdi",
ib::info(ER_IB_MSG_214) << "dict_sdi_get(" << tablespace.name()
<< "," << tablespace.id()
<< " sdi_key: type: " << sdi_key->type
<< " id: " << sdi_key->id
<< ")";
);
if (dd_tablespace_is_discarded(&tablespace)) {
/* sdi_get shouldn't be called on discarded tablespaces.*/
ut_ad(0);
}
uint32 space_id;
if (dict_sdi_exists(tablespace, &space_id)
!= DB_SUCCESS) {
return(true);
}
#ifdef UNIV_DEBUG
if (fsp_is_undo_tablespace(space_id)
|| fsp_is_system_temporary(space_id)) {
/* There shouldn't be access for SDI on these tabelspaces as
SDI doesn't exist. */
ut_ad(0);
}
#endif /* UNIV_DEBUG */
trx_t* trx = check_trx_exists(current_thd);
trx_start_if_not_started(trx, true);
ib_sdi_key_t ib_sdi_key;
ib_sdi_key.sdi_key = sdi_key;
ut_ad(*sdi_len < UINT32_MAX);
uint32_t uncompressed_sdi_len;
uint32_t compressed_sdi_len = static_cast<uint32_t>(*sdi_len);
byte* compressed_sdi = static_cast<byte*>(
ut_malloc_nokey(compressed_sdi_len));
dberr_t err = ib_sdi_get(
space_id, &ib_sdi_key,
compressed_sdi, &compressed_sdi_len,
&uncompressed_sdi_len,
trx);
if (err == DB_OUT_OF_MEMORY) {
*sdi_len = uncompressed_sdi_len;
} else if (err != DB_SUCCESS) {
*sdi_len = UINT64_MAX;
} else {
*sdi_len = uncompressed_sdi_len;
/* Decompress the data */
Sdi_Decompressor decompressor(static_cast<byte*>(sdi),
uncompressed_sdi_len,
compressed_sdi,
compressed_sdi_len);
decompressor.decompress();
}
ut_free(compressed_sdi);
return(err != DB_SUCCESS);
#endif /* TODO: Enable in WL#9761 */
ut_ad(0);
return (false);
}
/** Insert/Update SDI in tablespace
@param[in] hton handlerton object
@param[in] tablespace tablespace object
@param[in] table table object
@param[in] sdi_key SDI key to uniquely identify the tablespace
object
@param[in] sdi SDI to be stored in tablespace
@param[in] sdi_len SDI length
@retval false success
@retval true failure */
bool dict_sdi_set(handlerton *hton, const dd::Tablespace &tablespace,
const dd::Table *table, const sdi_key_t *sdi_key,
const void *sdi, uint64 sdi_len) {
const char *operation = "set";
DBUG_EXECUTE_IF("ib_sdi", ib::info(ER_IB_MSG_215)
<< "dict_sdi_set(" << tablespace.name() << ","
<< tablespace.id()
<< " sdi_key: type: " << sdi_key->type
<< " id: " << sdi_key->id << ")";);
/* Used for testing purpose for DDLs from Memcached */
DBUG_EXECUTE_IF("skip_sdi", return (false););
if (dd_tablespace_is_discarded(&tablespace)) {
/* Claim success. */
return (false);
}
/* Check if dd:Table has valid se_private_id. In case of partitions,
all partitions should have valid se_private_id. If not, we cannot
proceed with storing SDI as the tablespace is not created yet. */
if (table && (table->se_private_id() == dd::INVALID_OBJECT_ID) &&
std::all_of(table->leaf_partitions().begin(),
table->leaf_partitions().end(), [](const dd::Partition *lp) {
return (lp->se_private_id() == dd::INVALID_OBJECT_ID);
})) {
/* This is a preliminary store of the object - before SE has
added SE-specific data. Cannot, and should not, store sdi at
this point. We should not throw error here. There will be SDI
store again with valid se_private_id/data */
DBUG_EXECUTE_IF(
"ib_sdi", ib::info(ER_IB_MSG_216)
<< "dict_sdi_set(" << tablespace.name() << ","
<< tablespace.id() << " sdi_key: type: " << sdi_key->type
<< " id: " << sdi_key->id << "): invalid se_private_id";);
return (false);
}
if (!tablespace.se_private_data().exists(dd_space_key_strings[DD_SPACE_ID])) {
/* Claim success, there will be one more sdi_set()
after the tablespace is created. */
return (false);
}
uint32 space_id;
dberr_t err = dict_sdi_exists(tablespace, &space_id);
if (err != DB_SUCCESS) {
if (err == DB_TABLESPACE_NOT_FOUND) {
/* Claim Success */
return (false);
} else {
ut_ad(0);
dict_sdi_report_error(operation, table, tablespace);
return (true);
}
}
if (fsp_is_undo_tablespace(space_id) || fsp_is_system_temporary(space_id)) {
/* Claim Success */
return (false);
}
trx_t *trx = check_trx_exists(current_thd);
trx_start_if_not_started(trx, true);
innobase_register_trx(hton, current_thd, trx);
ib_sdi_key_t ib_sdi_key;
ib_sdi_key.sdi_key = sdi_key;
Sdi_Compressor compressor(static_cast<uint32_t>(sdi_len), sdi);
compressor.compress();
err = ib_sdi_set(space_id, &ib_sdi_key, static_cast<uint32_t>(sdi_len),
compressor.get_comp_len(), compressor.get_data(), trx);
DBUG_EXECUTE_IF("sdi_set_failure",
dict_sdi_report_error(operation, table, tablespace);
return (true););
if (err == DB_INTERRUPTED) {
my_error(ER_QUERY_INTERRUPTED, MYF(0));
DBUG_EXECUTE_IF("ib_sdi",
ib::info(ER_IB_MSG_217)
<< "dict_sdi_set: " << tablespace.name() << ","
<< tablespace.id() << " InnoDB space_id: " << space_id
<< " sdi_key: type: " << sdi_key->type
<< " id: " << sdi_key->id << " trx_id: " << trx->id
<< " is interrupted";);
return (true);
} else if (err != DB_SUCCESS) {
ut_ad(0);
dict_sdi_report_error(operation, table, tablespace);
return (true);
} else {
return (false);
}
}
/** Delete SDI from tablespace
@param[in] tablespace tablespace object
@param[in] table table object
@param[in] sdi_key SDI key to uniquely identify the tablespace
object
@retval false success
@retval true failure */
bool dict_sdi_delete(const dd::Tablespace &tablespace, const dd::Table *table,
const sdi_key_t *sdi_key) {
const char *operation = "delete";
DBUG_EXECUTE_IF("ib_sdi", ib::info(ER_IB_MSG_218)
<< "dict_sdi_delete(" << tablespace.name()
<< "," << tablespace.id()
<< " sdi_key: type: " << sdi_key->type
<< " id: " << sdi_key->id << ")";);
/* Used for testing purpose for DDLs from Memcached */
DBUG_EXECUTE_IF("skip_sdi", return (false););
if (dd_tablespace_is_discarded(&tablespace)) {
/* Claim success. */
return (false);
}
/* Check if dd:Table has valid se_private_id. In case of partitions,
all partitions should have valid se_private_id. If not, we cannot
proceed with storing SDI as the tablespace is not created yet. */
if (table && (table->se_private_id() == dd::INVALID_OBJECT_ID) &&
std::all_of(table->leaf_partitions().begin(),
table->leaf_partitions().end(), [](const dd::Partition *p) {
return (p->se_private_id() == dd::INVALID_OBJECT_ID);
})) {
/* This is a preliminary store of the object - before SE has
added SE-specific data. Cannot, and should not, store sdi at
this point. We should not throw error here. There will be SDI
store again with valid se_private_id/data */
DBUG_EXECUTE_IF(
"ib_sdi", ib::info(ER_IB_MSG_219)
<< "dict_sdi_delete(" << tablespace.name() << ","
<< tablespace.id() << " sdi_key: type: " << sdi_key->type
<< " id: " << sdi_key->id << "): invalid se_private_id";);
return (false);
}
uint32 space_id;
dberr_t err = dict_sdi_exists(tablespace, &space_id);
if (err != DB_SUCCESS) {
if (err == DB_TABLESPACE_NOT_FOUND) {
/* Claim Success */
return (false);
} else {
ut_ad(0);
dict_sdi_report_error(operation, table, tablespace);
return (true);
}
}
if (fsp_is_undo_tablespace(space_id) || fsp_is_system_temporary(space_id)) {
/* Claim Success */
return (false);
}
trx_t *trx = check_trx_exists(current_thd);
trx_start_if_not_started(trx, true);
ib_sdi_key_t ib_sdi_key;
ib_sdi_key.sdi_key = sdi_key;
err = ib_sdi_delete(space_id, &ib_sdi_key, trx);
DBUG_EXECUTE_IF("sdi_delete_failure",
dict_sdi_report_error(operation, table, tablespace);
return (true););
if (err == DB_INTERRUPTED) {
my_error(ER_QUERY_INTERRUPTED, MYF(0));
DBUG_EXECUTE_IF("ib_sdi",
ib::info(ER_IB_MSG_220)
<< "dict_sdi_delete(" << tablespace.name() << ","
<< tablespace.id() << " InnoDB space_id: " << space_id
<< " sdi_key: type: " << sdi_key->type
<< " id: " << sdi_key->id << " trx_id: " << trx->id
<< " is interrupted";);
return (true);
} else if (err != DB_SUCCESS) {
dict_sdi_report_error(operation, table, tablespace);
return (true);
} else {
return (false);
}
}