~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/db.cc

  • Committer: Brian Aker
  • Date: 2009-11-24 02:06:37 UTC
  • mfrom: (1223.1.7 push)
  • Revision ID: brian@gaz-20091124020637-9gb65vj98x1arydm
Merge for staging.

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
 
12
12
   You should have received a copy of the GNU General Public License
13
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 */
 
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
15
 
16
16
 
17
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>
 
18
#include <drizzled/global.h>
 
19
#include CSTDINT_H
 
20
#include CINTTYPES_H
25
21
#include <string>
26
22
#include <fstream>
27
 
 
28
 
#include "drizzled/error.h"
 
23
#include <drizzled/message/schema.pb.h>
 
24
using namespace std;
 
25
#include <drizzled/server_includes.h>
 
26
#include <mysys/mysys_err.h>
 
27
#include <mysys/my_dir.h>
 
28
#include <drizzled/error.h>
29
29
#include <drizzled/gettext.h>
30
 
#include <drizzled/my_hash.h>
31
 
#include "drizzled/internal/m_string.h"
 
30
#include <mysys/hash.h>
32
31
#include <drizzled/session.h>
33
32
#include <drizzled/db.h>
34
33
#include <drizzled/sql_base.h>
35
34
#include <drizzled/lock.h>
36
35
#include <drizzled/errmsg_print.h>
37
 
#include <drizzled/transaction_services.h>
 
36
#include <drizzled/replication_services.h>
38
37
#include <drizzled/message/schema.pb.h>
39
 
#include "drizzled/sql_table.h"
40
 
#include "drizzled/plugin/storage_engine.h"
41
 
#include "drizzled/plugin/authorization.h"
42
 
#include "drizzled/global_charset_info.h"
43
 
#include "drizzled/pthread_globals.h"
44
 
#include "drizzled/charset.h"
45
 
 
46
 
#include <boost/thread/mutex.hpp>
47
 
 
48
 
#include "drizzled/internal/my_sys.h"
49
 
 
 
38
 
 
39
using namespace drizzled;
 
40
 
 
41
#define MY_DB_OPT_FILE "db.opt"
50
42
#define MAX_DROP_TABLE_Q_LEN      1024
51
43
 
52
 
using namespace std;
53
 
 
54
 
namespace drizzled
55
 
