1 /* Copyright (c) 2016, 2019, 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/sql_import.h"
24 
25 #include <sys/types.h>
26 #include <algorithm>
27 #include <memory>
28 #include <string>
29 #include <utility>
30 #include <vector>
31 
32 #include "my_dbug.h"
33 #include "my_inttypes.h"
34 #include "mysql/mysql_lex_string.h"
35 #include "prealloced_array.h"  // Prealloced_array
36 #include "sql/auth/auth_acls.h"
37 #include "sql/auth/auth_common.h"
38 #include "sql/dd/cache/dictionary_client.h"  // dd::cache::Dictionary_client::Auto_releaser
39 #include "sql/dd/impl/sdi_utils.h"  // dd::sdi_utils::handle_errors
40 #include "sql/dd/sdi_api.h"         // dd::sdi::Import_target
41 #include "sql/dd/sdi_file.h"        // dd::sdi_file::expand_pattern
42 #include "sql/dd/string_type.h"     // dd::String_type
43 #include "sql/mdl.h"                // MDL_request
44 #include "sql/mysqld.h"             // is_secure_file_path
45 #include "sql/psi_memory_key.h"     // key_memory_DD_import
46 #include "sql/sql_backup_lock.h"    // acquire_shared_backup_lock
47 #include "sql/sql_class.h"          // THD
48 #include "sql/sql_error.h"
49 #include "sql/stateless_allocator.h"
50 #include "sql/system_variables.h"
51 #include "sql/transaction.h"  // trans_rollback_stmt
52 
53 namespace {
54 
55 typedef Prealloced_array<dd::sdi::Import_target, 5> Targets_type;
56 
57 }  // namespace
58 
Sql_cmd_import_table(const Sdi_patterns_type & sdi_patterns)59 Sql_cmd_import_table::Sql_cmd_import_table(
60     const Sdi_patterns_type &sdi_patterns)
61     : m_sdi_patterns(sdi_patterns) {}
62 
execute(THD * thd)63 bool Sql_cmd_import_table::execute(THD *thd) {
64   DBUG_ASSERT(!m_sdi_patterns.empty());
65 
66   auto rbgrd = dd::sdi_utils::make_guard(thd, [&](THD *) {
67     trans_rollback_stmt(thd);
68     trans_rollback(thd);
69   });
70 
71   // Need to keep this alive until after commit/rollback has been done
72   dd::cache::Dictionary_client::Auto_releaser ar{thd->dd_client()};
73 
74   if (check_access(thd, FILE_ACL, nullptr, nullptr, nullptr, false, false)) {
75     return true;
76   }
77 
78   // Convert supplied sdi patterns into path,in_datadir pairs
79   dd::sdi_file::Paths_type paths{key_memory_DD_import};
80   paths.reserve(m_sdi_patterns.size());
81   for (auto &pattern : m_sdi_patterns) {
82     if (thd->charset() == files_charset_info) {
83       if (dd::sdi_file::expand_pattern(thd, pattern, &paths)) {
84         return true;
85       }
86       continue;
87     }
88 
89     LEX_STRING converted;
90     if (thd->convert_string(&converted, files_charset_info, pattern.str,
91                             pattern.length, thd->charset())) {
92       return true;
93     }
94 
95     if (dd::sdi_file::expand_pattern(thd, converted, &paths)) {
96       return true;
97     }
98   }
99 
100   Targets_type targets{key_memory_DD_import};
101 
102   auto tgtgrd = dd::sdi_utils::make_guard(thd, [&](THD *) {
103     for (auto &tgt : targets) {
104       tgt.rollback();
105     }
106   });
107 
108   for (auto &p : paths) {
109     // Move the path string from paths to avoid copy - paths is now
110     // empty shell
111     targets.emplace_back(std::move(p.first), p.second);
112   }
113   // Have a valid list of sdi files to import
114 
115   dd::String_type shared_buffer;
116   MDL_request_list mdl_requests;
117   for (auto &t : targets) {
118     if (t.load(thd, &shared_buffer)) {
119       return true;
120     }
121 
122     if (check_privileges(thd, t)) {
123       return true;
124     }
125 
126     mdl_requests.push_front(mdl_request(t, thd->mem_root));
127   }
128   // Table objects and their schema names have been loaded, privileges
129   // checked and EXCLUSIVE MDL requests for the tables been added to
130   // mdl_requests.
131 
132   std::vector<dd::String_type> schema_names;
133   schema_names.reserve(targets.size());
134   for (auto &t : targets) {
135     schema_names.push_back(*t.can_schema_name());
136   }
137   std::sort(schema_names.begin(), schema_names.end());
138   auto uniq_end = std::unique(schema_names.begin(), schema_names.end());
139   schema_names.erase(uniq_end, schema_names.end());
140 
141   for (auto &sn : schema_names) {
142     MDL_request *r = new (thd->mem_root) MDL_request;
143     MDL_REQUEST_INIT(r, MDL_key::SCHEMA, sn.c_str(), "",
144                      MDL_INTENTION_EXCLUSIVE, MDL_TRANSACTION);
145     mdl_requests.push_front(r);
146   }
147 
148   MDL_request *mdl_request_for_backup_lock = new (thd->mem_root) MDL_request;
149   MDL_REQUEST_INIT(mdl_request_for_backup_lock, MDL_key::BACKUP_LOCK, "", "",
150                    MDL_INTENTION_EXCLUSIVE, MDL_TRANSACTION);
151   mdl_requests.push_front(mdl_request_for_backup_lock);
152 
153   // If we cannot acquire protection against GRL, err out.
154   if (thd->global_read_lock.can_acquire_protection()) return true;
155 
156   MDL_request *mdl_request_for_grl = new (thd->mem_root) MDL_request;
157   MDL_REQUEST_INIT(mdl_request_for_grl, MDL_key::GLOBAL, "", "",
158                    MDL_INTENTION_EXCLUSIVE, MDL_TRANSACTION);
159   mdl_requests.push_front(mdl_request_for_grl);
160 
161   if (thd->mdl_context.acquire_locks(&mdl_requests,
162                                      thd->variables.lock_wait_timeout)) {
163     return true;
164   }
165 
166   // Now when we have protection against concurrent change of read_only
167   // option we can safely re-check its value.
168   if (check_readonly(thd, true)) return true;
169 
170   // Now we have MDL on all schemas and tables involved
171 
172   for (auto &t : targets) {
173     if (t.store_in_dd(thd)) {
174       return true;
175     }
176   }
177 
178   rbgrd.release();
179   tgtgrd.release();
180 
181   // Downgrade failing delete_file errors to warning, and
182   // allow the transaction to commit.
183   dd::sdi_utils::handle_errors(
184       thd,
185       [](uint, const char *, Sql_condition::enum_severity_level *level,
186          const char *) {
187         (*level) = Sql_condition::SL_WARNING;
188         return false;
189       },
190       [&]() {
191         for (auto &tgt : targets) {
192           (void)tgt.commit();
193         }
194         return false;
195       });
196 
197   my_ok(thd);
198   return trans_commit_stmt(thd) || trans_commit(thd);
199 }
200 
sql_command_code() const201 enum_sql_command Sql_cmd_import_table::sql_command_code() const {
202   return SQLCOM_IMPORT;
203 }
204