~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/statement/create_table.cc

  • Committer: Jay Pipes
  • Date: 2009-09-15 21:01:42 UTC
  • mto: (1126.2.5 merge)
  • mto: This revision was merged to the branch mainline in revision 1128.
  • Revision ID: jpipes@serialcoder-20090915210142-x8mwiqn1q0vzjspp
Moves Alter_info out into its own header and source file, cleans up some related include mess in sql_lex.h, and renames Alter_info to AlterInfo.

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
18
18
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
19
 */
20
20
 
21
 
#include "config.h"
 
21
#include <drizzled/server_includes.h>
22
22
#include <drizzled/show.h>
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>
31
 
 
32
 
namespace drizzled
33
 
{
34
 
 
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
 
26
 
 
27
using namespace drizzled;
79
28
 
80
29
bool statement::CreateTable::execute()
81
30
{
82
 
  TableList *first_table= (TableList *) getSession()->lex->select_lex.table_list.first;
83
 
  TableList *all_tables= getSession()->lex->query_tables;
 
31
  TableList *first_table= (TableList *) session->lex->select_lex.table_list.first;
 
32
  TableList *all_tables= session->lex->query_tables;
84
33
  assert(first_table == all_tables && first_table != 0);
 
34
  Select_Lex *select_lex= &session->lex->select_lex;
 
35
  Select_Lex_Unit *unit= &session->lex->unit;
85
36
  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();
89
 
 
90
 
  if (is_engine_set)
91
 
  {
92
 
    create_info().db_type= 
93
 
      plugin::StorageEngine::findByName(*getSession(), createTableMessage().engine().name());
94
 
 
95
 
    if (create_info().db_type == NULL)
96
 
    {
97
 
      my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), 
98
 
               createTableMessage().engine().name().c_str());
99
 
 
100
 
      return true;
101
 
    }
102
 
  }
103
 
  else /* We now get the default, place it in create_info, and put the engine name in table proto */
