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