Drizzled Public API Documentation

create_table.cc

00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2009 Sun Microsystems, Inc.
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019  */
00020 
00021 #include <config.h>
00022 
00023 #include <drizzled/show.h>
00024 #include <drizzled/lock.h>
00025 #include <drizzled/session.h>
00026 #include <drizzled/statement/create_table.h>
00027 #include <drizzled/message.h>
00028 #include <drizzled/identifier.h>
00029 #include <drizzled/plugin/storage_engine.h>
00030 #include <drizzled/select_create.h>
00031 
00032 #include <iostream>
00033 
00034 namespace drizzled
00035 {
00036 
00037 namespace statement {
00038 
00039 CreateTable::CreateTable(Session *in_session, Table_ident *ident, bool is_temporary) :
00040   Statement(in_session),
00041   change(NULL),
00042   default_value(NULL),
00043   on_update_value(NULL),
00044   is_engine_set(false),
00045   is_create_table_like(false),
00046   lex_identified_temp_table(false),
00047   link_to_local(false),
00048   create_table_list(NULL)
00049 {
00050   set_command(SQLCOM_CREATE_TABLE);
00051   createTableMessage().set_name(ident->table.str, ident->table.length);
00052 #if 0
00053   createTableMessage().set_schema(ident->db.str, ident->db.length);
00054 #endif
00055 
00056   if (is_temporary)
00057   {
00058     createTableMessage().set_type(message::Table::TEMPORARY);
00059   }
00060   else
00061   {
00062     createTableMessage().set_type(message::Table::STANDARD);
00063   }
00064 }
00065 
00066 CreateTable::CreateTable(Session *in_session) :
00067   Statement(in_session),
00068   change(NULL),
00069   default_value(NULL),
00070   on_update_value(NULL),
00071   is_engine_set(false),
00072   is_create_table_like(false),
00073   lex_identified_temp_table(false),
00074   link_to_local(false),
00075   create_table_list(NULL)
00076 {
00077   set_command(SQLCOM_CREATE_TABLE);
00078 }
00079 
00080 } // namespace statement
00081 
00082 bool statement::CreateTable::execute()
00083 {
00084   TableList *first_table= (TableList *) lex().select_lex.table_list.first;
00085   TableList *all_tables= lex().query_tables;
00086   assert(first_table == all_tables && first_table != 0);
00087   bool need_start_waiting= false;
00088   lex_identified_temp_table= createTableMessage().type() == message::Table::TEMPORARY;
00089 
00090   is_engine_set= not createTableMessage().engine().name().empty();
00091 
00092   if (is_engine_set)
00093   {
00094     create_info().db_type= 
00095       plugin::StorageEngine::findByName(session(), createTableMessage().engine().name());
00096 
00097     if (create_info().db_type == NULL)
00098     {
00099       my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), 
00100                createTableMessage().engine().name().c_str());
00101 
00102       return true;
00103     }
00104   }
00105   else /* We now get the default, place it in create_info, and put the engine name in table proto */
00106   {
00107     create_info().db_type= session().getDefaultStorageEngine();
00108   }
00109 
00110   if (not validateCreateTableOption())
00111   {
00112     return true;
00113   }
00114 
00115   if (not lex_identified_temp_table)
00116   {
00117     if (session().inTransaction())
00118     {
00119       my_error(ER_TRANSACTIONAL_DDL_NOT_SUPPORTED, MYF(0));
00120       return true;
00121     }
00122   }
00123   /* Skip first table, which is the table we are creating */
00124   create_table_list= lex().unlink_first_table(&link_to_local);
00125 
00126   drizzled::message::table::init(createTableMessage(), createTableMessage().name(), create_table_list->getSchemaName(), create_info().db_type->getName());
00127 
00128   identifier::Table new_table_identifier(create_table_list->getSchemaName(),
00129                                        create_table_list->getTableName(),
00130                                        createTableMessage().type());
00131 
00132   if (not check(new_table_identifier))
00133   {
00134     /* put tables back for PS rexecuting */
00135     lex().link_first_table_back(create_table_list, link_to_local);
00136     return true;
00137   }
00138 
00139   /* Might have been updated in create_table_precheck */
00140   create_info().alias= create_table_list->alias;
00141 
00142   /*
00143      The create-select command will open and read-lock the select table
00144      and then create, open and write-lock the new table. If a global
00145      read lock steps in, we get a deadlock. The write lock waits for
00146      the global read lock, while the global read lock waits for the
00147      select table to be closed. So we wait until the global readlock is
00148      gone before starting both steps. Note that
00149      wait_if_global_read_lock() sets a protection against a new global
00150      read lock when it succeeds. This needs to be released by
00151      start_waiting_global_read_lock(). We protect the normal CREATE
00152      TABLE in the same way. That way we avoid that a new table is
00153      created during a gobal read lock.
00154    */
00155   if (! (need_start_waiting= not session().wait_if_global_read_lock(0, 1)))
00156   {
00157     /* put tables back for PS rexecuting */
00158     lex().link_first_table_back(create_table_list, link_to_local);
00159     return true;
00160   }
00161 
00162   bool res= executeInner(new_table_identifier);
00163 
00164   /*
00165     Release the protection against the global read lock and wake
00166     everyone, who might want to set a global read lock.
00167   */
00168   session().startWaitingGlobalReadLock();
00169 
00170   return res;
00171 }
00172 
00173 bool statement::CreateTable::executeInner(identifier::Table::const_reference new_table_identifier)
00174 {
00175   bool res= false;
00176   Select_Lex *select_lex= &lex().select_lex;
00177   TableList *select_tables= lex().query_tables;
00178 
00179   do 
00180   {
00181     if (select_lex->item_list.size())   // With select
00182     {
00183       Select_Lex_Unit *unit= &lex().unit;
00184       select_result *result;
00185 
00186       select_lex->options|= SELECT_NO_UNLOCK;
00187       unit->set_limit(select_lex);
00188 
00189       if (not lex_identified_temp_table)
00190       {
00191         lex().link_first_table_back(create_table_list, link_to_local);
00192         create_table_list->setCreate(true);
00193       }
00194 
00195       if (not (res= session().openTablesLock(lex().query_tables)))
00196       {
00197         /*
00198           Is table which we are changing used somewhere in other parts
00199           of query
00200         */
00201         if (not lex_identified_temp_table)
00202         {
00203           TableList *duplicate= NULL;
00204           create_table_list= lex().unlink_first_table(&link_to_local);
00205 
00206           if ((duplicate= unique_table(create_table_list, select_tables)))
00207           {
00208             my_error(ER_UPDATE_TABLE_USED, MYF(0), create_table_list->alias);
00209             /* put tables back for PS rexecuting */
00210             lex().link_first_table_back(create_table_list, link_to_local);
00211 
00212             res= true;
00213             break;
00214           }
00215         }
00216 
00217         /*
00218           select_create is currently not re-execution friendly and
00219           needs to be created for every execution of a PS/SP.
00220         */
00221         if ((result= new select_create(create_table_list,
00222                                        lex().exists(),
00223                                        &create_info(),
00224                                        createTableMessage(),
00225                                        &alter_info,
00226                                        select_lex->item_list,
00227                                        lex().duplicates,
00228                                        lex().ignore,
00229                                        select_tables,
00230                                        new_table_identifier)))
00231         {
00232           /*
00233             CREATE from SELECT give its Select_Lex for SELECT,
00234             and item_list belong to SELECT
00235           */
00236           res= handle_select(&session(), &lex(), result, 0);
00237           delete result;
00238         }
00239       }
00240       else if (not lex_identified_temp_table)
00241       {
00242         create_table_list= lex().unlink_first_table(&link_to_local);
00243       }
00244     }
00245     else
00246     {
00247       /* regular create */
00248       if (is_create_table_like)
00249       {
00250         res= create_like_table(&session(), 
00251                                new_table_identifier,
00252                                identifier::Table(select_tables->getSchemaName(),
00253                                                  select_tables->getTableName()),
00254                                createTableMessage(),
00255                                lex().exists(),
00256                                is_engine_set);
00257       }
00258       else
00259       {
00260 
00261         for (int32_t x= 0; x < alter_info.alter_proto.added_field_size(); x++)
00262         {
00263           message::Table::Field *field= createTableMessage().add_field();
00264 
00265           *field= alter_info.alter_proto.added_field(x);
00266         }
00267 
00268         res= create_table(&session(), 
00269                           new_table_identifier,
00270                           &create_info(),
00271                           createTableMessage(),
00272                           &alter_info, 
00273                           false, 
00274                           0,
00275                           lex().exists());
00276       }
00277 
00278       if (not res)
00279       {
00280         session().my_ok();
00281       }
00282     }
00283   } while (0);
00284 
00285   return res;
00286 }
00287 
00288 bool statement::CreateTable::check(const identifier::Table &identifier)
00289 {
00290   // Check table name for validity
00291   if (not identifier.isValid())
00292     return false;
00293 
00294   // See if any storage engine objects to the name of the file
00295   if (not plugin::StorageEngine::canCreateTable(identifier))
00296   {
00297     identifier::Schema schema_identifier= identifier;
00298     error::access(*session().user(), schema_identifier);
00299 
00300     return false;
00301   }
00302 
00303   // Make sure the schema exists, we will do this again during the actual
00304   // create for the table.
00305   if (not plugin::StorageEngine::doesSchemaExist(identifier))
00306   {
00307     identifier::Schema schema_identifier= identifier;
00308     my_error(ER_BAD_DB_ERROR, schema_identifier);
00309 
00310     return false;
00311   }
00312 
00313   return true;
00314 }
00315 
00316 bool statement::CreateTable::validateCreateTableOption()
00317 {
00318   bool rc= true;
00319   size_t num_engine_options= createTableMessage().engine().options_size();
00320 
00321   assert(create_info().db_type);
00322 
00323   for (size_t y= 0; y < num_engine_options; ++y)
00324   {
00325     bool valid= create_info().db_type->validateCreateTableOption(createTableMessage().engine().options(y).name(),
00326                                                                  createTableMessage().engine().options(y).state());
00327 
00328     if (not valid)
00329     {
00330       my_error(ER_UNKNOWN_ENGINE_OPTION, MYF(0),
00331                createTableMessage().engine().options(y).name().c_str(),
00332                createTableMessage().engine().options(y).state().c_str());
00333 
00334       rc= false;
00335     }
00336   }
00337 
00338   return rc;
00339 }
00340 
00341 } /* namespace drizzled */
00342