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
 |