~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/db.cc

  • Committer: kalebral at gmail
  • Date: 2010-12-04 04:58:08 UTC
  • mto: (1971.1.2 build)
  • mto: This revision was merged to the branch mainline in revision 1972.
  • Revision ID: kalebral@gmail.com-20101204045808-acto22oxfg43m02e
a few more updates of files that did not have license or had incorrect license structure

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
#include <string>
26
26
#include <fstream>
27
27
 
 
28
#include <drizzled/message/schema.pb.h>
28
29
#include "drizzled/error.h"
29
30
#include <drizzled/gettext.h>
30
31
#include <drizzled/my_hash.h>
45
46
 
46
47
#include <boost/thread/mutex.hpp>
47
48
 
 
49
boost::mutex LOCK_create_db;
 
50
 
48
51
#include "drizzled/internal/my_sys.h"
49
52
 
50
53
#define MAX_DROP_TABLE_Q_LEN      1024
54
57
namespace drizzled
55
58
{
56
59
 
57
 
static void change_db_impl(Session *session);
58
 
static void change_db_impl(Session *session, identifier::Schema &schema_identifier);
 
60
static long drop_tables_via_filenames(Session *session,
 
61
                                 SchemaIdentifier &schema_identifier,
 
62
                                 TableIdentifier::vector &dropped_tables);
 
63
static void mysql_change_db_impl(Session *session);
 
64
static void mysql_change_db_impl(Session *session, SchemaIdentifier &schema_identifier);
59
65
 
60
66
/*
61
67
  Create a database
62
68
 
63
69
  SYNOPSIS
64
 
  create_db()
 
70
  mysql_create_db()
65
71
  session               Thread handler
66
72
  db            Name of database to create
67
73
                Function assumes that this is already validated.
78
84
 
79
85
*/
80
86
 
81
 
bool create_db(Session *session, const message::Schema &schema_message, const bool is_if_not_exists)
 
87
bool mysql_create_db(Session *session, const message::Schema &schema_message, const bool is_if_not_exists)
82
88
{
83
89
  TransactionServices &transaction_services= TransactionServices::singleton();
84
90
  bool error= false;
85
91
 
86
92
  /*
87
93
    Do not create database if another thread is holding read lock.
88
 
    Wait for global read lock before acquiring session->catalog()->schemaLock().
 
94
    Wait for global read lock before acquiring LOCK_create_db.
89
95
    After wait_if_global_read_lock() we have protection against another
90
 
    global read lock. If we would acquire session->catalog()->schemaLock() first,
 
96
    global read lock. If we would acquire LOCK_create_db first,
91
97
    another thread could step in and get the global read lock before we
92
98
    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()...
 
99
    (admin a db), it would then go and wait on LOCK_create_db...
94
100
    Furthermore wait_if_global_read_lock() checks if the current thread
95
101
    has the global read lock and refuses the operation with
96
102
    ER_CANT_UPDATE_WITH_READLOCK if applicable.
105
111
 
106
112
  // @todo push this lock down into the engine
107
113
  {
108
 
    boost::mutex::scoped_lock scopedLock(session->catalog().schemaLock());
 
114
    boost::mutex::scoped_lock scopedLock(LOCK_create_db);
109
115
 
110
116
    // Check to see if it exists already.  
111
 
    identifier::Schema schema_identifier(schema_message.name());
 
117
    SchemaIdentifier schema_identifier(schema_message.name());
112
118
    if (plugin::StorageEngine::doesSchemaExist(schema_identifier))
113
119
    {
114
120
      if (not is_if_not_exists)
115
121
      {
116
 
        my_error(ER_DB_CREATE_EXISTS, schema_identifier);
 
122
        my_error(ER_DB_CREATE_EXISTS, MYF(0), schema_message.name().c_str());
117
123
        error= true;
118
124
      }
119
125
      else
131
137
    }
132
138
    else // Created !
133
139
    {
134
 
      transaction_services.createSchema(*session, schema_message);
 
140
      transaction_services.createSchema(session, schema_message);
135
141
      session->my_ok(1);
136
142
    }
137
143
  }
143
149
 
144
150
/* db-name is already validated when we come here */
145
151
 
146
 
bool alter_db(Session *session,
147
 
              const message::Schema &schema_message,
148
 
              const message::schema::shared_ptr &original_schema)
 
152
bool mysql_alter_db(Session *session, const message::Schema &schema_message)
149
153
{
150
154
  TransactionServices &transaction_services= TransactionServices::singleton();
151
155
 
152
156
  /*
153
157
    Do not alter database if another thread is holding read lock.
154
 
    Wait for global read lock before acquiring session->catalog()->schemaLock().
 
158
    Wait for global read lock before acquiring LOCK_create_db.
155
159
    After wait_if_global_read_lock() we have protection against another
156
 
    global read lock. If we would acquire session->catalog()->schemaLock() first,
 
160
    global read lock. If we would acquire LOCK_create_db first,
157
161
    another thread could step in and get the global read lock before we
158
162
    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()...
 
163
    (admin a db), it would then go and wait on LOCK_create_db...
160
164
    Furthermore wait_if_global_read_lock() checks if the current thread
161
165
    has the global read lock and refuses the operation with
162
166
    ER_CANT_UPDATE_WITH_READLOCK if applicable.
166
170
 
167
171
  bool success;
168
172
  {
169
 
    boost::mutex::scoped_lock scopedLock(session->catalog().schemaLock());
 
173
    boost::mutex::scoped_lock scopedLock(LOCK_create_db);
170
174
 
171
 
    identifier::Schema schema_idenifier(schema_message.name());
 
175
    SchemaIdentifier schema_idenifier(schema_message.name());
172
176
    if (not plugin::StorageEngine::doesSchemaExist(schema_idenifier))
173
177
    {
174
 
      my_error(ER_SCHEMA_DOES_NOT_EXIST, schema_idenifier);
 
178
      my_error(ER_SCHEMA_DOES_NOT_EXIST, MYF(0), schema_message.name().c_str());
175
179
      return false;
176
180
    }
177
181
 
180
184
 
181
185
    if (success)
182
186
    {
183
 
      transaction_services.alterSchema(*session, original_schema, schema_message);
 
187
      transaction_services.rawStatement(session, *session->getQueryString());
184
188
      session->my_ok(1);
185
189
    }
186
190
    else
187
191
    {
188
 
      my_error(ER_ALTER_SCHEMA, schema_idenifier);
 
192
      my_error(ER_ALTER_SCHEMA, MYF(0), schema_message.name().c_str());
189
193
    }
190
194
  }
191
195
  session->startWaitingGlobalReadLock();
198
202
  Drop all tables in a database and the database itself
199
203
 
200
204
  SYNOPSIS
201
 
    rm_db()
 
205
    mysql_rm_db()
202
206
    session                     Thread handle
203
207
    db                  Database name in the case given by user
204
208
                        It's already validated and set to lower case
211
215
    ERROR Error
212
216
*/
213
217
 
214
 
bool rm_db(Session *session, identifier::Schema &schema_identifier, const bool if_exists)
 
218
bool mysql_rm_db(Session *session, SchemaIdentifier &schema_identifier, const bool if_exists)
215
219
{
216
 
  bool error= false;
 
220
  long deleted=0;
 
221
  int error= false;
 
222
  TableIdentifier::vector dropped_tables;
 
223
  message::Schema schema_proto;
217
224
 
218
225
  /*
219
226
    Do not drop database if another thread is holding read lock.
220
 
    Wait for global read lock before acquiring session->catalog()->schemaLock().
 
227
    Wait for global read lock before acquiring LOCK_create_db.
221
228
    After wait_if_global_read_lock() we have protection against another
222
 
    global read lock. If we would acquire session->catalog()->schemaLock() first,
 
229
    global read lock. If we would acquire LOCK_create_db first,
223
230
    another thread could step in and get the global read lock before we
224
231
    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()...
 
232
    (admin a db), it would then go and wait on LOCK_create_db...
226
233
    Furthermore wait_if_global_read_lock() checks if the current thread
227
234
    has the global read lock and refuses the operation with
228
235
    ER_CANT_UPDATE_WITH_READLOCK if applicable.
229
236
  */
230
237
  if (session->wait_if_global_read_lock(false, true))
231
238
  {
232
 
    return true;
233
 
  }
234
 
 
235
 
  do
236
 
  {
237
 
    boost::mutex::scoped_lock scopedLock(session->catalog().schemaLock());
 
239
    return -1;
 
240
  }
 
241
 
 
242
  // Lets delete the temporary tables first outside of locks.  
 
243
  set<string> set_of_names;
 
244
  session->doGetTableNames(schema_identifier, set_of_names);
 
245
 
 
246
  for (set<string>::iterator iter= set_of_names.begin(); iter != set_of_names.end(); iter++)
 
247
  {
 
248
    TableIdentifier identifier(schema_identifier, *iter, message::Table::TEMPORARY);
 
249
    Table *table= session->find_temporary_table(identifier);
 
250
    session->close_temporary_table(table);
 
251
  }
 
252
 
 
253
  {
 
254
    boost::mutex::scoped_lock scopedLock(LOCK_create_db);
238
255
 
239
256
    /* See if the schema exists */
240
257
    if (not plugin::StorageEngine::doesSchemaExist(schema_identifier))
241
258
    {
 
259
      std::string path;
 
260
      schema_identifier.getSQLPath(path);
 
261
 
242
262
      if (if_exists)
243
263
      {
244
 
        std::string path;
245
 
        schema_identifier.getSQLPath(path);
246
 
 
247
264
        push_warning_printf(session, DRIZZLE_ERROR::WARN_LEVEL_NOTE,
248
265
                            ER_DB_DROP_EXISTS, ER(ER_DB_DROP_EXISTS),
249
266
                            path.c_str());
250
267
      }
251
268
      else
252
269
      {
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
 
 
265
 
  /*
266
 
    If this database was the client's selected database, we silently
267
 
    change the client's selected database to nothing (to have an empty
268
 
    SELECT DATABASE() in the future). For this we free() session->db and set
269
 
    it to 0.
270
 
  */
271
 
  if (not error and schema_identifier.compare(*session->schema()))
272
 
    change_db_impl(session);
 
270
        error= -1;
 
271
        my_error(ER_DB_DROP_EXISTS, MYF(0), path.c_str());
 
272
        goto exit;
 
273
      }
 
274
    }
 
275
    else
 
276
    {
 
277
      /* After deleting database, remove all cache entries related to schema */
 
278
      table::Cache::singleton().removeSchema(schema_identifier);
 
279
 
 
280
 
 
281
      error= -1;
 
282
      deleted= drop_tables_via_filenames(session, schema_identifier, dropped_tables);
 
283
      if (deleted >= 0)
 
284
      {
 
285
        error= 0;
 
286
      }
 
287
    }
 
288
    if (deleted >= 0)
 
289
    {
 
290
      assert(not session->getQueryString()->empty());
 
291
 
 
292
      TransactionServices &transaction_services= TransactionServices::singleton();
 
293
      transaction_services.dropSchema(session, schema_identifier.getSchemaName());
 
294
      session->clear_error();
 
295
      session->server_status|= SERVER_STATUS_DB_DROPPED;
 
296
      session->my_ok((uint32_t) deleted);
 
297
      session->server_status&= ~SERVER_STATUS_DB_DROPPED;
 
298
    }
 
299
    else
 
300
    {
 
301
      char *query, *query_pos, *query_end, *query_data_start;
 
302
 
 
303
      if (!(query= (char*) session->alloc(MAX_DROP_TABLE_Q_LEN)))
 
304
        goto exit; /* not much else we can do */
 
305
      query_pos= query_data_start= strcpy(query,"drop table ")+11;
 
306
      query_end= query + MAX_DROP_TABLE_Q_LEN;
 
307
 
 
308
      TransactionServices &transaction_services= TransactionServices::singleton();
 
309
      for (TableIdentifier::vector::iterator it= dropped_tables.begin();
 
310
           it != dropped_tables.end();
 
311
           it++)
 
312
      {
 
313
        uint32_t tbl_name_len;
 
314
 
 
315
        /* 3 for the quotes and the comma*/
 
316
        tbl_name_len= (*it).getTableName().length() + 3;
 
317
        if (query_pos + tbl_name_len + 1 >= query_end)
 
318
        {
 
319
          /* These DDL methods and logging protected with LOCK_create_db */
 
320
          transaction_services.rawStatement(session, query);
 
321
          query_pos= query_data_start;
 
322
        }
 
323
 
 
324
        *query_pos++ = '`';
 
325
        query_pos= strcpy(query_pos, (*it).getTableName().c_str()) + (tbl_name_len-3);
 
326
        *query_pos++ = '`';
 
327
        *query_pos++ = ',';
 
328
      }
 
329
 
 
330
      if (query_pos != query_data_start)
 
331
      {
 
332
        /* These DDL methods and logging protected with LOCK_create_db */
 
333
        transaction_services.rawStatement(session, query);
 
334
      }
 
335
    }
 
336
 
 
337
exit:
 
338
    /*
 
339
      If this database was the client's selected database, we silently
 
340
      change the client's selected database to nothing (to have an empty
 
341
      SELECT DATABASE() in the future). For this we free() session->db and set
 
342
      it to 0.
 
343
    */
 
344
    if (schema_identifier.compare(session->db))
 
345
      mysql_change_db_impl(session);
 
346
  }
273
347
 
274
348
  session->startWaitingGlobalReadLock();
275
349
 
276
350
  return error;
277
351
}
278
352
 
 
353
 
 
354
static int rm_table_part2(Session *session, TableList *tables)
 
355
{
 
356
  TransactionServices &transaction_services= TransactionServices::singleton();
 
357
 
 
358
  TableList *table;
 
359
  String wrong_tables;
 
360
  int error= 0;
 
361
  bool foreign_key_error= false;
 
362
 
 
363
  {
 
364
    table::Cache::singleton().mutex().lock(); /* Part 2 of rm a table */
 
365
 
 
366
    if (session->lock_table_names_exclusively(tables))
 
367
    {
 
368
      table::Cache::singleton().mutex().unlock();
 
369
      return 1;
 
370
    }
 
371
 
 
372
    /* Don't give warnings for not found errors, as we already generate notes */
 
373
    session->no_warnings_for_error= 1;
 
374
 
 
375
    for (table= tables; table; table= table->next_local)
 
376
    {
 
377
      const char *db=table->getSchemaName();
 
378
      TableIdentifier identifier(table->getSchemaName(), table->getTableName());
 
379
 
 
380
      plugin::StorageEngine *table_type;
 
381
 
 
382
      error= session->drop_temporary_table(identifier);
 
383
 
 
384
      switch (error) {
 
385
      case  0:
 
386
        // removed temporary table
 
387
        continue;
 
388
      case -1:
 
389
        error= 1;
 
390
        tables->unlock_table_names();
 
391
        table::Cache::singleton().mutex().unlock();
 
392
        session->no_warnings_for_error= 0;
 
393
 
 
394
        return(error);
 
395
      default:
 
396
        // temporary table not found
 
397
        error= 0;
 
398
      }
 
399
 
 
400
      table_type= table->getDbType();
 
401
 
 
402
      {
 
403
        Table *locked_table;
 
404
        abort_locked_tables(session, identifier);
 
405
        table::Cache::singleton().removeTable(session, identifier,
 
406
                                              RTFC_WAIT_OTHER_THREAD_FLAG |
 
407
                                              RTFC_CHECK_KILLED_FLAG);
 
408
        /*
 
409
          If the table was used in lock tables, remember it so that
 
410
          unlock_table_names can free it
 
411
        */
 
412
        if ((locked_table= drop_locked_tables(session, identifier)))
 
413
          table->table= locked_table;
 
414
 
 
415
        if (session->getKilled())
 
416
        {
 
417
          error= -1;
 
418
          tables->unlock_table_names();
 
419
          table::Cache::singleton().mutex().unlock();
 
420
          session->no_warnings_for_error= 0;
 
421
 
 
422
          return(error);
 
423
        }
 
424
      }
 
425
      identifier.getPath();
 
426
 
 
427
      if (table_type == NULL && not plugin::StorageEngine::doesTableExist(*session, identifier))
 
428
      {
 
429
        // Table was not found on disk and table can't be created from engine
 
430
        push_warning_printf(session, DRIZZLE_ERROR::WARN_LEVEL_NOTE,
 
431
                            ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
 
432
                            table->getTableName());
 
433
      }
 
434
      else
 
435
      {
 
436
        error= plugin::StorageEngine::dropTable(*session, identifier);
 
437
 
 
438
        if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE))
 
439
        {
 
440
          error= 0;
 
441
          session->clear_error();
 
442
        }
 
443
 
 
444
        if (error == HA_ERR_ROW_IS_REFERENCED)
 
445
        {
 
446
          /* the table is referenced by a foreign key constraint */
 
447
          foreign_key_error= true;
 
448
        }
 
449
      }
 
450
 
 
451
      if (error == 0 || (foreign_key_error == false))
 
452
      {
 
453
        transaction_services.dropTable(session, string(db), string(table->getTableName()), true);
 
454
      }
 
455
 
 
456
      if (error)
 
457
      {
 
458
        if (wrong_tables.length())
 
459
          wrong_tables.append(',');
 
460
        wrong_tables.append(String(table->getTableName(),system_charset_info));
 
461
      }
 
462
    }
 
463
    /*
 
464
      It's safe to unlock table::Cache::singleton().mutex(): we have an exclusive lock
 
465
      on the table name.
 
466
    */
 
467
    table::Cache::singleton().mutex().unlock();
 
468
  }
 
469
 
 
470
  error= 0;
 
471
  if (wrong_tables.length())
 
472
  {
 
473
    if (not foreign_key_error)
 
474
      my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
 
475
                      wrong_tables.c_ptr());
 
476
    else
 
477
    {
 
478
      my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
 
479
    }
 
480
    error= 1;
 
481
  }
 
482
 
 
483
  {
 
484
    boost::mutex::scoped_lock scopedLock(table::Cache::singleton().mutex()); /* final bit in rm table lock */
 
485
    tables->unlock_table_names();
 
486
  }
 
487
  session->no_warnings_for_error= 0;
 
488
 
 
489
  return(error);
 
490
}
 
