1 /*
2    Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 #ifndef NDB_SCHEMA_DIST_H
26 #define NDB_SCHEMA_DIST_H
27 
28 #include <string>
29 #include <vector>
30 
31 #include "my_inttypes.h"
32 
33 /**
34   The numbers below must not change as they are passed
35   between MySQL servers as part of the schema distribution
36   protocol. Changes would break compatibility between versions.
37   Add new numbers to the end.
38 */
39 enum SCHEMA_OP_TYPE {
40   SOT_DROP_TABLE = 0,
41   SOT_CREATE_TABLE = 1,
42   SOT_RENAME_TABLE_NEW = 2,  // Unused, but still reserved
43   SOT_ALTER_TABLE_COMMIT = 3,
44   SOT_DROP_DB = 4,
45   SOT_CREATE_DB = 5,
46   SOT_ALTER_DB = 6,
47   SOT_CLEAR_SLOCK = 7,
48   SOT_TABLESPACE = 8,
49   SOT_LOGFILE_GROUP = 9,
50   SOT_RENAME_TABLE = 10,
51   SOT_TRUNCATE_TABLE = 11,
52   SOT_RENAME_TABLE_PREPARE = 12,
53   SOT_ONLINE_ALTER_TABLE_PREPARE = 13,
54   SOT_ONLINE_ALTER_TABLE_COMMIT = 14,
55   SOT_CREATE_USER = 15,
56   SOT_DROP_USER = 16,
57   SOT_RENAME_USER = 17,
58   SOT_GRANT = 18,
59   SOT_REVOKE = 19,
60   SOT_CREATE_TABLESPACE = 20,
61   SOT_ALTER_TABLESPACE = 21,
62   SOT_DROP_TABLESPACE = 22,
63   SOT_CREATE_LOGFILE_GROUP = 23,
64   SOT_ALTER_LOGFILE_GROUP = 24,
65   SOT_DROP_LOGFILE_GROUP = 25,
66   SOT_ACL_SNAPSHOT = 26,
67   SOT_ACL_STATEMENT = 27,
68   SOT_ACL_STATEMENT_REFRESH = 28,
69 };
70 
71 namespace Ndb_schema_dist {
72 
73 // Schema operation result codes
74 enum Schema_op_result_code {
75   NODE_UNSUBSCRIBE = 9001,   // Node unsubscribe during
76   NODE_FAILURE = 9002,       // Node failed during
77   NODE_TIMEOUT = 9003,       // Node timeout during
78   COORD_ABORT = 9004,        // Coordinator aborted
79   CLIENT_ABORT = 9005,       // Client aborted
80   CLIENT_KILLED = 9007,      // Client killed
81   SCHEMA_OP_FAILURE = 9008,  // Failure not related to protocol but the actual
82                              // schema operation to be distributed
83   NDB_TRANS_FAILURE = 9009   // An NDB read/write transaction failed
84 };
85 
86 /**
87   Check if schema distribution has been initialized and is
88   ready to communicate with the other MySQL Server(s) in the cluster.
89 
90   @param requestor Pointer value identifying caller
91 
92   @return true schema distribution is ready
93 */
94 bool is_ready(void *requestor);
95 
96 }  // namespace Ndb_schema_dist
97 
98 class Ndb;
99 
100 /**
101   @brief Ndb_schema_dist_client, class represents a Client
102   in the schema distribution.
103 
104   Contains functionality for distributing a schema operation
105   to the other MySQL Server(s) which need to update their
106   data structures when a metadata change occurs.
107 
108   The Client primarily communicates with the Coordinator(which is
109   in the same MySQL Server) while the Coordinator handles communication
110   with the Participant nodes(in other MySQL Server). When Coordinator
111   have got replies from all Participants, by acknowledging the schema
112   operation, the Client will be woken up again.
113 
114   Should also have functionality for:
115    - checking that "schema dist is ready", i.e checking that
116      the mysql.ndb_schema table has been created and schema
117      distribution has been initialized properly(by the ndb
118      binlog thread)
119    - checking that schema distribution of the table and db name
120      is supported by the current mysql.ndb_schema, for example
121      that length of the table or db name fits in the columns of that
122      table
123    - checking which functionality the other MySQL Server(s) support,
124      for example if they are on an older version they would probably
125      still not support longer table names or new schema dist operation types.
126 */
127 class Ndb_schema_dist_client {
128   class THD *const m_thd;
129   class Thd_ndb *const m_thd_ndb;
130   struct NDB_SHARE *m_share{nullptr};
131   const std::string m_share_reference;
132   class Prepared_keys {
133     using Key = std::pair<std::string, std::string>;
134     std::vector<Key> m_keys;
135 
136    public:
keys()137     const std::vector<Key> &keys() { return m_keys; }
138     void add_key(const char *db, const char *tabname);
139     bool check_key(const char *db, const char *tabname) const;
140   } m_prepared_keys;
141   bool m_holding_acl_mutex;
142 
143   // List of schema operation results, populated when schema operation has
144   // completed sucessfully.
145   struct Schema_op_result {
146     uint32 nodeid;
147     uint32 result;
148     std::string message;
149   };
150   std::vector<Schema_op_result> m_schema_op_results;
151 
152   static bool m_ddl_blocked;
153 
154   void push_and_clear_schema_op_results();
155 
156   bool log_schema_op_impl(Ndb *ndb, const char *query, int query_length,
157                           const char *db, const char *table_name,
158                           uint32 ndb_table_id, uint32 ndb_table_version,
159                           SCHEMA_OP_TYPE type, uint32 anyvalue);
160 
161   /**
162      @brief Write row to ndb_schema to initiate the schema operation
163      @return true on sucess and false on failure
164    */
165   bool write_schema_op_to_NDB(Ndb *ndb, const char *query, int query_length,
166                               const char *db, const char *name, uint32 id,
167                               uint32 version, uint32 nodeid, uint32 type,
168                               uint32 schema_op_id, uint32 anyvalue);
169   /**
170     @brief Distribute the schema operation to the other MySQL Server(s)
171     @note For now, just call the old log_schema_op_impl(), over time
172           the functionality of that function will gradually be moved over
173           to this new Ndb_schema_dist_client class
174     @return false if schema distribution fails
175    */
176   bool log_schema_op(const char *query, size_t query_length, const char *db,
177                      const char *table_name, uint32 id, uint32 version,
178                      SCHEMA_OP_TYPE type, bool log_query_on_participant = true);
179 
180   /**
181      @brief Calculate the anyvalue to use for this schema change. The anyvalue
182      is used to transport additional settings from client to the participants.
183 
184      @param force_nologging Force setting anyvalue to not log schema change on
185      participant
186 
187      @return The anyvalue to use for schema change
188    */
189   uint32 calculate_anyvalue(bool force_nologging) const;
190 
191   /**
192      @brief Acquire the ACL change mutex
193    */
194   void acquire_acl_lock();
195 
196  public:
197   Ndb_schema_dist_client() = delete;
198   Ndb_schema_dist_client(const Ndb_schema_dist_client &) = delete;
199   Ndb_schema_dist_client(class THD *thd);
200 
201   ~Ndb_schema_dist_client();
202 
block_ddl(bool ddl_blocked)203   static void block_ddl(bool ddl_blocked) { m_ddl_blocked = ddl_blocked; }
is_ddl_blocked()204   static bool is_ddl_blocked() { return m_ddl_blocked; }
205 
206   /*
207     @brief Generate unique id for distribution of objects which doesn't have
208            global id in NDB.
209     @return unique id
210   */
211   uint32 unique_id() const;
212 
213   /*
214     @brief Generate unique version for distribution of objects which doesn't
215            have global id in NDB.
216     @return unique version
217   */
218   uint32 unique_version() const;
219 
220   /**
221     @brief Prepare client for schema operation, check that
222            schema distribution is ready and other conditions are fulfilled.
223     @param db database name
224     @param tabname table name
225     @note Always done early to avoid changing metadata which is
226           hard to rollback at a later stage.
227     @return true if prepare succeed
228   */
229   bool prepare(const char *db, const char *tabname);
230 
231   /**
232     @brief Prepare client for rename schema operation, check that
233            schema distribution is ready and other conditions are fulfilled.
234            The rename case is different as two different "keys" may be used
235            and need to be prepared.
236     @param db database name
237     @param tabname table name
238     @param new_db new database name
239     @param new_tabname new table name
240     @note Always done early to avoid changing metadata which is
241           hard to rollback at a later stage.
242     @return true if prepare succeed
243   */
244   bool prepare_rename(const char *db, const char *tabname, const char *new_db,
245                       const char *new_tabname);
246 
247   /**
248     @brief Prepare client for an ACL change notification
249            (e.g. CREATE USER, GRANT, REVOKE, etc.).
250     @param node_id Unique number identifying this mysql server
251     @return true if prepare succeed
252   */
253   bool prepare_acl_change(uint node_id);
254 
255   /**
256     @brief Check that the prepared identifiers is supported by the schema
257            distribution. For example long identifiers can't be communicated
258            between the MySQL Servers unless the table used for communication
259            have large enough columns.
260     @note This is done separately from @prepare since different error
261           code(or none at all) should be returned for this error.
262     @note Always done early to avoid changing metadata which is
263           hard to rollback at a later stage.
264     @param invalid_identifier The name of the identifier that failed the check
265     @return true if check succeed
266   */
267   bool check_identifier_limits(std::string &invalid_identifier);
268 
269   /**
270    * @brief Check if given name is the schema distribution table, special
271             handling for that table is required in a few places.
272      @param db database name
273      @param table_name table name
274      @return true if table is the schema distribution table
275    */
276   static bool is_schema_dist_table(const char *db, const char *table_name);
277 
278   /**
279    * @brief Check if given name is the schema distribution result table, special
280             handling for that table is required in a few places.
281      @param db database name
282      @param table_name table name
283      @return true if table is the schema distribution result table
284    */
285   static bool is_schema_dist_result_table(const char *db,
286                                           const char *table_name);
287 
288   /**
289    * @brief Convert SCHEMA_OP_TYPE to string
290    * @return string describing the type
291    */
292   static const char *type_name(SCHEMA_OP_TYPE type);
293 
294   bool create_table(const char *db, const char *table_name, int id,
295                     int version);
296   bool truncate_table(const char *db, const char *table_name, int id,
297                       int version);
298   bool alter_table(const char *db, const char *table_name, int id, int version,
299                    bool log_on_participant = true);
300   bool alter_table_inplace_prepare(const char *db, const char *table_name,
301                                    int id, int version);
302   bool alter_table_inplace_commit(const char *db, const char *table_name,
303                                   int id, int version);
304   bool rename_table_prepare(const char *db, const char *table_name, int id,
305                             int version, const char *new_key_for_table);
306   bool rename_table(const char *db, const char *table_name, int id, int version,
307                     const char *new_dbname, const char *new_tabname,
308                     bool log_on_participant);
309   bool drop_table(const char *db, const char *table_name, int id, int version,
310                   bool log_on_participant = true);
311 
312   bool create_db(const char *query, uint query_length, const char *db,
313                  unsigned int id, unsigned int version);
314   bool alter_db(const char *query, uint query_length, const char *db,
315                 unsigned int id, unsigned int version);
316   bool drop_db(const char *db);
317 
318   bool acl_notify(const char *db, const char *query, uint query_length,
319                   bool participants_must_refresh);
320   bool acl_notify(std::string user_list);
321 
322   bool tablespace_changed(const char *tablespace_name, int id, int version);
323   bool logfilegroup_changed(const char *logfilegroup_name, int id, int version);
324 
325   bool create_tablespace(const char *tablespace_name, int id, int version);
326   bool alter_tablespace(const char *tablespace_name, int id, int version);
327   bool drop_tablespace(const char *tablespace_name, int id, int version);
328 
329   bool create_logfile_group(const char *logfile_group_name, int id,
330                             int version);
331   bool alter_logfile_group(const char *logfile_group_name, int id, int version);
332   bool drop_logfile_group(const char *logfile_group_name, int id, int version);
333 };
334 
335 #endif
336