1
/* Copyright (C) 2000-2003 MySQL AB
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17
/* create and drop of databases */
22
#include <sys/types.h>
28
#include <drizzled/message/schema.pb.h>
29
#include "drizzled/error.h"
30
#include <drizzled/gettext.h>
31
#include <drizzled/my_hash.h>
32
#include "drizzled/internal/m_string.h"
33
#include <drizzled/session.h>
34
#include <drizzled/db.h>
35
#include <drizzled/sql_base.h>
36
#include <drizzled/lock.h>
37
#include <drizzled/errmsg_print.h>
38
#include <drizzled/replication_services.h>
39
#include <drizzled/message/schema.pb.h>
40
#include "drizzled/sql_table.h"
41
#include "drizzled/plugin/storage_engine.h"
42
#include "drizzled/plugin/authorization.h"
43
#include "drizzled/global_charset_info.h"
44
#include "drizzled/pthread_globals.h"
45
#include "drizzled/charset.h"
47
#include "drizzled/internal/my_sys.h"
49
#define MAX_DROP_TABLE_Q_LEN 1024
56
static long mysql_rm_known_files(Session *session,
57
const string &db, const char *path,
58
plugin::TableNameList &dropped_tables);
59
static void mysql_change_db_impl(Session *session, LEX_STRING *new_db_name);
66
session Thread handler
67
db Name of database to create
68
Function assumes that this is already validated.
69
create_info Database create options (like character set)
72
1. Report back to client that command succeeded (my_ok)
73
2. Report errors to client
74
3. Log event to binary log
82
bool mysql_create_db(Session *session, const message::Schema &schema_message, const bool is_if_not_exists)
84
ReplicationServices &replication_services= ReplicationServices::singleton();
88
Do not create database if another thread is holding read lock.
89
Wait for global read lock before acquiring LOCK_create_db.
90
After wait_if_global_read_lock() we have protection against another
91
global read lock. If we would acquire LOCK_create_db first,
92
another thread could step in and get the global read lock before we
93
reach wait_if_global_read_lock(). If this thread tries the same as we
94
(admin a db), it would then go and wait on LOCK_create_db...
95
Furthermore wait_if_global_read_lock() checks if the current thread
96
has the global read lock and refuses the operation with
97
ER_CANT_UPDATE_WITH_READLOCK if applicable.
99
if (wait_if_global_read_lock(session, 0, 1))
104
assert(schema_message.has_name());
105
assert(schema_message.has_collation());
107
// @todo push this lock down into the engine
108
pthread_mutex_lock(&LOCK_create_db);
110
// Check to see if it exists already.
111
if (plugin::StorageEngine::doesSchemaExist(schema_message.name()))
113
if (not is_if_not_exists)
115
my_error(ER_DB_CREATE_EXISTS, MYF(0), schema_message.name().c_str());
120
push_warning_printf(session, DRIZZLE_ERROR::WARN_LEVEL_NOTE,
121
ER_DB_CREATE_EXISTS, ER(ER_DB_CREATE_EXISTS),
122
schema_message.name().c_str());
126
else if (not plugin::StorageEngine::createSchema(schema_message)) // Try to create it
128
my_error(ER_CANT_CREATE_DB, MYF(0), schema_message.name().c_str(), errno);
133
replication_services.createSchema(session, schema_message);
137
pthread_mutex_unlock(&LOCK_create_db);
138
start_waiting_global_read_lock(session);
144
/* db-name is already validated when we come here */
146
bool mysql_alter_db(Session *session, const message::Schema &schema_message)
148
ReplicationServices &replication_services= ReplicationServices::singleton();
151
Do not alter database if another thread is holding read lock.
152
Wait for global read lock before acquiring LOCK_create_db.
153
After wait_if_global_read_lock() we have protection against another
154
global read lock. If we would acquire LOCK_create_db first,
155
another thread could step in and get the global read lock before we
156
reach wait_if_global_read_lock(). If this thread tries the same as we
157
(admin a db), it would then go and wait on LOCK_create_db...
158
Furthermore wait_if_global_read_lock() checks if the current thread
159
has the global read lock and refuses the operation with
160
ER_CANT_UPDATE_WITH_READLOCK if applicable.
162
if ((wait_if_global_read_lock(session, 0, 1)))
165
pthread_mutex_lock(&LOCK_create_db);
167
if (not plugin::StorageEngine::doesSchemaExist(schema_message.name()))
169
my_error(ER_SCHEMA_DOES_NOT_EXIST, MYF(0), schema_message.name().c_str());
173
/* Change options if current database is being altered. */
174
bool success= plugin::StorageEngine::alterSchema(schema_message);
178
replication_services.rawStatement(session, session->getQueryString());
183
my_error(ER_ALTER_SCHEMA, MYF(0), schema_message.name().c_str());
186
pthread_mutex_unlock(&LOCK_create_db);
187
start_waiting_global_read_lock(session);
194
Drop all tables in a database and the database itself
198
session Thread handle
199
db Database name in the case given by user
200
It's already validated and set to lower case
201
(if needed) when we come here
202
if_exists Don't give error if database doesn't exists
203
silent Don't generate errors
206
false ok (Database dropped)
210
bool mysql_rm_db(Session *session, const std::string &schema_name, const bool if_exists)
214
char path[FN_REFLEN+16];
216
plugin::TableNameList dropped_tables;
217
message::Schema schema_proto;
220
Do not drop database if another thread is holding read lock.
221
Wait for global read lock before acquiring LOCK_create_db.
222
After wait_if_global_read_lock() we have protection against another
223
global read lock. If we would acquire LOCK_create_db first,
224
another thread could step in and get the global read lock before we
225
reach wait_if_global_read_lock(). If this thread tries the same as we
226
(admin a db), it would then go and wait on LOCK_create_db...
227
Furthermore wait_if_global_read_lock() checks if the current thread
228
has the global read lock and refuses the operation with
229
ER_CANT_UPDATE_WITH_READLOCK if applicable.
231
if (wait_if_global_read_lock(session, 0, 1))
236
pthread_mutex_lock(&LOCK_create_db);
239
length= build_table_filename(path, sizeof(path),
240
schema_name.c_str(), "", false);
241
path[length]= '\0'; // Remove file name
243
/* See if the schema exists */
244
if (not plugin::StorageEngine::doesSchemaExist(schema_name))
248
push_warning_printf(session, DRIZZLE_ERROR::WARN_LEVEL_NOTE,
249
ER_DB_DROP_EXISTS, ER(ER_DB_DROP_EXISTS),
255
my_error(ER_DB_DROP_EXISTS, MYF(0), path);
261
pthread_mutex_lock(&LOCK_open); /* After deleting database, remove all cache entries related to schema */
262
remove_db_from_cache(schema_name);
263
pthread_mutex_unlock(&LOCK_open);
267
deleted= mysql_rm_known_files(session, schema_name,
268
path, dropped_tables);
276
assert(! session->query.empty());
278
ReplicationServices &replication_services= ReplicationServices::singleton();
279
replication_services.dropSchema(session, schema_name);
280
session->clear_error();
281
session->server_status|= SERVER_STATUS_DB_DROPPED;
282
session->my_ok((uint32_t) deleted);
283
session->server_status&= ~SERVER_STATUS_DB_DROPPED;
287
char *query, *query_pos, *query_end, *query_data_start;
290
if (!(query= (char*) session->alloc(MAX_DROP_TABLE_Q_LEN)))
291
goto exit; /* not much else we can do */
292
query_pos= query_data_start= strcpy(query,"drop table ")+11;
293
query_end= query + MAX_DROP_TABLE_Q_LEN;
294
db_len= schema_name.length();
296
ReplicationServices &replication_services= ReplicationServices::singleton();
297
for (plugin::TableNameList::iterator it= dropped_tables.begin();
298
it != dropped_tables.end();
301
uint32_t tbl_name_len;
303
/* 3 for the quotes and the comma*/
304
tbl_name_len= (*it).length() + 3;
305
if (query_pos + tbl_name_len + 1 >= query_end)
307
/* These DDL methods and logging protected with LOCK_create_db */
308
replication_services.rawStatement(session, query);
309
query_pos= query_data_start;
313
query_pos= strcpy(query_pos, (*it).c_str()) + (tbl_name_len-3);
318
if (query_pos != query_data_start)
320
/* These DDL methods and logging protected with LOCK_create_db */
321
replication_services.rawStatement(session, query);
327
If this database was the client's selected database, we silently
328
change the client's selected database to nothing (to have an empty
329
SELECT DATABASE() in the future). For this we free() session->db and set
332
if (not session->db.empty() && session->db.compare(schema_name) == 0)
333
mysql_change_db_impl(session, NULL);
334
pthread_mutex_unlock(&LOCK_create_db);
335
start_waiting_global_read_lock(session);
341
static int rm_table_part2(Session *session, TableList *tables)
346
bool foreign_key_error= false;
348
pthread_mutex_lock(&LOCK_open); /* Part 2 of rm a table */
351
If we have the table in the definition cache, we don't have to check the
352
.frm cursor to find if the table is a normal table (not view) and what
356
for (table= tables; table; table= table->next_local)
359
table->db_type= NULL;
360
if ((share= TableShare::getShare(table->db, table->table_name)))
361
table->db_type= share->db_type();
364
if (lock_table_names_exclusively(session, tables))
366
pthread_mutex_unlock(&LOCK_open);
370
/* Don't give warnings for not found errors, as we already generate notes */
371
session->no_warnings_for_error= 1;
373
for (table= tables; table; table= table->next_local)
376
plugin::StorageEngine *table_type;
378
error= session->drop_temporary_table(table);
382
// removed temporary table
386
goto err_with_placeholders;
388
// temporary table not found
392
table_type= table->db_type;
396
abort_locked_tables(session, db, table->table_name);
397
remove_table_from_cache(session, db, table->table_name,
398
RTFC_WAIT_OTHER_THREAD_FLAG |
399
RTFC_CHECK_KILLED_FLAG);
401
If the table was used in lock tables, remember it so that
402
unlock_table_names can free it
404
if ((locked_table= drop_locked_tables(session, db, table->table_name)))
405
table->table= locked_table;
410
goto err_with_placeholders;
414
TableIdentifier identifier(db, table->table_name);
416
if (table_type == NULL && not plugin::StorageEngine::doesTableExist(*session, identifier))
418
// Table was not found on disk and table can't be created from engine
419
push_warning_printf(session, DRIZZLE_ERROR::WARN_LEVEL_NOTE,
420
ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
425
error= plugin::StorageEngine::dropTable(*session, identifier);
427
if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE))
430
session->clear_error();
433
if (error == HA_ERR_ROW_IS_REFERENCED)
435
/* the table is referenced by a foreign key constraint */
436
foreign_key_error= true;
440
if (error == 0 || (foreign_key_error == false))
441
write_bin_log_drop_table(session, true, db, table->table_name);
445
if (wrong_tables.length())
446
wrong_tables.append(',');
447
wrong_tables.append(String(table->table_name,system_charset_info));
451
It's safe to unlock LOCK_open: we have an exclusive lock
454
pthread_mutex_unlock(&LOCK_open);
456
if (wrong_tables.length())
458
if (not foreign_key_error)
459
my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
460
wrong_tables.c_ptr());
463
my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
468
pthread_mutex_lock(&LOCK_open); /* final bit in rm table lock */
469
err_with_placeholders:
470
unlock_table_names(tables, NULL);
471
pthread_mutex_unlock(&LOCK_open);
472
session->no_warnings_for_error= 0;
478
Removes files with known extensions plus.
479
session MUST be set when calling this function!
482
static long mysql_rm_known_files(Session *session,
484
const char *org_path,
485
plugin::TableNameList &dropped_tables)
487
CachedDirectory dirp(org_path);
492
TableList *tot_list= NULL, **tot_list_next;
494
tot_list_next= &tot_list;
496
plugin::StorageEngine::getTableNames(db, dropped_tables);
498
for (plugin::TableNameList::iterator it= dropped_tables.begin();
499
it != dropped_tables.end();
502
size_t db_len= db.size();
504
/* Drop the table nicely */
505
TableList *table_list=(TableList*)
506
session->calloc(sizeof(*table_list) +
513
table_list->db= (char*) (table_list+1);
514
table_list->table_name= strcpy(table_list->db, db.c_str()) + db_len + 1;
515
filename_to_tablename((*it).c_str(), table_list->table_name,
517
table_list->alias= table_list->table_name; // If lower_case_table_names=2
518
table_list->internal_tmp_table= (strncmp((*it).c_str(),
520
strlen(TMP_FILE_PREFIX)) == 0);
522
(*tot_list_next)= table_list;
523
tot_list_next= &table_list->next_local;
531
if (rm_table_part2(session, tot_list))
535
if (not plugin::StorageEngine::dropSchema(db))
537
my_error(ER_DROP_SCHEMA, MYF(0), db.c_str());
545
@brief Change the current database and its attributes unconditionally.
547
@param session thread handle
548
@param new_db_name database name
549
@param force_switch if force_switch is false, then the operation will fail if
551
- new_db_name is NULL or empty;
553
- OR new database name is invalid
554
(check_db_name() failed);
556
- OR user has no privilege on the new database;
558
- OR new database does not exist;
560
if force_switch is true, then
562
- if new_db_name is NULL or empty, the current
563
database will be NULL, @@collation_database will
564
be set to @@collation_server, the operation will
567
- if new database name is invalid
568
(check_db_name() failed), the current database
569
will be NULL, @@collation_database will be set to
570
@@collation_server, but the operation will fail;
572
- user privileges will not be checked
573
(Session::db_access however is updated);
575
TODO: is this really the intention?
576
(see sp-security.test).
578
- if new database does not exist,the current database
579
will be NULL, @@collation_database will be set to
580
@@collation_server, a warning will be thrown, the
581
operation will succeed.
583
@details The function checks that the database name corresponds to a
584
valid and existent database, checks access rights and changes the current
585
database with database attributes (@@collation_database session variable,
588
This function is not the only way to switch the database that is
589
currently employed. When the replication slave thread switches the
590
database before executing a query, it calls session->set_db directly.
591
However, if the query, in turn, uses a stored routine, the stored routine
592
will use this function, even if it's run on the slave.
594
This function allocates the name of the database on the system heap: this
595
is necessary to be able to uniformly change the database from any module
596
of the server. Up to 5.0 different modules were using different memory to
597
store the name of the database, and this led to memory corruption:
598
a stack pointer set by Stored Procedures was used by replication after
599
the stack address was long gone.
601
@return Operation status
602
@retval false Success
606
bool mysql_change_db(Session *session, const std::string &new_db_name)
609
assert(not new_db_name.empty());
611
if (not plugin::Authorization::isAuthorized(session->getSecurityContext(),
614
/* Error message is set in isAuthorized */
620
Now we need to make a copy because check_db_name requires a
621
non-constant argument. Actually, it takes database file name.
623
TODO: fix check_db_name().
626
LEX_STRING new_db_file_name;
627
new_db_file_name.length= new_db_name.length();
628
new_db_file_name.str= (char *)malloc(new_db_name.length() + 1);
629
if (new_db_file_name.str == NULL)
630
return true; /* the error is set */
631
memcpy(new_db_file_name.str, new_db_name.c_str(), new_db_name.length());
632
new_db_file_name.str[new_db_name.length()]= 0;
636
NOTE: if check_db_name() fails, we should throw an error in any case,
637
even if we are called from sp_head::execute().
639
It's next to impossible however to get this error when we are called
640
from sp_head::execute(). But let's switch the current database to NULL
641
in this case to be sure.
644
if (check_db_name(&new_db_file_name))
646
my_error(ER_WRONG_DB_NAME, MYF(0), new_db_file_name.str);
647
free(new_db_file_name.str);
652
if (not plugin::StorageEngine::doesSchemaExist(new_db_file_name.str))
654
/* Report an error and free new_db_file_name. */
656
my_error(ER_BAD_DB_ERROR, MYF(0), new_db_file_name.str);
657
free(new_db_file_name.str);
659
/* The operation failed. */
664
mysql_change_db_impl(session, &new_db_file_name);
665
free(new_db_file_name.str);
671
@brief Internal implementation: switch current database to a valid one.
673
@param session Thread context.
674
@param new_db_name Name of the database to switch to. The function will
675
take ownership of the name (the caller must not free
676
the allocated memory). If the name is NULL, we're
677
going to switch to NULL db.
678
@param new_db_charset Character set of the new database.
681
static void mysql_change_db_impl(Session *session, LEX_STRING *new_db_name)
683
/* 1. Change current database in Session. */
685
if (new_db_name == NULL)
688
Session::set_db() does all the job -- it frees previous database name and
692
session->set_db(NULL, 0);
697
Here we already have a copy of database name to be used in Session. So,
698
we just call Session::reset_db(). Since Session::reset_db() does not releases
699
the previous database name, we should do it explicitly.
702
session->set_db(new_db_name->str, new_db_name->length);
706
} /* namespace drizzled */