{
56
 
 
57
 
static void change_db_impl(Session *session);
58
 
static void change_db_impl(Session *session, identifier::Schema &schema_identifier);
 
44
const char *del_exts[]= {".dfe", ".BAK", ".TMD",".opt", NULL};
 
45
static TYPELIB deletable_extentions=
 
46
{array_elements(del_exts)-1,"del_exts", del_exts, NULL};
 
47
 
 
48
static long mysql_rm_known_files(Session *session, MY_DIR *dirp,
 
49
                                 const char *db, const char *path,
 
50
                                 TableList **dropped_tables);
 
51
 
 
52
static void mysql_change_db_impl(Session *session, LEX_STRING *new_db_name);
 
53
            
 
54
 
 
55
/**
 
56
  Return default database collation.
 
57
 
 
58
  @param session     Thread context.
 
59
  @param db_name Database name.
 
60
 
 
61
  @return CHARSET_INFO object. The operation always return valid character
 
62
    set, even if the database does not exist.
 
63
*/
 
64
 
 
65
const CHARSET_INFO *get_default_db_collation(const char *db_name)
 
66
{
 
67
  message::Schema db;
 
68
 
 
69
  get_database_metadata(db_name, &db);
 
70
 
 
71
  /* If for some reason the db.opt file lacks a collation,
 
72
     we just return the default */
 
73
 
 
74
  if (db.has_collation())
 
75
  {
 
76
    const string buffer= db.collation();
 
77
    const CHARSET_INFO* cs= get_charset_by_name(buffer.c_str());
 
78
 
 
79
    if (!cs)
 
80
    {
 
81
      errmsg_printf(ERRMSG_LVL_ERROR,
 
82
                    _("Error while loading database options: '%s':"),db_name);
 
83
      errmsg_printf(ERRMSG_LVL_ERROR, ER(ER_UNKNOWN_COLLATION), buffer.c_str());
 
84
 
 
85
      return default_charset_info;
 
86
    }
 
87
 
 
88
    return cs;
 
89
  }
 
90
 
 
91
  return default_charset_info;
 
92
}
 
93
 
 
94
/* path is path to database, not schema file */
 
95
static int write_schema_file(Session *session,
 
96
                             const char *path, const char *name,
 
97
                             HA_CREATE_INFO *create)
 
98
{
 
99
  message::Schema db;
 
100
  char schema_file_tmp[FN_REFLEN];
 
101
  string schema_file(path);
 
102
 
 
103
  assert(path);
 
104
  assert(name);
 
105
  assert(create);
 
106
 
 
107
  snprintf(schema_file_tmp, FN_REFLEN, "%s%c%s.tmpXXXXXX", path, FN_LIBCHAR, MY_DB_OPT_FILE);
 
108
 
 
109
  schema_file.append(1, FN_LIBCHAR);
 
110
  schema_file.append(MY_DB_OPT_FILE);
 
111
 
 
112
  int fd= mkstemp(schema_file_tmp);
 
113
 
 
114
  if (fd==-1)
 
115
    return errno;
 
116
 
 
117
  if (!create->default_table_charset)
 
118
    create->default_table_charset= session->variables.collation_server;
 
119
 
 
120
  db.set_name(name);
 
121
  db.set_collation(create->default_table_charset->name);
 
122
 
 
123
  if (!db.SerializeToFileDescriptor(fd))
 
124
  {
 
125
    close(fd);
 
126
    unlink(schema_file_tmp);
 
127
    return -1;
 
128
  }
 
129
 
 
130
  if (rename(schema_file_tmp, schema_file.c_str()) == -1)
 
131
  {
 
132
    close(fd);
 
133
    return errno;
 
134
  }
 
135
 
 
136
  close(fd);
 
137
  return 0;
 
138
}
 
139
 
 
140
int get_database_metadata(const char *dbname, message::Schema *db)
 
141
{
 
142
  char db_opt_path[FN_REFLEN];
 
143
  size_t length;
 
144
 
 
145
  /*
 
146
    Pass an empty file name, and the database options file name as extension
 
147
    to avoid table name to file name encoding.
 
148
  */
 
149
  length= build_table_filename(db_opt_path, sizeof(db_opt_path),
 
150
                              dbname, "", false);
 
151
  strcpy(db_opt_path + length, MY_DB_OPT_FILE);
 
152
 
 
153
  int fd= open(db_opt_path, O_RDONLY);
 
154
 
 
155
  if (fd == -1)
 
156
    return errno;
 
157
 
 
158
  if (!db->ParseFromFileDescriptor(fd))
 
159
  {
 
160
    close(fd);
 
161
    return -1;
 
162
  }
 
163
  close(fd);
 
164
 
 
165
  return 0;
 
166
}
59
167
 
60
168
/*
61
169
  Create a database
62
170
 
63
171
  SYNOPSIS
64
 
  create_db()
 
172
  mysql_create_db()
65
173
  session               Thread handler
66
174
  db            Name of database to create
67
175
                Function assumes that this is already validated.
78
186
 
79
187
*/
80
188
 
81
 
bool create_db(Session *session, const message::Schema &schema_message, const bool is_if_not_exists)
 
189
bool mysql_create_db(Session *session, const char *db, HA_CREATE_INFO *create_info, bool is_if_not_exists)
82
190
{
83
 
  TransactionServices &transaction_services= TransactionServices::singleton();
 
191
  ReplicationServices &replication_services= ReplicationServices::singleton();
 
192
  char   path[FN_REFLEN+16];
 
193
  long result= 1;
 
194
  int error_erno;
84
195
  bool error= false;
 
196
  uint32_t path_len;
 
197
 
 
198
  /* do not create 'information_schema' db */
 
199
  if (!my_strcasecmp(system_charset_info, db, INFORMATION_SCHEMA_NAME.c_str()))
 
200
  {
 
201
    my_error(ER_DB_CREATE_EXISTS, MYF(0), db);
 
202
    return(-1);
 
203
  }
85
204
 
86
205
  /*
87
206
    Do not create database if another thread is holding read lock.
88
 
    Wait for global read lock before acquiring session->catalog()->schemaLock().
 
207
    Wait for global read lock before acquiring LOCK_create_db.
89
208
    After wait_if_global_read_lock() we have protection against another
90
 
    global read lock. If we would acquire session->catalog()->schemaLock() first,
 
209
    global read lock. If we would acquire LOCK_create_db first,
91
210
    another thread could step in and get the global read lock before we
92
211
    reach wait_if_global_read_lock(). If this thread tries the same as we
93
 
    (admin a db), it would then go and wait on session->catalog()->schemaLock()...
 
212
    (admin a db), it would then go and wait on LOCK_create_db...
94
213
    Furthermore wait_if_global_read_lock() checks if the current thread
95
214
    has the global read lock and refuses the operation with
96
215
    ER_CANT_UPDATE_WITH_READLOCK if applicable.
97
216
  */
98
 
  if (session->wait_if_global_read_lock(false, true))
 
217
  if (wait_if_global_read_lock(session, 0, 1))
99
218
  {
100
 
    return false;
 
219
    error= true;
 
220
    goto exit2;
101
221
  }
102
222
 
103
 
  assert(schema_message.has_name());
104
 
  assert(schema_message.has_collation());
105
 
 
106
 
  // @todo push this lock down into the engine
 
223
  pthread_mutex_lock(&LOCK_create_db);
 
224
 
 
225
  /* Check directory */
 
226
  path_len= build_table_filename(path, sizeof(path), db, "", false);
 
227
  path[path_len-1]= 0;                    // Remove last '/' from path
 
228
 
 
229
  if (mkdir(path,0777) == -1)
107
230
  {
108
 
    boost::mutex::scoped_lock scopedLock(session->catalog().schemaLock());
109
 
 
110
 
    // Check to see if it exists already.  
111
 
    identifier::Schema schema_identifier(schema_message.name());
112
 
    if (plugin::StorageEngine::doesSchemaExist(schema_identifier))
 
231
    if (errno == EEXIST)
113
232
    {
114
 
      if (not is_if_not_exists)
115
 
      {
116
 
        my_error(ER_DB_CREATE_EXISTS, schema_identifier);
117
 
        error= true;
118
 
      }
119
 
      else
120
 
      {
121
 
        push_warning_printf(session, DRIZZLE_ERROR::WARN_LEVEL_NOTE,
122
 
                            ER_DB_CREATE_EXISTS, ER(ER_DB_CREATE_EXISTS),
123
 
                            schema_message.name().c_str());
124
 
        session->my_ok();
125
 
      }
 
233
      if (! is_if_not_exists)
 
234
      {
 
235
        my_error(ER_DB_CREATE_EXISTS, MYF(0), db);
 
236
        error= true;
 
237
        goto exit;
 
238
      }
 
239
      push_warning_printf(session, DRIZZLE_ERROR::WARN_LEVEL_NOTE,
 
240
                          ER_DB_CREATE_EXISTS, ER(ER_DB_CREATE_EXISTS), db);
 
241
      session->my_ok();
 
242
      error= false;
 
243
      goto exit;
126
244
    }
127
 
    else if (not plugin::StorageEngine::createSchema(schema_message)) // Try to create it 
 
245
 
 
246
    my_error(ER_CANT_CREATE_DB, MYF(0), db, my_errno);
 
247
    error= true;
 
248
    goto exit;
 
249
  }
 
250
 
 
251
  error_erno= write_schema_file(session, path, db, create_info);
 
252
  if (error_erno && error_erno != EEXIST)
 
253
  {
 
254
    if (rmdir(path) >= 0)
128
255
    {
129
 
      my_error(ER_CANT_CREATE_DB, MYF(0), schema_message.name().c_str(), errno);
130
256
      error= true;
131
 
    }
132
 
    else // Created !
133
 
    {
134
 
      transaction_services.createSchema(*session, schema_message);
135
 
      session->my_ok(1);
 
257
      goto exit;
136
258
    }
137
259
  }
138
 
  session->startWaitingGlobalReadLock();
139
 
 
 
260
  else if (error_erno)
 
261
    error= true;
 
262
 
 
263
  replication_services.rawStatement(session, session->query, session->query_length);
 
264
  session->my_ok(result);
 
265
 
 
266
exit:
 
267
  pthread_mutex_unlock(&LOCK_create_db);
 
268
  start_waiting_global_read_lock(session);
 
269
exit2:
140
270
  return error;
141
271
}
142
272
 
143
273
 
144
274
/* db-name is already validated when we come here */
145
275
 
146
 
bool alter_db(Session *session,
147
 
              const message::Schema &schema_message,
148
 
              const message::schema::shared_ptr &original_schema)
 
276
bool mysql_alter_db(Session *session, const char *db, HA_CREATE_INFO *create_info)
149
277
{
150
 
  TransactionServices &transaction_services= TransactionServices::singleton();
 
278
  ReplicationServices &replication_services= ReplicationServices::singleton();
 
279
  long result=1;
 
280
  int error= 0;
 
281
  char   path[FN_REFLEN+16];
 
282
  uint32_t path_len;
151
283
 
152
284
  /*
153
285
    Do not alter database if another thread is holding read lock.
154
 
    Wait for global read lock before acquiring session->catalog()->schemaLock().
 
286
    Wait for global read lock before acquiring LOCK_create_db.
155
287
    After wait_if_global_read_lock() we have protection against another
156
 
    global read lock. If we would acquire session->catalog()->schemaLock() first,
 
288
    global read lock. If we would acquire LOCK_create_db first,
157
289
    another thread could step in and get the global read lock before we
158
290
    reach wait_if_global_read_lock(). If this thread tries the same as we
159
 
    (admin a db), it would then go and wait on session->catalog()->schemaLock()...
 
291
    (admin a db), it would then go and wait on LOCK_create_db...
160
292
    Furthermore wait_if_global_read_lock() checks if the current thread
161
293
    has the global read lock and refuses the operation with
162
294
    ER_CANT_UPDATE_WITH_READLOCK if applicable.
163
295
  */
164
 
  if ((session->wait_if_global_read_lock(false, true)))
165
 
    return false;
166
 
 
167
 
  bool success;
 
296
  if ((error=wait_if_global_read_lock(session,0,1)))
 
297
    goto exit;
 
298
 
 
299
  pthread_mutex_lock(&LOCK_create_db);
 
300
 
 
301
  /* Change options if current database is being altered. */
 
302
  path_len= build_table_filename(path, sizeof(path), db, "", false);
 
303
  path[path_len-1]= 0;                    // Remove last '/' from path
 
304
 
 
305
  error= write_schema_file(session, path, db, create_info);
 
306
  if (error && error != EEXIST)
168
307
  {
169
 
    boost::mutex::scoped_lock scopedLock(session->catalog().schemaLock());
170
 
 
171
 
    identifier::Schema schema_idenifier(schema_message.name());
172
 
    if (not plugin::StorageEngine::doesSchemaExist(schema_idenifier))
173
 
    {
174
 
      my_error(ER_SCHEMA_DOES_NOT_EXIST, schema_idenifier);
175
 
      return false;
176
 
    }
177
 
 
178
 
    /* Change options if current database is being altered. */
179
 
    success= plugin::StorageEngine::alterSchema(schema_message);
180
 
 
181
 
    if (success)
182
 
    {
183
 
      transaction_services.alterSchema(*session, original_schema, schema_message);
184
 
      session->my_ok(1);
185
 
    }
186
 
    else
187
 
    {
188
 
      my_error(ER_ALTER_SCHEMA, schema_idenifier);
189
 
    }
 
308
    /* TODO: find some witty way of getting back an error message */
 
309
    pthread_mutex_unlock(&LOCK_create_db);
 
310
    goto exit;
190
311
  }
191
 
  session->startWaitingGlobalReadLock();
192
 
 
193
 
  return success;
 
312
 
 
313
  replication_services.rawStatement(session, session->getQueryString(), session->getQueryLength());
 
314
  session->my_ok(result);
 
315
 
 
316
  pthread_mutex_unlock(&LOCK_create_db);
 
317
  start_waiting_global_read_lock(session);
 
318
exit:
 
319
  return error ? true : false;
194
320
}
195
321
 
196
322
 
198
324
  Drop all tables in a database and the database itself
199
325
 
200
326
  SYNOPSIS
201
 
    rm_db()
 
327
    mysql_rm_db()
202
328
    session                     Thread handle
203
329
    db                  Database name in the case given by user
204
330
                        It's already validated and set to lower case
211
337
    ERROR Error
212
338
*/
213
339
 
214
 
bool rm_db(Session *session, identifier::Schema &schema_identifier, const bool if_exists)
 
340
bool mysql_rm_db(Session *session,char *db,bool if_exists)
215
341
{
216
 
  bool error= false;
 
342
  long deleted=0;
 
343
  int error= false;
 
344
  char  path[FN_REFLEN+16];
 
345
  MY_DIR *dirp;
 
346
  uint32_t length;
 
347
  TableList *dropped_tables= NULL;
 
348
 
 
349
  if (db && (strcmp(db, "information_schema") == 0))
 
350
  {
 
351
    my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), "", "", INFORMATION_SCHEMA_NAME.c_str());
 
352
    return true;
 
353
  }