491
 
 
492
/*
 
493
  Removes files with known extensions plus.
 
494
  session MUST be set when calling this function!
 
495
*/
 
496
 
 
497
static long drop_tables_via_filenames(Session *session,
 
498
                                      SchemaIdentifier &schema_identifier,
 
499
                                      TableIdentifier::vector &dropped_tables)
 
500
{
 
501
  long deleted= 0;
 
502
  TableList *tot_list= NULL, **tot_list_next;
 
503
 
 
504
  tot_list_next= &tot_list;
 
505
 
 
506
  plugin::StorageEngine::getIdentifiers(*session, schema_identifier, dropped_tables);
 
507
 
 
508
  for (TableIdentifier::vector::iterator it= dropped_tables.begin();
 
509
       it != dropped_tables.end();
 
510
       it++)
 
511
  {
 
512
    size_t db_len= schema_identifier.getSchemaName().size();
 
513
 
 
514
    /* Drop the table nicely */
 
515
    TableList *table_list=(TableList*)
 
516
      session->calloc(sizeof(*table_list) +
 
517
                      db_len + 1 +
 
518
                      (*it).getTableName().length() + 1);
 
519
 
 
520
    if (not table_list)
 
521
      return -1;
 
522
 
 
523
    table_list->setSchemaName((char*) (table_list+1));
 
524
    table_list->setTableName(strcpy((char*) (table_list+1), schema_identifier.getSchemaName().c_str()) + db_len + 1);
 
525
    TableIdentifier::filename_to_tablename((*it).getTableName().c_str(), const_cast<char *>(table_list->getTableName()), (*it).getTableName().size() + 1);
 
526
    table_list->alias= table_list->getTableName();  // If lower_case_table_names=2
 
527
    table_list->setInternalTmpTable((strncmp((*it).getTableName().c_str(),
 
528
                                             TMP_FILE_PREFIX,
 
529
                                             strlen(TMP_FILE_PREFIX)) == 0));
 
530
    /* Link into list */
 
531
    (*tot_list_next)= table_list;
 
532
    tot_list_next= &table_list->next_local;
 
533
    deleted++;
 
534
  }
 
535
  if (session->getKilled())
 
536
    return -1;
 
537
 
 
538
  if (tot_list)
 
539
  {
 
540
    if (rm_table_part2(session, tot_list))
 
541
      return -1;
 
542
  }
 
543
 
 
544
 
 
545
  if (not plugin::StorageEngine::dropSchema(schema_identifier))
 
546
  {
 
547
    std::string path;
 
548
    schema_identifier.getSQLPath(path);
 
549
    my_error(ER_DROP_SCHEMA, MYF(0), path.c_str());
 
550
 
 
551
    return -1;
 
552
  }
 
553
 
 
554
  return deleted;
 
555
}
 
