~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/db.cc

  • Committer: Brian Aker
  • Date: 2009-08-17 01:44:23 UTC
  • mto: This revision was merged to the branch mainline in revision 1118.
  • Revision ID: brian@gaz-20090817014423-jxi2qonsumm8mndf
Remove SQL level reference for DELAY (just now done correctly by default in
engine).

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
extern drizzled::ReplicationServices replication_services;
 
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
  drizzled::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
  drizzled::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, drizzled::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)
82
190
{
83
 
  TransactionServices &transaction_services= TransactionServices::singleton();
 
191
  char   path[FN_REFLEN+16];
 
192
  long result= 1;
 
193
  int error_erno;
84
194
  bool error= false;
 
195
  uint32_t create_options= create_info ? create_info->options : 0;
 
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 (!(create_options & HA_LEX_CREATE_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
  long result=1;
 
279
  int error= 0;
 
280
  char   path[FN_REFLEN+16];
 
281
  uint32_t path_len;
151
282
 
152
283
  /*
153
284
    Do not alter database if another thread is holding read lock.
154
 
    Wait for global read lock before acquiring session->catalog()->schemaLock().
 
285
    Wait for global read lock before acquiring LOCK_create_db.
155
286
    After wait_if_global_read_lock() we have protection against another
156
 
    global read lock. If we would acquire session->catalog()->schemaLock() first,
 
287
    global read lock. If we would acquire LOCK_create_db first,
157
288
    another thread could step in and get the global read lock before we
158
289
    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()...
 
290
    (admin a db), it would then go and wait on LOCK_create_db...
160
291
    Furthermore wait_if_global_read_lock() checks if the current thread
161
292
    has the global read lock and refuses the operation with
162
293
    ER_CANT_UPDATE_WITH_READLOCK if applicable.
163
294
  */
164
 
  if ((session->wait_if_global_read_lock(false, true)))
165
 
    return false;
166
 
 
167
 
  bool success;
 
295
  if ((error=wait_if_global_read_lock(session,0,1)))
 
296
    goto exit;
 
297
 
 
298
  pthread_mutex_lock(&LOCK_create_db);
 
299
 
 
300
  /* Change options if current database is being altered. */
 
301
  path_len= build_table_filename(path, sizeof(path), db, "", false);
 
302
  path[path_len-1]= 0;                    // Remove last '/' from path
 
303
 
 
304
  error= write_schema_file(session, path, db, create_info);
 
305
  if (error && error != EEXIST)
168
306
  {
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
 
    }
 
307
    /* TODO: find some witty way of getting back an error message */
 
308
    pthread_mutex_unlock(&LOCK_create_db);
 
309
    goto exit;
190
310
  }
191
 
  session->startWaitingGlobalReadLock();
192
 
 
193
 
  return success;
 
311
 
 
312
  replication_services.rawStatement(session, session->getQueryString(), session->getQueryLength());
 
313
  session->my_ok(result);
 
314
 
 
315
  pthread_mutex_unlock(&LOCK_create_db);
 
316
  start_waiting_global_read_lock(session);
 
317
exit:
 
318
  return error ? true : false;
194
319
}
195
320
 
196
321
 
198
323
  Drop all tables in a database and the database itself
199
324
 
200
325
  SYNOPSIS
201
 
    rm_db()
 
326
    mysql_rm_db()
202
327
    session                     Thread handle
203
328
    db                  Database name in the case given by user
204
329
                        It's already validated and set to lower case
211
336
    ERROR Error
212
337
*/
213
338
 
214
 
bool rm_db(Session *session, identifier::Schema &schema_identifier, const bool if_exists)
 
339
bool mysql_rm_db(Session *session,char *db,bool if_exists)
215
340
{
216
 
  bool error= false;
 
341
  long deleted=0;
 
342
  int error= false;
 
343
  char  path[FN_REFLEN+16];
 
344
  MY_DIR *dirp;
 
345
  uint32_t length;
 
346
  TableList *dropped_tables= NULL;
 
347
 
 
348
  if (db && (strcmp(db, "information_schema") == 0))
 
349
  {
 
350
    my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), "", "", INFORMATION_SCHEMA_NAME.c_str());
 
351
    return true;
 
352
  }
217
353
 