217
354
 
218
355
  /*
219
356
    Do not drop database if another thread is holding read lock.
220
 
    Wait for global read lock before acquiring session->catalog()->schemaLock().
 
357
    Wait for global read lock before acquiring LOCK_create_db.
221
358
    After wait_if_global_read_lock() we have protection against another
222
 
    global read lock. If we would acquire session->catalog()->schemaLock() first,
 
359
    global read lock. If we would acquire LOCK_create_db first,
223
360
    another thread could step in and get the global read lock before we
224
361
    reach wait_if_global_read_lock(). If this thread tries the same as we
225
 
    (admin a db), it would then go and wait on session->catalog()->schemaLock()...
 
362
    (admin a db), it would then go and wait on LOCK_create_db...
226
363
    Furthermore wait_if_global_read_lock() checks if the current thread
227
364
    has the global read lock and refuses the operation with
228
365
    ER_CANT_UPDATE_WITH_READLOCK if applicable.
229
366
  */
230
 
  if (session->wait_if_global_read_lock(false, true))
231
 
  {
232
 
    return true;
233
 
  }
234
 
 
235
 
  do
236
 
  {
237
 
    boost::mutex::scoped_lock scopedLock(session->catalog().schemaLock());
238
 
 
239
 
    /* See if the schema exists */
240
 
    if (not plugin::StorageEngine::doesSchemaExist(schema_identifier))
241
 
    {
242
 
      if (if_exists)
243
 
      {
244
 
        std::string path;
245
 
        schema_identifier.getSQLPath(path);
246
 
 
247
 
        push_warning_printf(session, DRIZZLE_ERROR::WARN_LEVEL_NOTE,
248
 
                            ER_DB_DROP_EXISTS, ER(ER_DB_DROP_EXISTS),
249
 
                            path.c_str());
250
 
      }
251
 
      else
252
 
      {
253
 
        error= true;
254
 
        my_error(ER_DB_DROP_EXISTS, schema_identifier);
255
 
        break;
256
 
      }
257
 
    }
258
 
    else
259
 
    {
260
 
      error= plugin::StorageEngine::dropSchema(*session, schema_identifier);
261
 
    }
262
 
 
263
 
  } while (0);
