~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/statement/create_table.cc

Merge Stewart.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
2
 *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3
3
 *
4
 
 *  Copyright (C) 2009 Sun Microsystems, Inc.
 
4
 *  Copyright (C) 2009 Sun Microsystems
5
5
 *
6
6
 *  This program is free software; you can redistribute it and/or modify
7
7
 *  it under the terms of the GNU General Public License as published by
23
23
#include <drizzled/lock.h>
24
24
#include <drizzled/session.h>
25
25
#include <drizzled/statement/create_table.h>
26
 
#include <drizzled/message.h>
27
 
#include <drizzled/identifier.h>
28
 
#include <drizzled/plugin/storage_engine.h>
29
 
 
30
 
#include <iostream>
 
26
#include <drizzled/table_identifier.h>
31
27
 
32
28
namespace drizzled
33
29
{
34
30
 
35
 
namespace statement {
36
 
 
37
 
CreateTable::CreateTable(Session *in_session, Table_ident *ident, bool is_temporary) :
38
 
  Statement(in_session),
39
 
  change(NULL),
40
 
  default_value(NULL),
41
 
  on_update_value(NULL),
42
 
  is_engine_set(false),
43
 
  is_create_table_like(false),
44
 
  lex_identified_temp_table(false),
45
 
  link_to_local(false),
46
 
  create_table_list(NULL)
47
 
{
48
 
  getSession()->getLex()->sql_command= SQLCOM_CREATE_TABLE;
49
 
  createTableMessage().set_name(ident->table.str, ident->table.length);
50
 
#if 0
51
 
  createTableMessage().set_schema(ident->db.str, ident->db.length);
52
 
#endif
53
 
 
54
 
  if (is_temporary)
55
 
  {
56
 
    createTableMessage().set_type(message::Table::TEMPORARY);
57
 
  }
58
 
  else
59
 
  {
60
 
    createTableMessage().set_type(message::Table::STANDARD);
61
 
  }
62
 
}
63
 
 
64
 
CreateTable::CreateTable(Session *in_session) :
65
 
  Statement(in_session),
66
 
  change(NULL),
67
 
  default_value(NULL),
68
 
  on_update_value(NULL),
69
 
  is_engine_set(false),
70
 
  is_create_table_like(false),
71
 
  lex_identified_temp_table(false),
72
 
  link_to_local(false),
73
 
  create_table_list(NULL)
74
 
{
75
 
  getSession()->getLex()->sql_command= SQLCOM_CREATE_TABLE;
76
 
}
77
 
 
78
 
} // namespace statement
79
 
 
80
31
bool statement::CreateTable::execute()
81
32
{
82
 
  TableList *first_table= (TableList *) getSession()->lex->select_lex.table_list.first;
83
 
  TableList *all_tables= getSession()->lex->query_tables;
 
33
  TableList *first_table= (TableList *) session->lex->select_lex.table_list.first;
 
34
  TableList *all_tables= session->lex->query_tables;
84
35
  assert(first_table == all_tables && first_table != 0);
 
36
  Select_Lex *select_lex= &session->lex->select_lex;
 
37
  Select_Lex_Unit *unit= &session->lex->unit;
85
38
  bool need_start_waiting= false;
86
 
  lex_identified_temp_table= createTableMessage().type() == message::Table::TEMPORARY;
87
 
 
88
 
  is_engine_set= not createTableMessage().engine().name().empty();
 
39
  bool res= false;
 
40
  bool link_to_local= false;
 
41
  bool lex_identified_temp_table= 
 
42
    create_table_proto.type() == message::Table::TEMPORARY;
89
43
 
90
44
  if (is_engine_set)
91
45
  {
92
 
    create_info().db_type= 
93
 
      plugin::StorageEngine::findByName(*getSession(), createTableMessage().engine().name());
 
46
    create_info.db_type= 
 
47
      plugin::StorageEngine::findByName(*session, create_table_proto.engine().name());
94
48
 
95
 
    if (create_info().db_type == NULL)
 
49
    if (create_info.db_type == NULL)
96
50
    {
97
51
      my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), 
98
 
               createTableMessage().engine().name().c_str());
 
52
               create_table_proto.name().c_str());
99
53
 
100
54
      return true;
101
55
    }
102
56
  }
103
57
  else /* We now get the default, place it in create_info, and put the engine name in table proto */
104
58
  {
105
 
    create_info().db_type= getSession()->getDefaultStorageEngine();
106
 
  }
107
 
 
108
 
  if (not validateCreateTableOption())
109
 
  {
110
 
    return true;
111
 
  }