218
354
  /*
219
355
    Do not drop database if another thread is holding read lock.
220
 
    Wait for global read lock before acquiring session->catalog()->schemaLock().
 
356
    Wait for global read lock before acquiring LOCK_create_db.
221
357
    After wait_if_global_read_lock() we have protection against another
222
 
    global read lock. If we would acquire session->catalog()->schemaLock() first,
 
358
    global read lock. If we would acquire LOCK_create_db first,
223
359
    another thread could step in and get the global read lock before we
224
360
    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()...
 
361
    (admin a db), it would then go and wait on LOCK_create_db...
226
362
    Furthermore wait_if_global_read_lock() checks if the current thread
227
363
    has the global read lock and refuses the operation with
228
364
    ER_CANT_UPDATE_WITH_READLOCK if applicable.
229
365
  */
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
 
 
 
366
  if (wait_if_global_read_lock(session, 0, 1))
 
367
  {
 
368
    error= -1;
 
369
    goto exit2;
 
370
  }
 
371
 
 
372
  pthread_mutex_lock(&LOCK_create_db);
 
373
 
 
374
  length= build_table_filename(path, sizeof(path), db, "", false);
 
375
  strcpy(path+length, MY_DB_OPT_FILE);         // Append db option file name
 
376
  unlink(path);
 
377
  path[length]= '\0';                           // Remove file name
 
378
 
 
379
  /* See if the directory exists */
 
380
  if (!(dirp= my_dir(path,MYF(MY_DONT_SORT))))
 
381
  {
 
382
    if (!if_exists)
 
383
    {
 
384
      error= -1;
 
385
      my_error(ER_DB_DROP_EXISTS, MYF(0), db);
 
386
      goto exit;
 
387
    }
 
388
    else
 
389
      push_warning_printf(session, DRIZZLE_ERROR::WARN_LEVEL_NOTE,
 
390
                          ER_DB_DROP_EXISTS, ER(ER_DB_DROP_EXISTS), db);
 
391
  }
 
392
  else
 
393
  {
 
394
    pthread_mutex_lock(&LOCK_open); /* After deleting database, remove all cache entries related to schema */
 
395
    remove_db_from_cache(db);
 
396
    pthread_mutex_unlock(&LOCK_open);
 
397
 
 
398
 
 
399
    error= -1;
 
400
    if ((deleted= mysql_rm_known_files(session, dirp, db, path, &dropped_tables)) >= 0)
 
401
    {
 
402
      ha_drop_database(path);
 
403
      error = 0;
 
404
    }
 
405
  }
 
406
  if (deleted >= 0)
 
407
  {
 
408
    const char *query;
 
409
    uint32_t query_length;
 
410
    if (!session->query)
 
411
    {
 
412
      /* The client used the old obsolete mysql_drop_db() call */
 
413
      query= path;
 
414
      query_length= sprintf(path, "drop database `%s`", db);
 
415
    }
 
416
    else
 
417
    {
 
418
      query= session->query;
 
419
      query_length= session->query_length;
 
420
    }
 
421
    replication_services.rawStatement(session, session->getQueryString(), session->getQueryLength());
 
422
    session->clear_error();
 
423
    session->server_status|= SERVER_STATUS_DB_DROPPED;
 
424
    session->my_ok((uint32_t) deleted);
 
425
    session->server_status&= ~SERVER_STATUS_DB_DROPPED;
 
426
  }
 
427
  else
 
428
  {
 
429
    char *query, *query_pos, *query_end, *query_data_start;
 
430
    TableList *tbl;
 
431
    uint32_t db_len;
 
432
 
 
433
    if (!(query= (char*) session->alloc(MAX_DROP_TABLE_Q_LEN)))
 
434
      goto exit; /* not much else we can do */
 
435
    query_pos= query_data_start= strcpy(query,"drop table ")+11;
 
436
    query_end= query + MAX_DROP_TABLE_Q_LEN;
 
437
    db_len= strlen(db);
 
438
 
 
439
    for (tbl= dropped_tables; tbl; tbl= tbl->next_local)
 
440
    {
 
441
      uint32_t tbl_name_len;
 
442
 
 
443
      /* 3 for the quotes and the comma*/
 
444
      tbl_name_len= strlen(tbl->table_name) + 3;
 
445
      if (query_pos + tbl_name_len + 1 >= query_end)
 
446
      {
 
447
        /* These DDL methods and logging protected with LOCK_create_db */
 
448
        replication_services.rawStatement(session, query, (size_t) (query_pos -1 - query));
 
449
        query_pos= query_data_start;
 
450
      }
 
451
 
 
452
      *query_pos++ = '`';
 
453
      query_pos= strcpy(query_pos,tbl->table_name) + (tbl_name_len-3);
 
454
      *query_pos++ = '`';
 
455
      *query_pos++ = ',';
 
456
    }
 
457
 
 
458
    if (query_pos != query_data_start)
 
459
    {
 
460
      /* These DDL methods and logging protected with LOCK_create_db */
 
461
      replication_services.rawStatement(session, query, (size_t) (query_pos -1 - query));
 
462
    }
 
463
  }
 