264
 
 
 
367
  if (wait_if_global_read_lock(session, 0, 1))
 
368
  {
 
369
    error= -1;
 
370
    goto exit2;
 
371
  }
 
372
 
 
373
  pthread_mutex_lock(&LOCK_create_db);
 
374
 
 
375
  length= build_table_filename(path, sizeof(path), db, "", false);
 
376
  strcpy(path+length, MY_DB_OPT_FILE);         // Append db option file name
 
377
  unlink(path);
 
378
  path[length]= '\0';                           // Remove file name
 
379
 
 
380
  /* See if the directory exists */
 
381
  if (!(dirp= my_dir(path,MYF(MY_DONT_SORT))))
 
382
  {
 
383
    if (!if_exists)
 
384
    {
 
385
      error= -1;
 
386
      my_error(ER_DB_DROP_EXISTS, MYF(0), db);
 
387
      goto exit;
 
388
    }
 
389
    else
 
390
      push_warning_printf(session, DRIZZLE_ERROR::WARN_LEVEL_NOTE,
 
391
                          ER_DB_DROP_EXISTS, ER(ER_DB_DROP_EXISTS), db);
 
392
  }
 
393
  else
 
394
  {
 
395
    pthread_mutex_lock(&LOCK_open); /* After deleting database, remove all cache entries related to schema */
 
396
    remove_db_from_cache(db);
 
397
    pthread_mutex_unlock(&LOCK_open);
 
398
 
 
399
 
 
400
    error= -1;
 
401
    if ((deleted= mysql_rm_known_files(session, dirp, db, path, &dropped_tables)) >= 0)
 
402
    {
 
403
      plugin::StorageEngine::dropDatabase(path);
 
404
      error = 0;
 
405
    }
 
406
  }
 
