1 /* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include "sql/dd/impl/tables/dd_properties.h"
24
25 #include <string>
26
27 #include "m_ctype.h"
28 #include "my_base.h"
29 #include "my_bitmap.h"
30 #include "my_dbug.h"
31 #include "my_inttypes.h"
32 #include "my_sys.h"
33 #include "mysql/udf_registration_types.h"
34 #include "mysqld_error.h"
35 #include "sql/auth/sql_security_ctx.h"
36 #include "sql/dd/dd_version.h" // dd::DD_VERSION
37 #include "sql/dd/impl/raw/raw_table.h"
38 #include "sql/dd/impl/transaction_impl.h"
39 #include "sql/dd/impl/types/object_table_definition_impl.h"
40 #include "sql/dd/properties.h"
41 #include "sql/dd/string_type.h" // dd::String_type, dd::Stringstream_type
42 #include "sql/field.h"
43 #include "sql/handler.h"
44 #include "sql/sql_const.h"
45 #include "sql/stateless_allocator.h"
46 #include "sql/table.h"
47 #include "sql_string.h"
48
49 namespace dd {
50 namespace tables {
51
instance()52 DD_properties &DD_properties::instance() {
53 static DD_properties *s_instance = new (std::nothrow) DD_properties();
54 return *s_instance;
55 }
56
57 // Setup the initial definition of mysql.dd_properties table.
DD_properties()58 DD_properties::DD_properties() : m_properties() {
59 m_target_def.set_table_name("dd_properties");
60
61 m_target_def.add_field(FIELD_PROPERTIES, "FIELD_PROPERTIES",
62 "properties MEDIUMBLOB");
63
64 m_target_def.add_populate_statement(
65 "INSERT INTO dd_properties (properties)"
66 "VALUES ('DD_VERSION=0')");
67
68 /*
69 Initialize the descriptors of the valid keys. The keys are used for
70 the following purposes:
71
72 DD_VERSION Actual DD version.
73 IS_VERSION Actual I_S version.
74 PS_VERSION Actual P_S version.
75 NDBINFO_VERSION Actual ndbinfo version.
76 SDI_VERSION Actual SDI version.
77 LCTN L_C_T_N setting used during
78 --initialize.
79 MYSQLD_VERSION_LO Lowest server version which has
80 been using the data directory.
81 MYSQLD_VERSION_HI Highest server version which has
82 been using the data directory.
83 MYSQLD_VERSION Current server version.
84 MINOR_DOWNGRADE_THRESHOLD The current DD can be used by
85 previous MRUs, unless their
86 target DD version is less than
87 the downgrade threshold.
88 SYSTEM_TABLES List of system tables with
89 definitions.
90 UPGRADE_TARGET_SCHEMA Temporary schema used during
91 upgrade.
92 UPGRADE_ACTUAL_SCHEMA Temporary schema used during
93 upgrade.
94 MYSQLD_VERSION_UPGRADED The server version of the last
95 completed successful upgrade.
96 */
97 m_property_desc = {
98 {"DD_VERSION", Property_type::UNSIGNED_INT_32},
99 {"IS_VERSION", Property_type::UNSIGNED_INT_32},
100 {"PS_VERSION", Property_type::UNSIGNED_INT_32},
101 {"NDBINFO_VERSION", Property_type::UNSIGNED_INT_32},
102 {"SDI_VERSION", Property_type::UNSIGNED_INT_32},
103 {"LCTN", Property_type::UNSIGNED_INT_32},
104 {"MYSQLD_VERSION_LO", Property_type::UNSIGNED_INT_32},
105 {"MYSQLD_VERSION_HI", Property_type::UNSIGNED_INT_32},
106 {"MYSQLD_VERSION", Property_type::UNSIGNED_INT_32},
107 {"MINOR_DOWNGRADE_THRESHOLD", Property_type::UNSIGNED_INT_32},
108 {"SYSTEM_TABLES", Property_type::PROPERTIES},
109 {"UPGRADE_TARGET_SCHEMA", Property_type::CHARACTER_STRING},
110 {"UPGRADE_ACTUAL_SCHEMA", Property_type::CHARACTER_STRING},
111 {"MYSQLD_VERSION_UPGRADED", Property_type::UNSIGNED_INT_32}};
112 }
113
114 // Read all properties from disk and populate the cache.
init_cached_properties(THD * thd)115 bool DD_properties::init_cached_properties(THD *thd) {
116 // Early exit in case the properties are already initialized.
117 if (!m_properties.empty()) return false;
118
119 /*
120 Start a DD transaction to get the properties. Please note that we
121 must do this read using isolation level ISO_READ_UNCOMMITTED
122 because the SE undo logs may not yet be available.
123 */
124 Transaction_ro trx(thd, ISO_READ_UNCOMMITTED);
125 trx.otx.add_table<DD_properties>();
126
127 if (trx.otx.open_tables()) {
128 DBUG_ASSERT(false);
129 return true;
130 }
131
132 Raw_table *raw_t = trx.otx.get_table(name());
133 DBUG_ASSERT(raw_t);
134 TABLE *t = raw_t->get_table();
135 DBUG_ASSERT(t);
136 t->use_all_columns();
137
138 /*
139 We should not read from this table until after it has been populated,
140 so there should always be a row stored. We read the row, and populate
141 the property cache based on its contents.
142 */
143 if (t->file->ha_rnd_init(true) || t->file->ha_rnd_next(t->record[0])) {
144 DBUG_ASSERT(false);
145 t->file->ha_rnd_end();
146 return true;
147 }
148
149 String val;
150 t->field[FIELD_PROPERTIES]->val_str(&val);
151 m_properties.insert_values(val.c_ptr_safe());
152
153 t->file->ha_rnd_end();
154 return (m_properties.empty());
155 }
156
157 // Flush all properties from the cache to disk.
flush_cached_properties(THD * thd)158 bool DD_properties::flush_cached_properties(THD *thd) {
159 DBUG_ASSERT(!m_properties.empty());
160
161 Update_dictionary_tables_ctx ctx(thd);
162 ctx.otx.add_table<DD_properties>();
163
164 if (ctx.otx.open_tables()) return true;
165
166 Raw_table *raw_t = ctx.otx.get_table(name());
167 DBUG_ASSERT(raw_t);
168 TABLE *t = raw_t->get_table();
169 DBUG_ASSERT(t);
170 t->use_all_columns();
171 bitmap_set_all(t->write_set);
172 bitmap_set_all(t->read_set);
173
174 int rc = 0;
175 if ((rc = t->file->ha_rnd_init(true))) {
176 t->file->print_error(rc, MYF(0));
177 t->file->ha_rnd_end();
178 return true;
179 }
180
181 /*
182 If a row is already stored, then we update it. If no row is stored,
183 it means we an error situation, since we should not write to the table
184 until after its populate SQL statement has been executed.
185 */
186 if ((rc = t->file->ha_rnd_next(t->record[0]))) {
187 DBUG_ASSERT(false);
188 t->file->ha_rnd_end();
189 return true;
190 }
191
192 store_record(t, record[1]);
193 const String_type &prop_str = m_properties.raw_string();
194 t->field[FIELD_PROPERTIES]->store(prop_str.c_str(), prop_str.length(),
195 system_charset_info);
196 rc = t->file->ha_update_row(t->record[1], t->record[0]);
197 t->file->ha_rnd_end();
198
199 if (rc && rc != HA_ERR_RECORD_IS_THE_SAME) {
200 t->file->print_error(rc, MYF(0));
201 return true;
202 }
203
204 return false;
205 }
206
207 /*
208 Initialize the cache, and read the property value for the given key
209 without checking or validating the key.
210 */
unchecked_get(THD * thd,const String_type & key,String_type * value,bool * exists)211 bool DD_properties::unchecked_get(THD *thd, const String_type &key,
212 String_type *value, bool *exists) {
213 if (init_cached_properties(thd)) return true;
214
215 *exists = m_properties.exists(key);
216 if (*exists) m_properties.get(key, value);
217
218 if (*exists == false && key == "DD_VERSION") {
219 *exists = m_properties.exists("DD_version");
220 if (*exists) m_properties.get("DD_version", value);
221 }
222
223 return false;
224 }
225
226 /*
227 Initialize the cache, and set the property value for the given key
228 without checking or validating the key. Flush the cache to disk in
229 order to make the value persistent.
230 */
unchecked_set(THD * thd,const String_type & key,const String_type & value)231 bool DD_properties::unchecked_set(THD *thd, const String_type &key,
232 const String_type &value) {
233 // Read cached properties from disk, if not existing.
234 if (init_cached_properties(thd)) return true;
235
236 // Update the cached properties and the table.
237 m_properties.set(key, value);
238 return flush_cached_properties(thd);
239 }
240
241 // Read the integer property for the given key.
get(THD * thd,const String_type & key,uint * value,bool * exists)242 bool DD_properties::get(THD *thd, const String_type &key, uint *value,
243 bool *exists) {
244 DBUG_ASSERT(m_property_desc.find(key) != m_property_desc.end() &&
245 m_property_desc[key] == Property_type::UNSIGNED_INT_32);
246 String_type val_str;
247 return unchecked_get(thd, key, &val_str, exists) ||
248 dd::Properties::from_str(val_str, value);
249 }
250
251 // Set the integer property for the given key.
set(THD * thd,const String_type & key,uint value)252 bool DD_properties::set(THD *thd, const String_type &key, uint value) {
253 DBUG_ASSERT(m_property_desc.find(key) != m_property_desc.end() &&
254 m_property_desc[key] == Property_type::UNSIGNED_INT_32);
255 return unchecked_set(thd, key, dd::Properties::to_str(value));
256 }
257
258 // Read the character string property for the given key.
get(THD * thd,const String_type & key,String_type * value,bool * exists)259 bool DD_properties::get(THD *thd, const String_type &key, String_type *value,
260 bool *exists) {
261 DBUG_ASSERT(m_property_desc.find(key) != m_property_desc.end() &&
262 m_property_desc[key] == Property_type::CHARACTER_STRING);
263 return unchecked_get(thd, key, value, exists);
264 }
265
266 // Set the character string property for the given key.
set(THD * thd,const String_type & key,const String_type & value)267 bool DD_properties::set(THD *thd, const String_type &key,
268 const String_type &value) {
269 DBUG_ASSERT(m_property_desc.find(key) != m_property_desc.end() &&
270 m_property_desc[key] == Property_type::CHARACTER_STRING);
271 return unchecked_set(thd, key, value);
272 }
273
274 // Read the properties object for the given key.
get(THD * thd,const String_type & key,std::unique_ptr<Properties> * properties,bool * exists)275 bool DD_properties::get(THD *thd, const String_type &key,
276 std::unique_ptr<Properties> *properties, bool *exists) {
277 DBUG_ASSERT(m_property_desc.find(key) != m_property_desc.end() &&
278 m_property_desc[key] == Property_type::PROPERTIES);
279
280 String_type property_string;
281 if (unchecked_get(thd, key, &property_string, exists)) return true;
282
283 properties->reset(Properties::parse_properties(property_string.c_str()));
284 return false;
285 }
286
287 // Set the property object for the given key.
set(THD * thd,const String_type & key,const dd::Properties & properties)288 bool DD_properties::set(THD *thd, const String_type &key,
289 const dd::Properties &properties) {
290 DBUG_ASSERT(m_property_desc.find(key) != m_property_desc.end() &&
291 m_property_desc[key] == Property_type::PROPERTIES);
292 return unchecked_set(thd, key, properties.raw_string());
293 }
294
295 // Initialize the cache, and remove the submitted key if it exists.
remove(THD * thd,const String_type & key)296 bool DD_properties::remove(THD *thd, const String_type &key) {
297 // Read cached properties from disk, if not existing.
298 if (init_cached_properties(thd)) return true;
299
300 // Update the cached properties and the table.
301 if (m_properties.exists(key)) (void)m_properties.remove(key);
302 return flush_cached_properties(thd);
303 }
304
305 } // namespace tables
306 } // namespace dd
307