464
 
 
465
exit:
265
466
  /*
266
467
    If this database was the client's selected database, we silently
267
468
    change the client's selected database to nothing (to have an empty
268
469
    SELECT DATABASE() in the future). For this we free() session->db and set
269
470
    it to 0.
270
471
  */
271
 
  if (not error and schema_identifier.compare(*session->schema()))
272
 
    change_db_impl(session);
273
 
 
274
 
  session->startWaitingGlobalReadLock();
275
 
 
276
 
  return error;
277
 
}
 
472
  if (session->db && !strcmp(session->db, db))
 
473
    mysql_change_db_impl(session, NULL);
 
474
  pthread_mutex_unlock(&LOCK_create_db);
 
475
  start_waiting_global_read_lock(session);
 
476
exit2:
 
477
  return(error);
 
478
}
 
479
 
 
480
/*
 
481
  Removes files with known extensions plus.
 
482
  session MUST be set when calling this function!
 
483
*/
 
484
 
 
485
static long mysql_rm_known_files(Session *session, MY_DIR *dirp, const char *db,
 
486
                                 const char *org_path,
 
487
                                 TableList **dropped_tables)
 
488
{
 
489
  long deleted= 0;
 
490
  char filePath[FN_REFLEN];
 
491
  TableList *tot_list= NULL, **tot_list_next;
 
492
 
 
493
  tot_list_next= &tot_list;
 
494
 
 
495
  for (uint32_t idx= 0;
 
496
       idx < (uint32_t) dirp->number_off_files && !session->killed ;
 
497
       idx++)
 
498
  {
 
499
    FILEINFO *file=dirp->dir_entry+idx;
 
500
    char *extension;
 
501
 
 
502
    /* skiping . and .. */
 
503
    if (file->name[0] == '.' && (!file->name[1] ||
 
504
       (file->name[1] == '.' &&  !file->name[2])))
 
505
      continue;
 
506
 
 
507
    if (!(extension= strrchr(file->name, '.')))
 
508
      extension= strchr(file->name, '\0');
 
509
    if (find_type(extension, &deletable_extentions,1+2) <= 0)
 
510
    {
 
511
      /*
 
512
        ass ass ass.
 
513
 
 
514
        strange checking for magic extensions that are then deleted if
 
515
        not reg_ext (i.e. .frm).
 
516
 
 
517
        and (previously) we'd err out on drop database if files not matching
 
518
        engine ha_known_exts() or deletable_extensions were present.
 
519
 
 
520
        presumably this was to avoid deleting other user data... except if that
 
521
        data happened to be in files ending in .BAK, .opt or .TMD. *fun*
 
522
       */
 
523
      continue;
 
524
    }
 
525
    /* just for safety we use files_charset_info */
 
526
    if (db && !my_strcasecmp(files_charset_info,
 
527
                             extension, ".dfe"))
 
528
    {
 
529
      uint32_t db_len= strlen(db);
 
530
 
 
531
      /* Drop the table nicely */
 
532
      *extension= 0;                    // Remove extension
 
533
      TableList *table_list=(TableList*)
 
534
                              session->calloc(sizeof(*table_list) +
 
535
                                          db_len + 1 +
 
536
                                          strlen(file->name) + 1);
 
537
 
 
538
      if (!table_list)
 
539
        goto err;
 
540
      table_list->db= (char*) (table_list+1);
 
541
      table_list->table_name= strcpy(table_list->db, db) + db_len + 1;
 
542
      filename_to_tablename(file->name, table_list->table_name,
 
543
                            strlen(file->name) + 1);
 
544
      table_list->alias= table_list->table_name;        // If lower_case_table_names=2
 
545
      table_list->internal_tmp_table= is_prefix(file->name, TMP_FILE_PREFIX);
 
546
      /* Link into list */
 
547
      (*tot_list_next)= table_list;
 
548
      tot_list_next= &table_list->next_local;
 
549
      deleted++;
 
550
    }
 
551
    else
 
552
    {
 
553
      sprintf(filePath, "%s/%s", org_path, file->name);
 
554
      if (my_delete_with_symlink(filePath,MYF(MY_WME)))
 
555
      {
 
556
        goto err;
 
557
      }
 
558
    }
 
559
  }
 
560
  if (session->killed ||
 
561
      (tot_list && mysql_rm_table_part2(session, tot_list, 1, 0, 1)))
 
562
    goto err;
 
563
 
 
564
  my_dirend(dirp);
 
565
 
 
566
  if (dropped_tables)
 
567
    *dropped_tables= tot_list;
 
568
 
 
569
  if (rmdir(org_path))
 
570
  {
 
571
    my_error(ER_DB_DROP_RMDIR, MYF(0), org_path, errno);
 
572
    return -1;
 
573
  }
 
574
 
 
575
  return deleted;
 
576
 
 
577
err:
 
578
  my_dirend(dirp);
 
579
  return -1;
 
580
}
 