407
  if (deleted >= 0)
 
408
  {
 
409
    const char *query;
 
410
    uint32_t query_length;
 
411
    if (!session->query)
 
412
    {
 
413
      /* The client used the old obsolete mysql_drop_db() call */
 
414
      query= path;
 
415
      query_length= sprintf(path, "drop database `%s`", db);
 
416
    }
 
417
    else
 
418
    {
 
419
      query= session->query;
 
420
      query_length= session->query_length;
 
421
    }
 
422
    ReplicationServices &replication_services= ReplicationServices::singleton();
 
423
    replication_services.rawStatement(session, session->getQueryString(), session->getQueryLength());
 
424
    session->clear_error();
 
425
    session->server_status|= SERVER_STATUS_DB_DROPPED;
 
426
    session->my_ok((uint32_t) deleted);
 
427
    session->server_status&= ~SERVER_STATUS_DB_DROPPED;
 
428
  }
 
429
  else
 
430
  {
 
431
    char *query, *query_pos, *query_end, *query_data_start;
 
432
    TableList *tbl;
 
433
    uint32_t db_len;
 
434
 
 
435
    if (!(query= (char*) session->alloc(MAX_DROP_TABLE_Q_LEN)))
 
436
      goto exit; /* not much else we can do */
 
437
    query_pos= query_data_start= strcpy(query,"drop table ")+11;
 
438
    query_end= query + MAX_DROP_TABLE_Q_LEN;
 
439
    db_len= strlen(db);
 
440
 
 
441
    ReplicationServices &replication_services= ReplicationServices::singleton();
 
442
    for (tbl= dropped_tables; tbl; tbl= tbl->next_local)
 
443
    {
 
444
      uint32_t tbl_name_len;
 
445
 
 
446
      /* 3 for the quotes and the comma*/
 
447
      tbl_name_len= strlen(tbl->table_name) + 3;
 
448
      if (query_pos + tbl_name_len + 1 >= query_end)
 
449
      {
 
450
        /* These DDL methods and logging protected with LOCK_create_db */
 
451
        replication_services.rawStatement(session, query, (size_t) (query_pos -1 - query));
 
452
        query_pos= query_data_start;
 
453
      }
 
454
 
 
455
      *query_pos++ = '`';
 
456
      query_pos= strcpy(query_pos,tbl->table_name) + (tbl_name_len-3);
 
457
      *query_pos++ = '`';
 
458
      *query_pos++ = ',';
 
459
    }
 
460
 
 
461
    if (query_pos != query_data_start)
 
462
    {
 
463
      /* These DDL methods and logging protected with LOCK_create_db */
 
464
      replication_services.rawStatement(session, query, (size_t) (query_pos -1 - query));
 
465
    }
 
466
  }
 
