1.2.9
by Monty Taylor
Import upstream version 2011.03.11 |
1 |
/* Copyright (C) 2000-2003 MySQL AB
|
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 as published by
|
|
5 |
the Free Software Foundation; version 2 of the License.
|
|
6 |
||
7 |
This program is distributed in the hope that it will be useful,
|
|
8 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10 |
GNU General Public License for more details.
|
|
11 |
||
12 |
You should have received a copy of the GNU General Public License
|
|
13 |
along with this program; if not, write to the Free Software
|
|
14 |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
15 |
||
16 |
||
17 |
/* create and drop of databases */
|
|
18 |
#include <config.h> |
|
19 |
||
20 |
#include <fcntl.h> |
|
21 |
#include <sys/stat.h> |
|
22 |
#include <sys/types.h> |
|
23 |
||
24 |
#include <set> |
|
25 |
#include <string> |
|
26 |
#include <fstream> |
|
27 |
||
28 |
#include <drizzled/error.h> |
|
29 |
#include <drizzled/gettext.h> |
|
30 |
#include <drizzled/internal/m_string.h> |
|
31 |
#include <drizzled/session.h> |
|
32 |
#include <drizzled/schema.h> |
|
33 |
#include <drizzled/sql_base.h> |
|
34 |
#include <drizzled/lock.h> |
|
35 |
#include <drizzled/errmsg_print.h> |
|
36 |
#include <drizzled/transaction_services.h> |
|
37 |
#include <drizzled/message/schema.pb.h> |
|
38 |
#include <drizzled/sql_table.h> |
|
39 |
#include <drizzled/plugin/storage_engine.h> |
|
40 |
#include <drizzled/plugin/authorization.h> |
|
41 |
#include <drizzled/pthread_globals.h> |
|
42 |
#include <drizzled/charset.h> |
|
43 |
#include <drizzled/internal/my_sys.h> |
|
1.1.3
by Tobias Frost
Import upstream version 2012.01.30 |
44 |
#include <drizzled/catalog/instance.h> |
1.2.9
by Monty Taylor
Import upstream version 2011.03.11 |
45 |
#include <boost/thread/mutex.hpp> |
46 |
||
47 |
using namespace std; |
|
48 |
||
1.1.3
by Tobias Frost
Import upstream version 2012.01.30 |
49 |
namespace drizzled { |
50 |
namespace schema { |
|
1.2.9
by Monty Taylor
Import upstream version 2011.03.11 |
51 |
|
52 |
/*
|
|
53 |
Create a database
|
|
54 |
||
55 |
SYNOPSIS
|
|
56 |
create_db()
|
|
57 |
session Thread handler
|
|
58 |
db Name of database to create
|
|
59 |
Function assumes that this is already validated.
|
|
60 |
create_info Database create options (like character set)
|
|
61 |
||
62 |
SIDE-EFFECTS
|
|
63 |
1. Report back to client that command succeeded (my_ok)
|
|
64 |
2. Report errors to client
|
|
65 |
3. Log event to binary log
|
|
66 |
||
67 |
RETURN VALUES
|
|
68 |
false ok
|
|
69 |
true Error
|
|
70 |
||
71 |
*/
|
|
72 |
||
73 |
bool create(Session &session, const message::Schema &schema_message, const bool is_if_not_exists) |
|
74 |
{
|
|
75 |
bool error= false; |
|
76 |
||
77 |
/*
|
|
78 |
Do not create database if another thread is holding read lock.
|
|
79 |
Wait for global read lock before acquiring session->catalog()->schemaLock().
|
|
80 |
After wait_if_global_read_lock() we have protection against another
|
|
81 |
global read lock. If we would acquire session->catalog()->schemaLock() first,
|
|
82 |
another thread could step in and get the global read lock before we
|
|
83 |
reach wait_if_global_read_lock(). If this thread tries the same as we
|
|
84 |
(admin a db), it would then go and wait on session->catalog()->schemaLock()...
|
|
85 |
Furthermore wait_if_global_read_lock() checks if the current thread
|
|
86 |
has the global read lock and refuses the operation with
|
|
87 |
ER_CANT_UPDATE_WITH_READLOCK if applicable.
|
|
88 |
*/
|
|
89 |
if (session.wait_if_global_read_lock(false, true)) |
|
90 |
{
|
|
91 |
return false; |
|
92 |
}
|
|
93 |
||
94 |
assert(schema_message.has_name()); |
|
95 |
assert(schema_message.has_collation()); |
|
96 |
||
97 |
// @todo push this lock down into the engine
|
|
98 |
{
|
|
99 |
boost::mutex::scoped_lock scopedLock(session.catalog().schemaLock()); |
|
100 |
||
1.1.7
by Tobias Frost
Import upstream version 7.2.3 |
101 |
// Check to see if it exists already.
|
102 |
identifier::Schema schema_identifier(session.catalog().identifier(), |
|
103 |
schema_message.name()); |
|
1.2.9
by Monty Taylor
Import upstream version 2011.03.11 |
104 |
if (plugin::StorageEngine::doesSchemaExist(schema_identifier)) |
105 |
{
|
|
106 |
if (not is_if_not_exists) |
|
107 |
{
|
|
108 |
my_error(ER_DB_CREATE_EXISTS, schema_identifier); |
|
109 |
error= true; |
|
110 |
}
|
|
111 |
else
|
|
112 |
{
|
|
113 |
push_warning_printf(&session, DRIZZLE_ERROR::WARN_LEVEL_NOTE, |
|
114 |
ER_DB_CREATE_EXISTS, ER(ER_DB_CREATE_EXISTS), |
|
115 |
schema_message.name().c_str()); |
|
116 |
session.my_ok(); |
|
117 |
}
|
|
118 |
}
|
|
119 |
else if (not plugin::StorageEngine::createSchema(schema_message)) // Try to create it |
|
120 |
{
|
|
121 |
my_error(ER_CANT_CREATE_DB, MYF(0), schema_message.name().c_str(), errno); |
|
122 |
error= true; |
|
123 |
}
|
|
124 |
else // Created ! |
|
125 |
{
|
|
1.1.3
by Tobias Frost
Import upstream version 2012.01.30 |
126 |
TransactionServices::createSchema(session, schema_message); |
1.2.9
by Monty Taylor
Import upstream version 2011.03.11 |
127 |
session.my_ok(1); |
128 |
}
|
|
129 |
}
|
|
130 |
session.startWaitingGlobalReadLock(); |
|
131 |
||
132 |
return error; |
|
133 |
}
|
|
134 |
||
135 |
||
136 |
/* db-name is already validated when we come here */
|
|
137 |
||
138 |
bool alter(Session &session, |
|
139 |
const message::Schema &schema_message, |
|
140 |
const message::Schema &original_schema) |
|
141 |
{
|
|
142 |
/*
|
|
143 |
Do not alter database if another thread is holding read lock.
|
|
144 |
Wait for global read lock before acquiring session->catalog()->schemaLock().
|
|
145 |
After wait_if_global_read_lock() we have protection against another
|
|
146 |
global read lock. If we would acquire session->catalog()->schemaLock() first,
|
|
147 |
another thread could step in and get the global read lock before we
|
|
148 |
reach wait_if_global_read_lock(). If this thread tries the same as we
|
|
149 |
(admin a db), it would then go and wait on session->catalog()->schemaLock()...
|
|
150 |
Furthermore wait_if_global_read_lock() checks if the current thread
|
|
151 |
has the global read lock and refuses the operation with
|
|
152 |
ER_CANT_UPDATE_WITH_READLOCK if applicable.
|
|
153 |
*/
|
|
154 |
if ((session.wait_if_global_read_lock(false, true))) |
|
155 |
return false; |
|
156 |
||
157 |
bool success; |
|
158 |
{
|
|
159 |
boost::mutex::scoped_lock scopedLock(session.catalog().schemaLock()); |
|
160 |
||
1.1.7
by Tobias Frost
Import upstream version 7.2.3 |
161 |
identifier::Schema schema_idenifier(session.catalog().identifier(), |
162 |
schema_message.name()); |
|
1.2.9
by Monty Taylor
Import upstream version 2011.03.11 |
163 |
if (not plugin::StorageEngine::doesSchemaExist(schema_idenifier)) |
164 |
{
|
|
165 |
my_error(ER_SCHEMA_DOES_NOT_EXIST, schema_idenifier); |
|
166 |
return false; |
|
167 |
}
|
|
168 |
||
169 |
/* Change options if current database is being altered. */
|
|
170 |
success= plugin::StorageEngine::alterSchema(schema_message); |
|
171 |
||
172 |
if (success) |
|
173 |
{
|
|
1.1.3
by Tobias Frost
Import upstream version 2012.01.30 |
174 |
TransactionServices::alterSchema(session, original_schema, schema_message); |
1.2.9
by Monty Taylor
Import upstream version 2011.03.11 |
175 |
session.my_ok(1); |
176 |
}
|
|
177 |
else
|
|
178 |
{
|
|
179 |
my_error(ER_ALTER_SCHEMA, schema_idenifier); |
|
180 |
}
|
|
181 |
}
|
|
182 |
session.startWaitingGlobalReadLock(); |
|
183 |
||
184 |
return success; |
|
185 |
}
|
|
186 |
||
187 |
||
188 |
/*
|
|
189 |
Drop all tables in a database and the database itself
|
|
190 |
||
191 |
SYNOPSIS
|
|
192 |
rm_db()
|
|
193 |
session Thread handle
|
|
194 |
db Database name in the case given by user
|
|
195 |
It's already validated and set to lower case
|
|
196 |
(if needed) when we come here
|
|
197 |
if_exists Don't give error if database doesn't exists
|
|
198 |
silent Don't generate errors
|
|
199 |
||
200 |
RETURN
|
|
201 |
false ok (Database dropped)
|
|
202 |
ERROR Error
|
|
203 |
*/
|
|
204 |
||
1.1.3
by Tobias Frost
Import upstream version 2012.01.30 |
205 |
bool drop(Session &session, const identifier::Schema &schema_identifier, bool if_exists) |
1.2.9
by Monty Taylor
Import upstream version 2011.03.11 |
206 |
{
|
207 |
/*
|
|
208 |
Do not drop database if another thread is holding read lock.
|
|
209 |
Wait for global read lock before acquiring session->catalog()->schemaLock().
|
|
210 |
After wait_if_global_read_lock() we have protection against another
|
|
211 |
global read lock. If we would acquire session->catalog()->schemaLock() first,
|
|
212 |
another thread could step in and get the global read lock before we
|
|
213 |
reach wait_if_global_read_lock(). If this thread tries the same as we
|
|
214 |
(admin a db), it would then go and wait on session->catalog()->schemaLock()...
|
|
215 |
Furthermore wait_if_global_read_lock() checks if the current thread
|
|
216 |
has the global read lock and refuses the operation with
|
|
217 |
ER_CANT_UPDATE_WITH_READLOCK if applicable.
|
|
218 |
*/
|
|
219 |
if (session.wait_if_global_read_lock(false, true)) |
|
220 |
{
|
|
221 |
return true; |
|
222 |
}
|
|
223 |
||
1.1.3
by Tobias Frost
Import upstream version 2012.01.30 |
224 |
bool error= false; |
1.2.9
by Monty Taylor
Import upstream version 2011.03.11 |
225 |
{
|
226 |
boost::mutex::scoped_lock scopedLock(session.catalog().schemaLock()); |
|
1.1.3
by Tobias Frost
Import upstream version 2012.01.30 |
227 |
if (message::schema::shared_ptr message= plugin::StorageEngine::getSchemaDefinition(schema_identifier)) |
1.2.9
by Monty Taylor
Import upstream version 2011.03.11 |
228 |
{
|
229 |
error= plugin::StorageEngine::dropSchema(session, schema_identifier, *message); |
|
230 |
}
|
|
1.1.3
by Tobias Frost
Import upstream version 2012.01.30 |
231 |
else if (if_exists) |
232 |
{
|
|
233 |
push_warning_printf(&session, DRIZZLE_ERROR::WARN_LEVEL_NOTE, ER_DB_DROP_EXISTS, ER(ER_DB_DROP_EXISTS), |
|
234 |
schema_identifier.getSQLPath().c_str()); |
|
235 |
}
|
|
236 |
else
|
|
237 |
{
|
|
238 |
error= true; |
|
239 |
my_error(ER_DB_DROP_EXISTS, schema_identifier); |
|
240 |
}
|
|
241 |
};
|
|
1.2.9
by Monty Taylor
Import upstream version 2011.03.11 |
242 |
|
243 |
/*
|
|
244 |
If this database was the client's selected database, we silently
|
|
245 |
change the client's selected database to nothing (to have an empty
|
|
246 |
SELECT DATABASE() in the future). For this we free() session->db and set
|
|
247 |
it to 0.
|
|
248 |
*/
|
|
249 |
if (not error and schema_identifier.compare(*session.schema())) |
|
1.1.3
by Tobias Frost
Import upstream version 2012.01.30 |
250 |
session.set_schema(""); |
1.2.9
by Monty Taylor
Import upstream version 2011.03.11 |
251 |
|
252 |
session.startWaitingGlobalReadLock(); |
|
253 |
||
254 |
return error; |
|
255 |
}
|
|
256 |
||
257 |
/**
|
|
258 |
@brief Change the current database and its attributes unconditionally.
|
|
259 |
||
260 |
@param session thread handle
|
|
261 |
@param new_db_name database name
|
|
262 |
@param force_switch if force_switch is false, then the operation will fail if
|
|
263 |
||
264 |
- new_db_name is NULL or empty;
|
|
265 |
||
266 |
- OR new database name is invalid
|
|
267 |
(check_db_name() failed);
|
|
268 |
||
269 |
- OR user has no privilege on the new database;
|
|
270 |
||
271 |
- OR new database does not exist;
|
|
272 |
||
273 |
if force_switch is true, then
|
|
274 |
||
275 |
- if new_db_name is NULL or empty, the current
|
|
276 |
database will be NULL, @@collation_database will
|
|
277 |
be set to @@collation_server, the operation will
|
|
278 |
succeed.
|
|
279 |
||
280 |
- if new database name is invalid
|
|
281 |
(check_db_name() failed), the current database
|
|
282 |
will be NULL, @@collation_database will be set to
|
|
283 |
@@collation_server, but the operation will fail;
|
|
284 |
||
285 |
- user privileges will not be checked
|
|
286 |
(Session::db_access however is updated);
|
|
287 |
||
288 |
TODO: is this really the intention?
|
|
289 |
(see sp-security.test).
|
|
290 |
||
291 |
- if new database does not exist,the current database
|
|
292 |
will be NULL, @@collation_database will be set to
|
|
293 |
@@collation_server, a warning will be thrown, the
|
|
294 |
operation will succeed.
|
|
295 |
||
296 |
@details The function checks that the database name corresponds to a
|
|
297 |
valid and existent database, checks access rights and changes the current
|
|
298 |
database with database attributes (@@collation_database session variable,
|
|
299 |
Session::db_access).
|
|
300 |
||
301 |
This function is not the only way to switch the database that is
|
|
302 |
currently employed. When the replication slave thread switches the
|
|
303 |
database before executing a query, it calls session->set_db directly.
|
|
304 |
However, if the query, in turn, uses a stored routine, the stored routine
|
|
305 |
will use this function, even if it's run on the slave.
|
|
306 |
||
307 |
This function allocates the name of the database on the system heap: this
|
|
308 |
is necessary to be able to uniformly change the database from any module
|
|
309 |
of the server. Up to 5.0 different modules were using different memory to
|
|
310 |
store the name of the database, and this led to memory corruption:
|
|
311 |
a stack pointer set by Stored Procedures was used by replication after
|
|
312 |
the stack address was long gone.
|
|
313 |
||
314 |
@return Operation status
|
|
315 |
@retval false Success
|
|
316 |
@retval true Error
|
|
317 |
*/
|
|
318 |
||
1.1.3
by Tobias Frost
Import upstream version 2012.01.30 |
319 |
bool change(Session &session, const identifier::Schema &schema_identifier) |
1.2.9
by Monty Taylor
Import upstream version 2011.03.11 |
320 |
{
|
321 |
||
322 |
if (not plugin::Authorization::isAuthorized(*session.user(), schema_identifier)) |
|
323 |
{
|
|
324 |
/* Error message is set in isAuthorized */
|
|
325 |
return true; |
|
326 |
}
|
|
327 |
||
328 |
if (not check(session, schema_identifier)) |
|
329 |
{
|
|
330 |
my_error(ER_WRONG_DB_NAME, schema_identifier); |
|
331 |
||
332 |
return true; |
|
333 |
}
|
|
334 |
||
335 |
if (not plugin::StorageEngine::doesSchemaExist(schema_identifier)) |
|
336 |
{
|
|
337 |
my_error(ER_BAD_DB_ERROR, schema_identifier); |
|
338 |
||
339 |
/* The operation failed. */
|
|
340 |
||
341 |
return true; |
|
342 |
}
|
|
343 |
||
1.1.3
by Tobias Frost
Import upstream version 2012.01.30 |
344 |
session.set_schema(schema_identifier.getSchemaName()); |
1.2.9
by Monty Taylor
Import upstream version 2011.03.11 |
345 |
|
346 |
return false; |
|
347 |
}
|
|
348 |
||
349 |
/**
|
|
350 |
@brief Internal implementation: switch current database to a valid one.
|
|
351 |
||
352 |
@param session Thread context.
|
|
353 |
@param new_db_name Name of the database to switch to. The function will
|
|
354 |
take ownership of the name (the caller must not free
|
|
355 |
the allocated memory). If the name is NULL, we're
|
|
356 |
going to switch to NULL db.
|
|
357 |
@param new_db_charset Character set of the new database.
|
|
358 |
*/
|
|
359 |
||
360 |
||
361 |
/*
|
|
362 |
Check if database name is valid
|
|
363 |
||
364 |
SYNPOSIS
|
|
365 |
check()
|
|
366 |
org_name Name of database and length
|
|
367 |
||
368 |
RETURN
|
|
369 |
false error
|
|
370 |
true ok
|
|
371 |
*/
|
|
372 |
||
1.1.3
by Tobias Frost
Import upstream version 2012.01.30 |
373 |
bool check(Session &session, const identifier::Schema &schema_identifier) |
1.2.9
by Monty Taylor
Import upstream version 2011.03.11 |
374 |
{
|
375 |
if (not plugin::Authorization::isAuthorized(*session.user(), schema_identifier)) |
|
376 |
return false; |
|
377 |
return schema_identifier.isValid(); |
|
378 |
}
|
|
379 |
||
380 |
} /* namespace schema */ |
|
381 |
||
382 |
} /* namespace drizzled */ |