112
 
 
113
 
  if (not lex_identified_temp_table)
114
 
  {
115
 
    if (getSession()->inTransaction())
 
59
    create_info.db_type= session->getDefaultStorageEngine();
 
60
  }
 
61
 
 
62
 
 
63
  /* 
 
64
    Now we set the name in our Table proto so that it will match 
 
65
    create_info.db_type.
 
66
  */
 
67
  {
 
68
    message::Table::StorageEngine *protoengine;
 
69
 
 
70
    protoengine= create_table_proto.mutable_engine();
 
71
    protoengine->set_name(create_info.db_type->getName());
 
72
  }
 
73
 
 
74
 
 
75
  /* If CREATE TABLE of non-temporary table, do implicit commit */
 
76
  if (! lex_identified_temp_table)
 
77
  {
 
78
    if (! session->endActiveTransaction())
116
79
    {
117
 
      my_error(ER_TRANSACTIONAL_DDL_NOT_SUPPORTED, MYF(0));
118
80
      return true;
119
81
    }
120
82
  }
121
83
  /* Skip first table, which is the table we are creating */
122
 
  create_table_list= getSession()->lex->unlink_first_table(&link_to_local);
123
 
 
124
 
  drizzled::message::table::init(createTableMessage(), createTableMessage().name(), create_table_list->getSchemaName(), create_info().db_type->getName());
125
 
 
126
 
  identifier::Table new_table_identifier(create_table_list->getSchemaName(),
127
 
                                       create_table_list->getTableName(),
128
 
                                       createTableMessage().type());
129
 
 
130
 
  if (not check(new_table_identifier))
 
84
  TableList *create_table= session->lex->unlink_first_table(&link_to_local);
 
85
  TableList *select_tables= session->lex->query_tables;
 
86
 
 
87
 
 
88
  /*
 
89
    Now that we have the engine, we can figure out the table identifier. We need the engine in order
 
90
    to determine if the table is transactional or not if it is temp.
 
91
  */
 
92
  TableIdentifier new_table_identifier(create_table->db,
 
93
                                       create_table->table_name,
 
94
                                       create_table_proto.type() != message::Table::TEMPORARY ? NO_TMP_TABLE : TEMP_TABLE);
 
95
 
 
96
  if (create_table_precheck(new_table_identifier))
131
97
  {
132
98
    /* put tables back for PS rexecuting */
133
 
    getSession()->lex->link_first_table_back(create_table_list, link_to_local);
 
99
    session->lex->link_first_table_back(create_table, link_to_local);
134
100
    return true;
135
101
  }
136
102
 
137
103
  /* Might have been updated in create_table_precheck */
138
 
  create_info().alias= create_table_list->alias;
 
104
  create_info.alias= create_table->alias;
139
105
 
140
106
  /*
141
107
     The create-select command will open and read-lock the select table
150
116
     TABLE in the same way. That way we avoid that a new table is
151
117
     created during a gobal read lock.
152
118
   */
153
 
  if (! (need_start_waiting= not getSession()->wait_if_global_read_lock(0, 1)))
 
119
  if (! (need_start_waiting= ! wait_if_global_read_lock(session, 0, 1)))
154
120
  {
155
121
    /* put tables back for PS rexecuting */
156
 
    getSession()->lex->link_first_table_back(create_table_list, link_to_local);
 
122
    session->lex->link_first_table_back(create_table, link_to_local);
157
123
    return true;
158
124
  }
159
125
 
160
 
  bool res= executeInner(new_table_identifier);
161
 
 
162
 
  /*
163
 
    Release the protection against the global read lock and wake
164
 
    everyone, who might want to set a global read lock.
165
 
  */
166
 
  getSession()->startWaitingGlobalReadLock();
167
 
 
168
 
  return res;
169
 
}
170
 
 
171
 
bool statement::CreateTable::executeInner(identifier::Table::const_reference new_table_identifier)
172
 
