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