581
 
 
582
/**
 
583
  @brief Internal implementation: switch current database to a valid one.
 
584
 
 
585
  @param session            Thread context.
 
586
  @param new_db_name    Name of the database to switch to. The function will
 
587
                        take ownership of the name (the caller must not free
 
588
                        the allocated memory). If the name is NULL, we're
 
589
                        going to switch to NULL db.
 
590
  @param new_db_charset Character set of the new database.
 
591
*/
 
592
 
 
593
static void mysql_change_db_impl(Session *session, LEX_STRING *new_db_name)
 
594
{
 
595
  /* 1. Change current database in Session. */
 
596
 
 
597
  if (new_db_name == NULL)
 
598
  {
 
599
    /*
 
600
      Session::set_db() does all the job -- it frees previous database name and
 
601
      sets the new one.
 
602
    */
 
603
 
 
604
    session->set_db(NULL, 0);
 
605
  }
 
606
  else if (my_strcasecmp(system_charset_info, new_db_name->str,
 
607
                         INFORMATION_SCHEMA_NAME.c_str()) == 0)
 
608
  {
 
609
    /*
 
610
      Here we must use Session::set_db(), because we want to copy
 
611
      INFORMATION_SCHEMA_NAME constant.
 
612
    */
 
613
 
 
614
    session->set_db(INFORMATION_SCHEMA_NAME.c_str(),
 
615
                    INFORMATION_SCHEMA_NAME.length());
 
616
  }
 
617
  else
 
618
  {
 
619
    /*
 
620
      Here we already have a copy of database name to be used in Session. So,
 
621
      we just call Session::reset_db(). Since Session::reset_db() does not releases
 
622
      the previous database name, we should do it explicitly.
 
623
    */
 
624
 
 
625
    if (session->db)
 
626
      free(session->db);
 
627
 
 
628
    session->reset_db(new_db_name->str, new_db_name->length);
 
629
  }
 
630
}
 
631
 
 
632
/**
 
633
  Return true if db1_name is equal to db2_name, false otherwise.
 
634
 
 
635
  The function allows to compare database names according to the MySQL
 
636
  rules. The database names db1 and db2 are equal if:
 
637
     - db1 is NULL and db2 is NULL;
 
638
     or
 
639
     - db1 is not-NULL, db2 is not-NULL, db1 is equal (ignoring case) to
 
640
       db2 in system character set (UTF8).
 
641
*/
 
642
 
 
643
static inline bool
 
644
cmp_db_names(const char *db1_name,
 
645
             const char *db2_name)
 
646
{
 
647
  return
 
648
         /* db1 is NULL and db2 is NULL */
 
649
         (!db1_name && !db2_name) ||
 
650
 
 
651
         /* db1 is not-NULL, db2 is not-NULL, db1 == db2. */
 
652
         (db1_name && db2_name && my_strcasecmp(system_charset_info, db1_name, db2_name) == 0);
 
653
}
 
654
 
278
655
 
279
656
/**
280
657
  @brief Change the current database and its attributes unconditionally.
338
715
    @retval true  Error
339
716
*/
340
717
 
341
 
bool change_db(Session *session, identifier::Schema &schema_identifier)
 
718
bool mysql_change_db(Session *session, const LEX_STRING *new_db_name, bool force_switch)
342
719
{
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);
 
720
  LEX_STRING new_db_file_name;
 
721
  const CHARSET_INFO *db_default_cl;
 
722
 
 
723
  assert(new_db_name);
 
724
  assert(new_db_name->length);
 
725
 
 
726
  if (my_strcasecmp(system_charset_info, new_db_name->str,
 
727
                    INFORMATION_SCHEMA_NAME.c_str()) == 0)
 