104
 
  {
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())
116
 
    {
117
 
      my_error(ER_TRANSACTIONAL_DDL_NOT_SUPPORTED, MYF(0));
 
37
  bool res= false;
 
38
  bool link_to_local= false;
 
39
 
 
40
  /* If CREATE TABLE of non-temporary table, do implicit commit */
 
41
  if (! (session->lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
 
42
  {
 
43
    if (! session->endActiveTransaction())
 
44
    {
118
45
      return true;
119
46
    }
120
47
  }
121
48
  /* 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))
131
 
  {
132
 
    /* put tables back for PS rexecuting */
133
 
    getSession()->lex->link_first_table_back(create_table_list, link_to_local);
 
49
  TableList *create_table= session->lex->unlink_first_table(&link_to_local);
 
50
  TableList *select_tables= session->lex->query_tables;
 
51
  /*
 
52
     Code below (especially in mysql_create_table() and select_create
 
53
     methods) may modify HA_CREATE_INFO structure in LEX, so we have to
 
54
     use a copy of this structure to make execution prepared statement-
 
55
     safe. A shallow copy is enough as this code won't modify any memory
 
56
     referenced from this structure.
 
57
   */
 
58
  HA_CREATE_INFO create_info(session->lex->create_info);
 
59
  /*
 
60
     We need to copy alter_info for the same reasons of re-execution
 
61
     safety, only in case of AlterInfo we have to do (almost) a deep
 
62
     copy.
 
63
   */
 
64
  AlterInfo alter_info(session->lex->alter_info, session->mem_root);
 
65
 
 
66
  if (session->is_fatal_error)
 
67
  {
 
68
    /* If out of memory when creating a copy of alter_info. */
 
69
    /* put tables back for PS rexecuting */
 
70
    session->lex->link_first_table_back(create_table, link_to_local);
 
71
    return true;
 
72
  }
 
73
 
 
74
  if (create_table_precheck(session, select_tables, create_table))
 
75
  {
 
76
    /* put tables back for PS rexecuting */
 
77
    session->lex->link_first_table_back(create_table, link_to_local);
134
78
    return true;
135
79
  }
136
80
 
137
81
  /* Might have been updated in create_table_precheck */
138
 
  create_info().alias= create_table_list->alias;
 
82
  create_info.alias= create_table->alias;
139
83
 
140
84
  /*
141
85
     The create-select command will open and read-lock the select table
150
94
     TABLE in the same way. That way we avoid that a new table is
151
95
     created during a gobal read lock.
152
96
   */
153
 
  if (! (need_start_waiting= not getSession()->wait_if_global_read_lock(0, 1)))
 
97
  if (! (need_start_waiting= ! wait_if_global_read_lock(session, 0, 1)))
154
98
  {
155
99
    /* put tables back for PS rexecuting */
156
 
    getSession()->lex->link_first_table_back(create_table_list, link_to_local);
 
100
    session->lex->link_first_table_back(create_table, link_to_local);
157
101
    return true;
158
102
  }
159
 
 
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 
 
103
  if (select_lex->item_list.elements)           // With select
178
104
  {
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
 
        {
 
105
    select_result *result;
 
106
 
 
107
    select_lex->options|= SELECT_NO_UNLOCK;
 
108
    unit->set_limit(select_lex);
 
109
 
 
110
    if (! (create_info.options & HA_LEX_CREATE_TMP_TABLE))
 
111
    {
 
112
      session->lex->link_first_table_back(create_table, link_to_local);
 
113
      create_table->create= true;
 
114
    }
 
115
 
 
116
    if (! (res= session->openTablesLock(session->lex->query_tables)))
 
117
    {
 
118
      /*
 
119
         Is table which we are changing used somewhere in other parts
 
120
         of query
 
121
       */
 
122
      if (! (create_info.options & HA_LEX_CREATE_TMP_TABLE))
 
123
      {
 
124
        TableList *duplicate= NULL;
 
125
        create_table= session->lex->unlink_first_table(&link_to_local);
 
126
        if ((duplicate= unique_table(session, create_table, select_tables, 0)))
 
127
        {
 
128
          my_error(ER_UPDATE_TABLE_USED, MYF(0), create_table->alias);
230
129
          /*
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;
 
130
             Release the protection against the global read lock and wake
 
131
             everyone, who might want to set a global read lock.
 
132
           */
 
133
          start_waiting_global_read_lock(session);
 
134
          /* put tables back for PS rexecuting */
 
135
          session->lex->link_first_table_back(create_table, link_to_local);
 
136
          return true;
236
137
        }
237
138
      }
238
 
      else if (not lex_identified_temp_table)
 
139
 
 
140
      /*
 
141
         select_create is currently not re-execution friendly and
 
142
         needs to be created for every execution of a PS/SP.
 
143
       */
 
144
      if ((result= new select_create(create_table,
 
145
                                     &create_info,
 
146
                                     session->lex->create_table_proto,
 
147
                                     &alter_info,
 
148
                                     select_lex->item_list,
 
149
                                     session->lex->duplicates,
 
150
                                     session->lex->ignore,
 
151
                                     select_tables)))
239
152
      {
240
 
        create_table_list= getSession()->lex->unlink_first_table(&link_to_local);
 
153
        /*
 
154
           CREATE from SELECT give its Select_Lex for SELECT,
 
155
           and item_list belong to SELECT
 
156
         */
 
157
        res= handle_select(session, session->lex, result, 0);
 
158
        delete result;
241
159
      }
242
160
    }
 
161
    else if (! (create_info.options & HA_LEX_CREATE_TMP_TABLE))
 
162
    {
 
163
      create_table= session->lex->unlink_first_table(&link_to_local);
 
164
    }
 
165
  }
 
166
  else
 
167
  {
 
168
    /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
 
169
    if (create_info.options & HA_LEX_CREATE_TMP_TABLE)
 
170
      session->options|= OPTION_KEEP_LOG;
 
171
    /* regular create */
 
172
    if (create_info.options & HA_LEX_CREATE_TABLE_LIKE)
 
173
    {
 
174
      res= mysql_create_like_table(session, 
 
175
                                   create_table, 
 
176
                                   select_tables,
 
177
                                   &create_info);
 
178
    }
243
179
    else
244
180
    {
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);
 
181
      res= mysql_create_table(session, 
 
182
                              create_table->db,
 
183
                              create_table->table_name, 
 
184
                              &create_info,
 
185
                              session->lex->create_table_proto,
 
186
                              &alter_info, 
 
187
                              0, 
 
188
                              0);
 
189
    }
 
190
    if (! res)
 
191
    {
 
192
      session->my_ok();
 
193
    }
 
194
  }
 
195
 
 
196
  /*
 
197
     Release the protection against the global read lock and wake
 
198
     everyone, who might want to set a global read lock.
 
199
   */
 
200
  start_waiting_global_read_lock(session);
282
201
 
283
202
  return res;
284
203
}
285
 
 
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
 
} /* namespace drizzled */
338