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