556
 
279
557
/**
280
558
  @brief Change the current database and its attributes unconditionally.
281
559
 
338
616
    @retval true  Error
339
617
*/
340
618
 
341
 
bool change_db(Session *session, identifier::Schema &schema_identifier)
 
619
bool mysql_change_db(Session *session, SchemaIdentifier &schema_identifier)
342
620
{
343
621
 
344
 
  if (not plugin::Authorization::isAuthorized(session->user(), schema_identifier))
 
622
  if (not plugin::Authorization::isAuthorized(session->getSecurityContext(), schema_identifier))
345
623
  {
346
624
    /* Error message is set in isAuthorized */
347
625
    return true;
349
627
 
350
628
  if (not check_db_name(session, schema_identifier))
351
629
  {
352
 
    my_error(ER_WRONG_DB_NAME, schema_identifier);
 
630
    std::string path;
 
631
    schema_identifier.getSQLPath(path);
 
632
    my_error(ER_WRONG_DB_NAME, MYF(0), path.c_str());
353
633
 
354
634
    return true;
355
635
  }
356
636
 
357
637
  if (not plugin::StorageEngine::doesSchemaExist(schema_identifier))
358
638
  {
359
 
    my_error(ER_BAD_DB_ERROR, schema_identifier);
 
639
    /* Report an error and free new_db_file_name. */
 
640
    std::string path;
 
641
    schema_identifier.getSQLPath(path);
 
642
 
 
643
    my_error(ER_BAD_DB_ERROR, MYF(0), path.c_str());
360
644
 
361
645
    /* The operation failed. */
362
646
 
363
647
    return true;
364
648
  }
365
649
 
366
 
  change_db_impl(session, schema_identifier);
 
650
  mysql_change_db_impl(session, schema_identifier);
367
651
 
368
652
  return false;
369
653
}
379
663
  @param new_db_charset Character set of the new database.
380
664
*/
381
665
 
382
 
static void change_db_impl(Session *session, identifier::Schema &schema_identifier)
 
666
static void mysql_change_db_impl(Session *session, SchemaIdentifier &schema_identifier)
383
667
{
384
668
  /* 1. Change current database in Session. */
385
669
 
406
690
  }
407
691
}
408
692
 
409
 
static void change_db_impl(Session *session)
 
693
static void mysql_change_db_impl(Session *session)
410
694
{
411
695
  session->set_db(string());
412
696
}