467
 
 
468
exit:
265
469
  /*
266
470
    If this database was the client's selected database, we silently
267
471
    change the client's selected database to nothing (to have an empty
268
472
    SELECT DATABASE() in the future). For this we free() session->db and set
269
473
    it to 0.
270
474
  */
271
 
  if (not error and schema_identifier.compare(*session->schema()))
272
 
    change_db_impl(session);
273
 
 
274
 
  session->startWaitingGlobalReadLock();
275
 
 
276
 
  return error;
277
 
}
 
475
  if (! session->db.empty() && ! strcmp(session->db.c_str(), db))
 
476
    mysql_change_db_impl(session, NULL);
 
477
  pthread_mutex_unlock(&LOCK_create_db);
 
478
  start_waiting_global_read_lock(session);
 
479
exit2:
 
480
  return(error);
 
481
}
 
482
 
 
483
/*
 
484
  Removes files with known extensions plus.
 
485
  session MUST be set when calling this function!
 
486
*/
 
487
 
 
488
static long mysql_rm_known_files(Session *session, MY_DIR *dirp, const char *db,
 
489
                                 const char *org_path,
 
490
                                 TableList **dropped_tables)
 
491
{
 
492
  long deleted= 0;
 
493
  char filePath[FN_REFLEN];
 
494
  TableList *tot_list= NULL, **tot_list_next;
 
495
 
 
496
  tot_list_next= &tot_list;
 
497
 
 
498
  for (uint32_t idx= 0;
 
499
       idx < (uint32_t) dirp->number_off_files && !session->killed ;
 
500
       idx++)
 
501
  {
 
502
    FILEINFO *file=dirp->dir_entry+idx;
 
503
    char *extension;
 
504
 
 
505
    /* skiping . and .. */
 
506
    if (file->name[0] == '.' && (!file->name[1] ||
 
507
       (file->name[1] == '.' &&  !file->name[2])))
 
508
      continue;
 
509
 
 
510
    if (!(extension= strrchr(file->name, '.')))
 
511
      extension= strchr(file->name, '\0');
 
512
    if (find_type(extension, &deletable_extentions,1+2) <= 0)
 
513
    {
 
514
      /*
 
515
        ass ass ass.
 
516
 
 
517
        strange checking for magic extensions that are then deleted if
 
518
        not reg_ext (i.e. .frm).
 
519
 
 
520
        and (previously) we'd err out on drop database if files not matching
 
521
        engine ha_known_exts() or deletable_extensions were present.
 
522
 
 
523
        presumably this was to avoid deleting other user data... except if that
 
524
        data happened to be in files ending in .BAK, .opt or .TMD. *fun*
 
525
       */
 
526
      continue;
 
527
    }
 
528
    /* just for safety we use files_charset_info */
 
529
    if (db && !my_strcasecmp(files_charset_info,
 
530
                             extension, ".dfe"))
 
531
    {
 
532
      uint32_t db_len= strlen(db);
 
533
 
 
534
      /* Drop the table nicely */
 
535
      *extension= 0;                    // Remove extension
 
536
      TableList *table_list=(TableList*)
 
537
                              session->calloc(sizeof(*table_list) +
 
538
                                          db_len + 1 +
 
539
                                          strlen(file->name) + 1);
 
540
 
 
541
      if (!table_list)
 
542
        goto err;
 
543
      table_list->db= (char*) (table_list+1);
 
544
      table_list->table_name= strcpy(table_list->db, db) + db_len + 1;
 
545
      filename_to_tablename(file->name, table_list->table_name,
 
546
                            strlen(file->name) + 1);
 
547
      table_list->alias= table_list->table_name;        // If lower_case_table_names=2
 
548
      table_list->internal_tmp_table= is_prefix(file->name, TMP_FILE_PREFIX);
 
549
      /* Link into list */
 
550
      (*tot_list_next)= table_list;
 
551
      tot_list_next= &table_list->next_local;
 
552
      deleted++;
 
553
    }
 
554
    else
 
555
    {
 
556
      sprintf(filePath, "%s/%s", org_path, file->name);
 
557
      if (my_delete_with_symlink(filePath,MYF(MY_WME)))
 
558
      {
 
559
        goto err;
 
560
      }
 
561
    }
 
562
  }
 
563
  if (session->killed ||
 
564
      (tot_list && mysql_rm_table_part2(session, tot_list, 1, 0, 1)))
 
565
    goto err;
 
566
 
 
567
  my_dirend(dirp);
 
568
 
 
569
  if (dropped_tables)
 
570
    *dropped_tables= tot_list;
 
571
 
 
572
  if (rmdir(org_path))
 
573
  {
 
574
    my_error(ER_DB_DROP_RMDIR, MYF(0), org_path, errno);
 
575
    return -1;
 
576
  }
 
577
 
 
578
  return deleted;
 
579
 
 
580
err:
 
581
  my_dirend(dirp);
 
582
  return -1;
 
583
}
 