{
173
 
  bool res= false;
174
 
  Select_Lex *select_lex= &getSession()->lex->select_lex;
175
 
  TableList *select_tables= getSession()->lex->query_tables;
176
 
 
177
 
  do 
 
126
  if (select_lex->item_list.elements)           // With select
178
127
  {
179
 
    if (select_lex->item_list.elements)         // With select
180
 
    {
181
 
      Select_Lex_Unit *unit= &getSession()->lex->unit;
182
 
      select_result *result;
183
 
 
184
 
      select_lex->options|= SELECT_NO_UNLOCK;
185
 
      unit->set_limit(select_lex);
186
 
 
187
 
      if (not lex_identified_temp_table)
188
 
      {
189
 
        getSession()->lex->link_first_table_back(create_table_list, link_to_local);
190
 
        create_table_list->setCreate(true);
191
 
      }
192
 
 
193
 
      if (not (res= getSession()->openTablesLock(getSession()->lex->query_tables)))
194
 
      {
195
 
        /*
196
 
          Is table which we are changing used somewhere in other parts
197
 
          of query
198
 
        */
199
 
        if (not lex_identified_temp_table)
200
 
        {
201
 
          TableList *duplicate= NULL;
202
 
          create_table_list= getSession()->lex->unlink_first_table(&link_to_local);
203
 
 
204
 
          if ((duplicate= unique_table(create_table_list, select_tables)))
205
 
          {
206
 
            my_error(ER_UPDATE_TABLE_USED, MYF(0), create_table_list->alias);
207
 
            /* put tables back for PS rexecuting */
208
 
            getSession()->lex->link_first_table_back(create_table_list, link_to_local);
209
 
 
210
 
            res= true;
211
 
            break;
212
 
          }
213
 
        }
214
 
 
215
 
        /*
216
 
          select_create is currently not re-execution friendly and
217
 
          needs to be created for every execution of a PS/SP.
218
 
        */
219
 
        if ((result= new select_create(create_table_list,
220
 
                                       getSession()->getLex()->exists(),
221
 
                                       &create_info(),
222
 
                                       createTableMessage(),
223
 
                                       &alter_info,
224
 
                                       select_lex->item_list,
225
 
                                       getSession()->lex->duplicates,
226
 
                                       getSession()->lex->ignore,
227
 
                                       select_tables,
228
 
                                       new_table_identifier)))
229
 
        {
 
128
    select_result *result;
 
129
 
 
130
    select_lex->options|= SELECT_NO_UNLOCK;
 
131
    unit->set_limit(select_lex);
 
132
 
 
133
    if (! lex_identified_temp_table)
 
134
    {
 
135
      session->lex->link_first_table_back(create_table, link_to_local);
 
136
      create_table->create= true;
 
137
    }
 
138
 
 
139
    if (! (res= session->openTablesLock(session->lex->query_tables)))
 
140
    {
 
141
      /*
 
142
         Is table which we are changing used somewhere in other parts
 
143
         of query
 
144
       */
 
145
      if (! lex_identified_temp_table)
 
146
      {
 
147
        TableList *duplicate= NULL;
 
148
        create_table= session->lex->unlink_first_table(&link_to_local);
 
149
        if ((duplicate= unique_table(create_table, select_tables)))
 
150
        {
 
151
          my_error(ER_UPDATE_TABLE_USED, MYF(0), create_table->alias);
230
152
          /*
231
 
            CREATE from SELECT give its Select_Lex for SELECT,
232
 
            and item_list belong to SELECT
233
 
          */
234
 
          res= handle_select(getSession(), getSession()->lex, result, 0);
235
 
          delete result;
 
153
             Release the protection against the global read lock and wake
 
154
             everyone, who might want to set a global read lock.
 
155
           */
 
156
          start_waiting_global_read_lock(session);
 
157
          /* put tables back for PS rexecuting */
 
158
          session->lex->link_first_table_back(create_table, link_to_local);
 
159
          return true;
236
160
        }
237
161
      }
238
 
      else if (not lex_identified_temp_table)
 
162
 
 
163
      /*
 
164
         select_create is currently not re-execution friendly and
 
165
         needs to be created for every execution of a PS/SP.
 
166
       */
 
167
      if ((result= new select_create(create_table,
 
168
                                     is_if_not_exists,
 
169
                                     &create_info,
 
170
                                     &create_table_proto,
 
171
                                     &alter_info,
 
172
                                     select_lex->item_list,
 
173
                                     session->lex->duplicates,
 
174
                                     session->lex->ignore,
 
175
                                     select_tables)))
239
176
      {
240
 
        create_table_list= getSession()->lex->unlink_first_table(&link_to_local);
 
177
        /*
 
178
           CREATE from SELECT give its Select_Lex for SELECT,
 
179
           and item_list belong to SELECT
 
180
         */
 
181
        res= handle_select(session, session->lex, result, 0);
 
182
        delete result;
241
183
      }
242
184
    }
 
185
    else if (! lex_identified_temp_table)
 
186
    {
 
187
      create_table= session->lex->unlink_first_table(&link_to_local);
 
188
    }
 
189
  }
 
190
  else
 
191
  {
 
192
    /* regular create */
 
193
    if (is_create_table_like)
 
194
    {
 
195
      res= mysql_create_like_table(session, 
 
196
                                   create_table, 
 
197
                                   select_tables,
 
198
                                   create_table_proto,
 
199
                                   create_info.db_type, 
 
200
                                   is_if_not_exists,
 
201
                                   is_engine_set);
 
202
    }
243
203
    else
244
204
    {
245
 
      /* regular create */
246
 
      if (is_create_table_like)
247
 
      {
248
 
        res= create_like_table(getSession(), 
249
 
                               new_table_identifier,
250
 
                               identifier::Table(select_tables->getSchemaName(),
251
 
                                                 select_tables->getTableName()),
252
 
                               createTableMessage(),
253
 
                               getSession()->getLex()->exists(),
254
 
                               is_engine_set);
255
 
      }
256
 
      else
257
 
      {
258
 
 
259
 
        for (int32_t x= 0; x < alter_info.alter_proto.added_field_size(); x++)
260
 
        {
261
 
          message::Table::Field *field= createTableMessage().add_field();
262
 
 
263
 
          *field= alter_info.alter_proto.added_field(x);
264
 
        }
265
 
 
266
 
        res= create_table(getSession(), 
267
 
                          new_table_identifier,
268
 
                          &create_info(),
269
 
                          createTableMessage(),
270
 
                          &alter_info, 
271
 
                          false, 
272
 
                          0,
273
 
                          getSession()->getLex()->exists());
274
 
      }
275
 
 
276
 
      if (not res)
277
 
      {
278
 
        getSession()->my_ok();
279
 
      }
280
 
    }
281
 
  } while (0);
 
205
 
 
206
      for (int32_t x= 0; x < alter_info.alter_proto.added_field_size(); x++)
 
207
      {
 
208
        message::Table::Field *field= create_table_proto.add_field();
 
209
 
 
210
        *field= alter_info.alter_proto.added_field(x);
 
211
      }
 
212
 
 
213
      res= mysql_create_table(session, 
 
214
                              new_table_identifier,
 
215
                              &create_info,
 
216
                              &create_table_proto,
 
217
                              &alter_info, 
 
218
                              false, 
 
219
                              0,
 
220
                              is_if_not_exists);
 
221
    }
 
222
    if (! res)
 
223
    {
 
224
      session->my_ok();
 
225
    }
 
226
  }
 
227
 
 
228
  /*
 
229
     Release the protection against the global read lock and wake
 
230
     everyone, who might want to set a global read lock.
 
231
   */
 
232
  start_waiting_global_read_lock(session);
282
233
 
283
234
  return res;
284
235
}
285
236
 
286
 
bool statement::CreateTable::check(const identifier::Table &identifier)
287
 
{
288
 
  // Check table name for validity
289
 
  if (not identifier.isValid())
290
 
    return false;
291
 
 
292
 
  // See if any storage engine objects to the name of the file
293
 
  if (not plugin::StorageEngine::canCreateTable(identifier))
294
 
  {
295
 
    my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), "", "", identifier.getSchemaName().c_str());
296
 
 
297
 
    return false;
298
 
  }
