Drizzled Public API Documentation

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