584
 
 
585
/**
 
586
  @brief Internal implementation: switch current database to a valid one.
 
587
 
 
588
  @param session            Thread context.
 
589
  @param new_db_name    Name of the database to switch to. The function will
 
590
                        take ownership of the name (the caller must not free
 
591
                        the allocated memory). If the name is NULL, we're
 
592
                        going to switch to NULL db.
 
593
  @param new_db_charset Character set of the new database.
 
594
*/
 
595
 
 
596
static void mysql_change_db_impl(Session *session, LEX_STRING *new_db_name)
 
597
{
 
598
  /* 1. Change current database in Session. */
 
599
 
 
600
  if (new_db_name == NULL)
 
601
  {
 
602
    /*
 
603
      Session::set_db() does all the job -- it frees previous database name and
 
604
      sets the new one.
 
605
    */
 
606
 
 
607
    session->set_db(NULL, 0);
 
608
  }
 
609
  else
 
610
  {
 
611
    /*
 
612
      Here we already have a copy of database name to be used in Session. So,
 
613
      we just call Session::reset_db(). Since Session::reset_db() does not releases
 
614
      the previous database name, we should do it explicitly.
 
615
    */
 
616
 
 
617
    session->set_db(new_db_name->str, new_db_name->length);
 
618
  }
 
619
}
 
620
 
 
621
/**
 
622
  Return true if db1_name is equal to db2_name, false otherwise.
 
623
 
 
624
  The function allows to compare database names according to the MySQL
 
625
  rules. The database names db1 and db2 are equal if:
 
626
     - db1 is NULL and db2 is NULL;
 
627
     or
 
628
     - db1 is not-NULL, db2 is not-NULL, db1 is equal (ignoring case) to
 
629
       db2 in system character set (UTF8).
 
630
*/
 
631
 
 
632
static inline bool
 
633
cmp_db_names(const char *db1_name,
 
634
             const char *db2_name)
 
635
{
 
636
  return
 
637
         /* db1 is NULL and db2 is NULL */
 
638
         (!db1_name && !db2_name) ||
 
639
 
 
640
         /* db1 is not-NULL, db2 is not-NULL, db1 == db2. */
 
641
         (db1_name && db2_name && my_strcasecmp(system_charset_info, db1_name, db2_name) == 0);
 
642
}
 
643
 
278
644
 
279
645
/**
280
646
  @brief Change the current database and its attributes unconditionally.
338
704
    @retval true  Error
339
705
*/
340
706
 
341
 
bool change_db(Session *session, identifier::Schema &schema_identifier)
 
707
bool mysql_change_db(Session *session, const LEX_STRING *new_db_name, bool force_switch)
342
708
{
343
 
 
344
 
  if (not plugin::Authorization::isAuthorized(session->user(), schema_identifier))
345
 
  {
346
 
    /* Error message is set in isAuthorized */
347
 
    return true;
348
 
  }
349
 
 
350
 
  if (not check_db_name(session, schema_identifier))
351
 
  {
352
 
    my_error(ER_WRONG_DB_NAME, schema_identifier);
353
 
 
354
 
    return true;
355
 
  }
356
 
 
357
 
  if (not plugin::StorageEngine::doesSchemaExist(schema_identifier))
358
 
  {
359
 
    my_error(ER_BAD_DB_ERROR, schema_identifier);
360
 
 
361
 
    /* The operation failed. */
362
 
 
363
 
    return true;
364
 
  }
365
 
 
366
 
  change_db_impl(session, schema_identifier);
 
709
  LEX_STRING new_db_file_name;
 
710
  const CHARSET_INFO *db_default_cl;
 
711
 
 
712
  assert(new_db_name);
 
713
  assert(new_db_name->length);
 
714
 
 
715
  if (my_strcasecmp(system_charset_info, new_db_name->str,
 
716
                    INFORMATION_SCHEMA_NAME.c_str()) == 0)
 
717
  {
 
718
    /* Switch the current database to INFORMATION_SCHEMA. */
 
719
    /* const_cast<> is safe here: mysql_change_db_impl does a copy */
 
720
    LEX_STRING is_name= { const_cast<char *>(INFORMATION_SCHEMA_NAME.c_str()),
 
721
                          INFORMATION_SCHEMA_NAME.length() };
 
722
    mysql_change_db_impl(session, &is_name);
 
723
 
 
724
    return false;
 
725
  }
 
726
 
 
727
  /*
 
728
    Now we need to make a copy because check_db_name requires a
 
729
    non-constant argument. Actually, it takes database file name.
 
730
 
 
731
    TODO: fix check_db_name().
 
732
  */
 
733
 
 
734
  new_db_file_name.length= new_db_name->length;
 
735
  new_db_file_name.str= (char *)malloc(new_db_name->length + 1);
 
736
  if (new_db_file_name.str == NULL)
 
737
    return true;                             /* the error is set */
 
738
  memcpy(new_db_file_name.str, new_db_name->str, new_db_name->length);
 
739
  new_db_file_name.str[new_db_name->length]= 0;
 
740
 
 
741
 
 
742
  /*
 
743
    NOTE: if check_db_name() fails, we should throw an error in any case,
 
744
    even if we are called from sp_head::execute().
 
745
 
 
746
    It's next to impossible however to get this error when we are called
 
747
    from sp_head::execute(). But let's switch the current database to NULL
 
748
    in this case to be sure.
 
749
  */
 
750
 
 
751
  if (check_db_name(&new_db_file_name))
 
752
  {
 
753
    my_error(ER_WRONG_DB_NAME, MYF(0), new_db_file_name.str);
 
754
    free(new_db_file_name.str);
 
755
 
 
756
    if (force_switch)
 
757
      mysql_change_db_impl(session, NULL);
 
758
 
 
759
    return true;
 
760
  }
 
761
 
 
762
  if (check_db_dir_existence(new_db_file_name.str))
 
763
  {
 
764
    if (force_switch)
 
765
    {
 
766
      /* Throw a warning and free new_db_file_name. */
 
767
 
 
768
      push_warning_printf(session, DRIZZLE_ERROR::WARN_LEVEL_NOTE,
 
769
                          ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR),
 
770
                          new_db_file_name.str);
 
771
 
 
772
      free(new_db_file_name.str);
 
773
 
 
774
      /* Change db to NULL. */
 
775
 
 
776
      mysql_change_db_impl(session, NULL);
 
777
 
 
778
      /* The operation succeed. */
 
779
 
 
780
      return false;
 
781
    }
 
782
    else
 
783
    {
 
784
      /* Report an error and free new_db_file_name. */
 
785
 
 
786
      my_error(ER_BAD_DB_ERROR, MYF(0), new_db_file_name.str);
 
787
      free(new_db_file_name.str);
 
788
 
 
789
      /* The operation failed. */
 
790
 
 
791
      return true;
 
792
    }
 
793
  }
 