299
 
 
300
 
  // Make sure the schema exists, we will do this again during the actual
301
 
  // create for the table.
302
 
  if (not plugin::StorageEngine::doesSchemaExist(identifier))
303
 
  {
304
 
    my_error(ER_BAD_DB_ERROR, MYF(0), identifier.getSchemaName().c_str());
305
 
 
306
 
    return false;
307
 
  }
308
 
 
309
 
  return true;
310
 
}
311
 
 
312
 
bool statement::CreateTable::validateCreateTableOption()
313
 
{
314
 
  bool rc= true;
315
 
  size_t num_engine_options= createTableMessage().engine().options_size();
316
 
 
317
 
  assert(create_info().db_type);
318
 
 
319
 
  for (size_t y= 0; y < num_engine_options; ++y)
320
 
  {
321
 
    bool valid= create_info().db_type->validateCreateTableOption(createTableMessage().engine().options(y).name(),
322
 
                                                                 createTableMessage().engine().options(y).state());
323
 
 
324
 
    if (not valid)
325
 
    {
326
 
      my_error(ER_UNKNOWN_ENGINE_OPTION, MYF(0),
327
 
               createTableMessage().engine().options(y).name().c_str(),
328
 
               createTableMessage().engine().options(y).state().c_str());
329
 
 
330
 
      rc= false;
331
 
    }
332
 
  }
333
 
 
334
 
  return rc;
335
 
}
336
 
 
337
237
} /* namespace drizzled */
338
238