308 lines
11 KiB
C++
308 lines
11 KiB
C++
/* 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 */
|
|
|
|
#include "sql/dd/impl/tables/dd_properties.h"
|
|
|
|
#include <string>
|
|
|
|
#include "m_ctype.h"
|
|
#include "my_base.h"
|
|
#include "my_bitmap.h"
|
|
#include "my_dbug.h"
|
|
#include "my_inttypes.h"
|
|
#include "my_sys.h"
|
|
#include "mysql/udf_registration_types.h"
|
|
#include "mysqld_error.h"
|
|
#include "sql/auth/sql_security_ctx.h"
|
|
#include "sql/dd/dd_version.h" // dd::DD_VERSION
|
|
#include "sql/dd/impl/raw/raw_table.h"
|
|
#include "sql/dd/impl/transaction_impl.h"
|
|
#include "sql/dd/impl/types/object_table_definition_impl.h"
|
|
#include "sql/dd/properties.h"
|
|
#include "sql/dd/string_type.h" // dd::String_type, dd::Stringstream_type
|
|
#include "sql/field.h"
|
|
#include "sql/handler.h"
|
|
#include "sql/sql_const.h"
|
|
#include "sql/stateless_allocator.h"
|
|
#include "sql/table.h"
|
|
#include "sql_string.h"
|
|
|
|
namespace dd {
|
|
namespace tables {
|
|
|
|
DD_properties &DD_properties::instance() {
|
|
static DD_properties *s_instance = new (std::nothrow) DD_properties();
|
|
return *s_instance;
|
|
}
|
|
|
|
// Setup the initial definition of mysql.dd_properties table.
|
|
DD_properties::DD_properties() : m_properties() {
|
|
m_target_def.set_table_name("dd_properties");
|
|
|
|
m_target_def.add_field(FIELD_PROPERTIES, "FIELD_PROPERTIES",
|
|
"properties MEDIUMBLOB");
|
|
|
|
m_target_def.add_populate_statement(
|
|
"INSERT INTO dd_properties (properties)"
|
|
"VALUES ('DD_VERSION=0')");
|
|
|
|
/*
|
|
Initialize the descriptors of the valid keys. The keys are used for
|
|
the following purposes:
|
|
|
|
DD_VERSION Actual DD version.
|
|
IS_VERSION Actual I_S version.
|
|
PS_VERSION Actual P_S version.
|
|
SDI_VERSION Actual SDI version.
|
|
LCTN L_C_T_N setting used during
|
|
--initialize.
|
|
MYSQLD_VERSION_LO Lowest server version which has
|
|
been using the data directory.
|
|
MYSQLD_VERSION_HI Highest server version which has
|
|
been using the data directory.
|
|
MYSQLD_VERSION Current server version.
|
|
MINOR_DOWNGRADE_THRESHOLD The current DD can be used by
|
|
previous MRUs, unless their
|
|
target DD version is less than
|
|
the downgrade threshold.
|
|
SYSTEM_TABLES List of system tables with
|
|
definitions.
|
|
UPGRADE_TARGET_SCHEMA Temporary schema used during
|
|
upgrade.
|
|
UPGRADE_ACTUAL_SCHEMA Temporary schema used during
|
|
upgrade.
|
|
MYSQLD_VERSION_UPGRADED The server version of the last
|
|
completed successful upgrade.
|
|
*/
|
|
m_property_desc = {
|
|
{"DD_VERSION", Property_type::UNSIGNED_INT_32},
|
|
{"IS_VERSION", Property_type::UNSIGNED_INT_32},
|
|
{"PS_VERSION", Property_type::UNSIGNED_INT_32},
|
|
{"SDI_VERSION", Property_type::UNSIGNED_INT_32},
|
|
{"LCTN", Property_type::UNSIGNED_INT_32},
|
|
{"MYSQLD_VERSION_LO", Property_type::UNSIGNED_INT_32},
|
|
{"MYSQLD_VERSION_HI", Property_type::UNSIGNED_INT_32},
|
|
{"MYSQLD_VERSION", Property_type::UNSIGNED_INT_32},
|
|
{"MINOR_DOWNGRADE_THRESHOLD", Property_type::UNSIGNED_INT_32},
|
|
{"SYSTEM_TABLES", Property_type::PROPERTIES},
|
|
{"UPGRADE_TARGET_SCHEMA", Property_type::CHARACTER_STRING},
|
|
{"UPGRADE_ACTUAL_SCHEMA", Property_type::CHARACTER_STRING},
|
|
{"EXTRA_IS_VERSION", Property_type::UNSIGNED_INT_32},
|
|
{"EXTRA_PS_VERSION", Property_type::UNSIGNED_INT_32},
|
|
{"MYSQLD_VERSION_UPGRADED", Property_type::UNSIGNED_INT_32},
|
|
{"EXTRA_MYSQLD_VERSION_UPGRADED", Property_type::UNSIGNED_INT_32}};
|
|
}
|
|
|
|
// Read all properties from disk and populate the cache.
|
|
bool DD_properties::init_cached_properties(THD *thd) {
|
|
// Early exit in case the properties are already initialized.
|
|
if (!m_properties.empty()) return false;
|
|
|
|
/*
|
|
Start a DD transaction to get the properties. Please note that we
|
|
must do this read using isolation level ISO_READ_UNCOMMITTED
|
|
because the SE undo logs may not yet be available.
|
|
*/
|
|
Transaction_ro trx(thd, ISO_READ_UNCOMMITTED);
|
|
trx.otx.add_table<DD_properties>();
|
|
|
|
if (trx.otx.open_tables()) {
|
|
DBUG_ASSERT(false);
|
|
return true;
|
|
}
|
|
|
|
Raw_table *raw_t = trx.otx.get_table(name());
|
|
DBUG_ASSERT(raw_t);
|
|
TABLE *t = raw_t->get_table();
|
|
DBUG_ASSERT(t);
|
|
t->use_all_columns();
|
|
|
|
/*
|
|
We should not read from this table until after it has been populated,
|
|
so there should always be a row stored. We read the row, and populate
|
|
the property cache based on its contents.
|
|
*/
|
|
if (t->file->ha_rnd_init(true) || t->file->ha_rnd_next(t->record[0])) {
|
|
DBUG_ASSERT(false);
|
|
t->file->ha_rnd_end();
|
|
return true;
|
|
}
|
|
|
|
String val;
|
|
t->field[FIELD_PROPERTIES]->val_str(&val);
|
|
m_properties.insert_values(val.c_ptr_safe());
|
|
|
|
t->file->ha_rnd_end();
|
|
return (m_properties.empty());
|
|
}
|
|
|
|
// Flush all properties from the cache to disk.
|
|
bool DD_properties::flush_cached_properties(THD *thd) {
|
|
DBUG_ASSERT(!m_properties.empty());
|
|
|
|
Update_dictionary_tables_ctx ctx(thd);
|
|
ctx.otx.add_table<DD_properties>();
|
|
|
|
if (ctx.otx.open_tables()) return true;
|
|
|
|
Raw_table *raw_t = ctx.otx.get_table(name());
|
|
DBUG_ASSERT(raw_t);
|
|
TABLE *t = raw_t->get_table();
|
|
DBUG_ASSERT(t);
|
|
t->use_all_columns();
|
|
bitmap_set_all(t->write_set);
|
|
bitmap_set_all(t->read_set);
|
|
|
|
int rc = 0;
|
|
if ((rc = t->file->ha_rnd_init(true))) {
|
|
t->file->print_error(rc, MYF(0));
|
|
t->file->ha_rnd_end();
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
If a row is already stored, then we update it. If no row is stored,
|
|
it means we an error situation, since we should not write to the table
|
|
until after its populate SQL statement has been executed.
|
|
*/
|
|
if ((rc = t->file->ha_rnd_next(t->record[0]))) {
|
|
DBUG_ASSERT(false);
|
|
t->file->ha_rnd_end();
|
|
return true;
|
|
}
|
|
|
|
store_record(t, record[1]);
|
|
const String_type &prop_str = m_properties.raw_string();
|
|
t->field[FIELD_PROPERTIES]->store(prop_str.c_str(), prop_str.length(),
|
|
system_charset_info);
|
|
rc = t->file->ha_update_row(t->record[1], t->record[0]);
|
|
t->file->ha_rnd_end();
|
|
|
|
if (rc && rc != HA_ERR_RECORD_IS_THE_SAME) {
|
|
t->file->print_error(rc, MYF(0));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Initialize the cache, and read the property value for the given key
|
|
without checking or validating the key.
|
|
*/
|
|
bool DD_properties::unchecked_get(THD *thd, const String_type &key,
|
|
String_type *value, bool *exists) {
|
|
if (init_cached_properties(thd)) return true;
|
|
|
|
*exists = m_properties.exists(key);
|
|
if (*exists) m_properties.get(key, value);
|
|
|
|
if (*exists == false && key == "DD_VERSION") {
|
|
*exists = m_properties.exists("DD_version");
|
|
if (*exists) m_properties.get("DD_version", value);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Initialize the cache, and set the property value for the given key
|
|
without checking or validating the key. Flush the cache to disk in
|
|
order to make the value persistent.
|
|
*/
|
|
bool DD_properties::unchecked_set(THD *thd, const String_type &key,
|
|
const String_type &value) {
|
|
// Read cached properties from disk, if not existing.
|
|
if (init_cached_properties(thd)) return true;
|
|
|
|
// Update the cached properties and the table.
|
|
m_properties.set(key, value);
|
|
return flush_cached_properties(thd);
|
|
}
|
|
|
|
// Read the integer property for the given key.
|
|
bool DD_properties::get(THD *thd, const String_type &key, uint *value,
|
|
bool *exists) {
|
|
DBUG_ASSERT(m_property_desc.find(key) != m_property_desc.end() &&
|
|
m_property_desc[key] == Property_type::UNSIGNED_INT_32);
|
|
String_type val_str;
|
|
return unchecked_get(thd, key, &val_str, exists) ||
|
|
dd::Properties::from_str(val_str, value);
|
|
}
|
|
|
|
// Set the integer property for the given key.
|
|
bool DD_properties::set(THD *thd, const String_type &key, uint value) {
|
|
DBUG_ASSERT(m_property_desc.find(key) != m_property_desc.end() &&
|
|
m_property_desc[key] == Property_type::UNSIGNED_INT_32);
|
|
return unchecked_set(thd, key, dd::Properties::to_str(value));
|
|
}
|
|
|
|
// Read the character string property for the given key.
|
|
bool DD_properties::get(THD *thd, const String_type &key, String_type *value,
|
|
bool *exists) {
|
|
DBUG_ASSERT(m_property_desc.find(key) != m_property_desc.end() &&
|
|
m_property_desc[key] == Property_type::CHARACTER_STRING);
|
|
return unchecked_get(thd, key, value, exists);
|
|
}
|
|
|
|
// Set the character string property for the given key.
|
|
bool DD_properties::set(THD *thd, const String_type &key,
|
|
const String_type &value) {
|
|
DBUG_ASSERT(m_property_desc.find(key) != m_property_desc.end() &&
|
|
m_property_desc[key] == Property_type::CHARACTER_STRING);
|
|
return unchecked_set(thd, key, value);
|
|
}
|
|
|
|
// Read the properties object for the given key.
|
|
bool DD_properties::get(THD *thd, const String_type &key,
|
|
std::unique_ptr<Properties> *properties, bool *exists) {
|
|
DBUG_ASSERT(m_property_desc.find(key) != m_property_desc.end() &&
|
|
m_property_desc[key] == Property_type::PROPERTIES);
|
|
|
|
String_type property_string;
|
|
if (unchecked_get(thd, key, &property_string, exists)) return true;
|
|
|
|
properties->reset(Properties::parse_properties(property_string.c_str()));
|
|
return false;
|
|
}
|
|
|
|
// Set the property object for the given key.
|
|
bool DD_properties::set(THD *thd, const String_type &key,
|
|
const dd::Properties &properties) {
|
|
DBUG_ASSERT(m_property_desc.find(key) != m_property_desc.end() &&
|
|
m_property_desc[key] == Property_type::PROPERTIES);
|
|
return unchecked_set(thd, key, properties.raw_string());
|
|
}
|
|
|
|
// Initialize the cache, and remove the submitted key if it exists.
|
|
bool DD_properties::remove(THD *thd, const String_type &key) {
|
|
// Read cached properties from disk, if not existing.
|
|
if (init_cached_properties(thd)) return true;
|
|
|
|
// Update the cached properties and the table.
|
|
if (m_properties.exists(key)) (void)m_properties.remove(key);
|
|
return flush_cached_properties(thd);
|
|
}
|
|
|
|
} // namespace tables
|
|
} // namespace dd
|