794
 
 
795
  db_default_cl= get_default_db_collation(new_db_file_name.str);
 
796
 
 
797
  mysql_change_db_impl(session, &new_db_file_name);
 
798
  free(new_db_file_name.str);
367
799
 
368
800
  return false;
369
801
}
370
802
 
371
 
/**
372
 
  @brief Internal implementation: switch current database to a valid one.
373
 
 
374
 
  @param session            Thread context.
375
 
  @param new_db_name    Name of the database to switch to. The function will
376
 
                        take ownership of the name (the caller must not free
377
 
                        the allocated memory). If the name is NULL, we're
378
 
                        going to switch to NULL db.
379
 
  @param new_db_charset Character set of the new database.
 
803
/*
 
804
  Check if there is directory for the database name.
 
805
 
 
806
  SYNOPSIS
 
807
    check_db_dir_existence()
 
808
    db_name   database name
 
809
 
 
810
  RETURN VALUES
 
811
    false   There is directory for the specified database name.
 
812
    true    The directory does not exist.
380
813
*/
381
814
 
382
 
static void change_db_impl(Session *session, identifier::Schema &schema_identifier)
383
 
{
384
 
  /* 1. Change current database in Session. */
385
 
 
386
 
#if 0
387
 
  if (new_db_name == NULL)
388
 
  {
389
 
    /*
390
 
      Session::set_db() does all the job -- it frees previous database name and
391
 
      sets the new one.
392
 
    */
393
 
 
394
 
    session->set_db(NULL, 0);
395
 
  }
396
 
  else
397
 
#endif
398
 
  {
399
 
    /*
400
 
      Here we already have a copy of database name to be used in Session. So,
401
 
      we just call Session::reset_db(). Since Session::reset_db() does not releases
402
 
      the previous database name, we should do it explicitly.
403
 
    */
404
 
 
405
 
    session->set_db(schema_identifier.getSchemaName());
406
 
  }
407
 
}
408
 
 
409
 
static void change_db_impl(Session *session)
410
 
{
411
 
  session->set_db(string());
412
 
}
413
 
 
414
 
} /* namespace drizzled */
 
815
bool check_db_dir_existence(const char *db_name)
 
816
{
 
817
  char db_dir_path[FN_REFLEN];
 
818
  uint32_t db_dir_path_len;
 
819
 
 
820
  db_dir_path_len= build_table_filename(db_dir_path, sizeof(db_dir_path),
 
821
                                        db_name, "", false);
 
822
 
 
823
  if (db_dir_path_len && db_dir_path[db_dir_path_len - 1] == FN_LIBCHAR)
 
824
    db_dir_path[db_dir_path_len - 1]= 0;
 
825
 
 
826
  /* Check access. */
 
827
 
 
828
  return my_access(db_dir_path, F_OK);
 
829
}