728
  {
 
729
    /* Switch the current database to INFORMATION_SCHEMA. */
 
730
    /* const_cast<> is safe here: mysql_change_db_impl does a copy */
 
731
    LEX_STRING is_name= { const_cast<char *>(INFORMATION_SCHEMA_NAME.c_str()),
 
732
                          INFORMATION_SCHEMA_NAME.length() };
 
733
    mysql_change_db_impl(session, &is_name);
 
734
 
 
735
    return false;
 
736
  }
 
737
 
 
738
  /*
 
739
    Now we need to make a copy because check_db_name requires a
 
740
    non-constant argument. Actually, it takes database file name.
 
741
 
 
742
    TODO: fix check_db_name().
 
743
  */
 
744
 
 
745
  new_db_file_name.length= new_db_name->length;
 
746
  new_db_file_name.str= (char *)malloc(new_db_name->length + 1);
 
747
  if (new_db_file_name.str == NULL)
 
748
    return true;                             /* the error is set */
 
749
  memcpy(new_db_file_name.str, new_db_name->str, new_db_name->length);
 
750
  new_db_file_name.str[new_db_name->length]= 0;
 
751
 
 
752
 
 
753
  /*
 
754
    NOTE: if check_db_name() fails, we should throw an error in any case,
 
755
    even if we are called from sp_head::execute().
 
756
 
 
757
    It's next to impossible however to get this error when we are called
 
758
    from sp_head::execute(). But let's switch the current database to NULL
 
759
    in this case to be sure.
 
760
  */
 
761
 
 
762
  if (check_db_name(&new_db_file_name))
 
763
  {
 
764
    my_error(ER_WRONG_DB_NAME, MYF(0), new_db_file_name.str);
 
765
    free(new_db_file_name.str);
 
766
 
 
767
    if (force_switch)
 
768
      mysql_change_db_impl(session, NULL);
 
769
 
 
770
    return true;
 
771
  }
 
772
 
 
773
  if (check_db_dir_existence(new_db_file_name.str))
 
774
  {
 
775
    if (force_switch)
 
776
    {
 
777
      /* Throw a warning and free new_db_file_name. */
 
778
 
 
779
      push_warning_printf(session, DRIZZLE_ERROR::WARN_LEVEL_NOTE,
 
780
                          ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR),
 
781
                          new_db_file_name.str);
 
782
 
 
783
      free(new_db_file_name.str);
 
784
 
 
785
      /* Change db to NULL. */
 
786
 
 
787
      mysql_change_db_impl(session, NULL);
 
788
 
 
789
      /* The operation succeed. */
 
790
 
 
791
      return false;
 
792
    }
 
793
    else
 
794
    {
 
795
      /* Report an error and free new_db_file_name. */
 
796
 
 
797
      my_error(ER_BAD_DB_ERROR, MYF(0), new_db_file_name.str);
 
798
      free(new_db_file_name.str);
 
799
 
 
800
      /* The operation failed. */
 
801
 
 
802
      return true;
 
803
    }
 
804
  }
 
805
 
 
806
  /*
 
807
    NOTE: in mysql_change_db_impl() new_db_file_name is assigned to Session
 
808
    attributes and will be freed in Session::~Session().
 
809
  */
 
810
 
 
811
  db_default_cl= get_default_db_collation(new_db_file_name.str);
 
812
 
 
813
  mysql_change_db_impl(session, &new_db_file_name);
367
814
 
368
815
  return false;
369
816
}
370
817
 
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.
 
818
/*
 
819
  Check if there is directory for the database name.
 
820
 
 
821
  SYNOPSIS
 
822
    check_db_dir_existence()
 
823
    db_name   database name
 
824
 
 
825
  RETURN VALUES
 
826
    false   There is directory for the specified database name.
 
827
    true    The directory does not exist.
380
828
*/
381
829
 
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 */
 
830
bool check_db_dir_existence(const char *db_name)
 
831
{
 
832
  char db_dir_path[FN_REFLEN];
 
833
  uint32_t db_dir_path_len;
 
834
 
 
835
  db_dir_path_len= build_table_filename(db_dir_path, sizeof(db_dir_path),
 
836
                                        db_name, "", false);
 
837
 
 
838
  if (db_dir_path_len && db_dir_path[db_dir_path_len - 1] == FN_LIBCHAR)
 
839
    db_dir_path[db_dir_path_len - 1]= 0;
 
840
 
 
841
  /* Check access. */
 
842
 
 
843
  return my_access(db_dir_path, F_OK);
 
844
}