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/types/weak_object_impl.h"
24
25 #include <memory>
26
27 #include "my_dbug.h"
28 #include "my_inttypes.h"
29 #include "my_loglevel.h"
30 #include "my_sys.h"
31 #include "mysql/components/services/log_builtins.h"
32 #include "mysqld_error.h" // ER_*
33 #include "sql/dd/impl/object_key.h" // Needed for destructor
34 #include "sql/dd/impl/raw/raw_record.h" // Raw_record
35 #include "sql/dd/impl/raw/raw_table.h" // Raw_table
36 #include "sql/dd/impl/transaction_impl.h" // Open_dictionary_tables_ctx
37 #include "sql/dd/impl/types/entity_object_impl.h"
38 #include "sql/dd/string_type.h"
39 #include "sql/dd/types/object_table.h" // Object_table
40 #include "sql/debug_sync.h" // DEBUG_SYNC
41 #include "sql/log.h"
42 #include "sql/sql_class.h" // current_thd, THD
43
44 namespace dd {
45
46 ///////////////////////////////////////////////////////////////////////////
47
48 /**
49 @brief
50 Store the DD object into DD table.
51
52 @param otx - DD transaction in use.
53
54 @return true - on failure and error is reported.
55 @return false - on success.
56 */
store(Open_dictionary_tables_ctx * otx)57 bool Weak_object_impl::store(Open_dictionary_tables_ctx *otx) {
58 DBUG_TRACE;
59
60 DBUG_EXECUTE_IF("fail_while_storing_dd_object", {
61 my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
62 return true;
63 });
64
65 const Object_table &obj_table = this->object_table();
66
67 // Get main object table.
68
69 Raw_table *t = otx->get_table(obj_table.name());
70
71 DBUG_ASSERT(t);
72
73 // Insert or update record.
74
75 do {
76 /*
77 If we know that object has new primary key (e.g. to be generated
78 at insert time) we can skip looking up and updating old record.
79 This measure greatly reduces probability of InnoDB deadlocks between
80 concurrent DDL. Deadlocks occur because each of concurrent DDL
81 first looks up record with non-existing PK (e.g. INVALID_OBJECT_ID
82 or value greater than existing PK values for non-Entity objects) and
83 this acquires gap lock on supremum record and then tries to insert
84 row into this gap.
85 */
86 if (this->has_new_primary_key()) break;
87
88 std::unique_ptr<Object_key> obj_key(this->create_primary_key());
89
90 if (!obj_key.get()) {
91 /* purecov: begin deadcode */
92 LogErr(ERROR_LEVEL, ER_DD_CANT_GET_OBJECT_KEY);
93 DBUG_ASSERT(false);
94 return true;
95 /* purecov: end */
96 }
97
98 std::unique_ptr<Raw_record> r;
99 if (t->prepare_record_for_update(*obj_key, r)) return true;
100
101 if (!r.get()) break;
102
103 // Existing record found -- do an UPDATE.
104
105 if (this->store_attributes(r.get())) {
106 my_error(ER_UPDATING_DD_TABLE, MYF(0), obj_table.name().c_str());
107 return true;
108 }
109
110 if (r->update()) return true;
111
112 return store_children(otx);
113 } while (false);
114
115 // No existing record exists -- do an INSERT.
116
117 std::unique_ptr<Raw_new_record> r(t->prepare_record_for_insert());
118
119 // Store attributes.
120
121 if (this->store_attributes(r.get())) {
122 my_error(ER_UPDATING_DD_TABLE, MYF(0), obj_table.name().c_str());
123 return true;
124 }
125
126 DEBUG_SYNC(current_thd, "before_insert_into_dd");
127
128 if (r->insert()) return true;
129
130 DBUG_EXECUTE_IF("weak_object_impl_store_fail_before_store_children", {
131 my_error(ER_UNKNOWN_ERROR, MYF(0));
132 return true;
133 });
134
135 this->set_primary_key_value(*r);
136
137 /*
138 It is necessary to destroy the Raw_new_record() object after
139 inserting the parent DD object and before creating children
140 DD object. The reason is that, the parent DD object may
141 insert a row in the same DD table (E.g., when storing parent
142 partition metadata in mysql.partitions), in which even the
143 child DD table would end-up inserting a row. (e.g., where
144 storing sub partition metadata in mysql.partitions)
145 Destroying Raw_new_record() enable accurate computation of
146 the auto increment values, for the child partition entry
147 being stored.
148 */
149 r.reset();
150
151 if (store_children(otx)) return true;
152
153 /*
154 Mark object as having existing PK only after processing its children.
155 This allows non-entity children to rely on parent has_new_primary_key()
156 method to figure out if their primary key based on parent's one was not
157 used before.
158 */
159 this->fix_has_new_primary_key();
160
161 return false;
162 }
163
164 ///////////////////////////////////////////////////////////////////////////
165
166 /**
167 @brief
168 Drop the DD object from DD table.
169
170 @param otx - DD transaction in use.
171
172 @return true - on failure and error is reported.
173 @return false - on success.
174 */
drop(Open_dictionary_tables_ctx * otx) const175 bool Weak_object_impl::drop(Open_dictionary_tables_ctx *otx) const {
176 DBUG_TRACE;
177
178 DBUG_EXECUTE_IF("fail_while_dropping_dd_object", {
179 my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
180 return true;
181 });
182
183 const Object_table &obj_table = this->object_table();
184
185 // Get main object table.
186
187 Raw_table *t = otx->get_table(obj_table.name());
188
189 DBUG_ASSERT(t);
190
191 // Find object to be dropped
192
193 std::unique_ptr<Object_key> obj_key(this->create_primary_key());
194
195 std::unique_ptr<Raw_record> r;
196 if (t->prepare_record_for_update(*obj_key, r)) return true;
197
198 if (!r.get()) {
199 /* purecov: begin deadcode */
200 LogErr(ERROR_LEVEL, ER_DD_CANT_CREATE_OBJECT_KEY);
201 DBUG_ASSERT(false);
202 return true;
203 /* purecov: end */
204 }
205
206 /**
207 Drop collections and then drop the object
208
209 We should drop collections first and then parent object
210 as we have referencial constraints. Mostly the reverse
211 order of restore/store operation.
212 */
213
214 if (this->drop_children(otx) || r->drop()) return true;
215
216 return false;
217 }
218
219 ///////////////////////////////////////////////////////////////////////////
220
check_parent_consistency(Entity_object_impl * parent,Object_id parent_id) const221 bool Weak_object_impl::check_parent_consistency(Entity_object_impl *parent,
222 Object_id parent_id) const {
223 DBUG_ASSERT(parent);
224 DBUG_ASSERT(parent->id() == parent_id);
225
226 if (!parent) {
227 my_error(ER_INVALID_DD_OBJECT, MYF(0), this->object_table().name().c_str(),
228 "Invalid parent reference (NULL).");
229 return true;
230 }
231
232 if (parent->id() != parent_id) {
233 my_error(ER_INVALID_DD_OBJECT, MYF(0), this->object_table().name().c_str(),
234 "Invalid parent ID");
235
236 return true;
237 }
238
239 return false;
240 }
241
242 ///////////////////////////////////////////////////////////////////////////
243
244 } // namespace dd
245