Drizzled Public API Documentation

schema.cc

00001 /* Copyright (C) 2000-2003 MySQL AB
00002 
00003    This program is free software; you can redistribute it and/or modify
00004    it under the terms of the GNU General Public License as published by
00005    the Free Software Foundation; version 2 of the License.
00006 
00007    This program is distributed in the hope that it will be useful,
00008    but WITHOUT ANY WARRANTY; without even the implied warranty of
00009    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010    GNU General Public License for more details.
00011 
00012    You should have received a copy of the GNU General Public License
00013    along with this program; if not, write to the Free Software
00014    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00016 
00017 /* create and drop of databases */
00018 #include <config.h>
00019 
00020 #include <fcntl.h>
00021 #include <sys/stat.h>
00022 #include <sys/types.h>
00023 
00024 #include <set>
00025 #include <string>
00026 #include <fstream>
00027 
00028 #include <drizzled/error.h>
00029 #include <drizzled/gettext.h>
00030 #include <drizzled/internal/m_string.h>
00031 #include <drizzled/session.h>
00032 #include <drizzled/schema.h>
00033 #include <drizzled/sql_base.h>
00034 #include <drizzled/lock.h>
00035 #include <drizzled/errmsg_print.h>
00036 #include <drizzled/transaction_services.h>
00037 #include <drizzled/message/schema.pb.h>
00038 #include <drizzled/sql_table.h>
00039 #include <drizzled/plugin/storage_engine.h>
00040 #include <drizzled/plugin/authorization.h>
00041 #include <drizzled/global_charset_info.h>
00042 #include <drizzled/pthread_globals.h>
00043 #include <drizzled/charset.h>
00044 #include <drizzled/internal/my_sys.h>
00045 
00046 #include <boost/thread/mutex.hpp>
00047 
00048 #define MAX_DROP_TABLE_Q_LEN      1024
00049 
00050 using namespace std;
00051 
00052 namespace drizzled
00053 {
00054 
00055 namespace schema
00056 {
00057 
00058 static void change_db_impl(Session &session);
00059 static void change_db_impl(Session &session, identifier::Schema &schema_identifier);
00060 
00061 /*
00062   Create a database
00063 
00064   SYNOPSIS
00065   create_db()
00066   session   Thread handler
00067   db    Name of database to create
00068     Function assumes that this is already validated.
00069   create_info Database create options (like character set)
00070 
00071   SIDE-EFFECTS
00072    1. Report back to client that command succeeded (my_ok)
00073    2. Report errors to client
00074    3. Log event to binary log
00075 
00076   RETURN VALUES
00077   false ok
00078   true  Error
00079 
00080 */
00081 
00082 bool create(Session &session, const message::Schema &schema_message, const bool is_if_not_exists)
00083 {
00084   TransactionServices &transaction_services= TransactionServices::singleton();
00085   bool error= false;
00086 
00087   /*
00088     Do not create database if another thread is holding read lock.
00089     Wait for global read lock before acquiring session->catalog()->schemaLock().
00090     After wait_if_global_read_lock() we have protection against another
00091     global read lock. If we would acquire session->catalog()->schemaLock() first,
00092     another thread could step in and get the global read lock before we
00093     reach wait_if_global_read_lock(). If this thread tries the same as we
00094     (admin a db), it would then go and wait on session->catalog()->schemaLock()...
00095     Furthermore wait_if_global_read_lock() checks if the current thread
00096     has the global read lock and refuses the operation with
00097     ER_CANT_UPDATE_WITH_READLOCK if applicable.
00098   */
00099   if (session.wait_if_global_read_lock(false, true))
00100   {
00101     return false;
00102   }
00103 
00104   assert(schema_message.has_name());
00105   assert(schema_message.has_collation());
00106 
00107   // @todo push this lock down into the engine
00108   {
00109     boost::mutex::scoped_lock scopedLock(session.catalog().schemaLock());
00110 
00111     // Check to see if it exists already.  
00112     identifier::Schema schema_identifier(schema_message.name());
00113     if (plugin::StorageEngine::doesSchemaExist(schema_identifier))
00114     {
00115       if (not is_if_not_exists)
00116       {
00117         my_error(ER_DB_CREATE_EXISTS, schema_identifier);
00118         error= true;
00119       }
00120       else
00121       {
00122         push_warning_printf(&session, DRIZZLE_ERROR::WARN_LEVEL_NOTE,
00123                             ER_DB_CREATE_EXISTS, ER(ER_DB_CREATE_EXISTS),
00124                             schema_message.name().c_str());
00125         session.my_ok();
00126       }
00127     }
00128     else if (not plugin::StorageEngine::createSchema(schema_message)) // Try to create it 
00129     {
00130       my_error(ER_CANT_CREATE_DB, MYF(0), schema_message.name().c_str(), errno);
00131       error= true;
00132     }
00133     else // Created !
00134     {
00135       transaction_services.createSchema(session, schema_message);
00136       session.my_ok(1);
00137     }
00138   }
00139   session.startWaitingGlobalReadLock();
00140 
00141   return error;
00142 }
00143 
00144 
00145 /* db-name is already validated when we come here */
00146 
00147 bool alter(Session &session,
00148            const message::Schema &schema_message,
00149            const message::Schema &original_schema)
00150 {
00151   TransactionServices &transaction_services= TransactionServices::singleton();
00152 
00153   /*
00154     Do not alter database if another thread is holding read lock.
00155     Wait for global read lock before acquiring session->catalog()->schemaLock().
00156     After wait_if_global_read_lock() we have protection against another
00157     global read lock. If we would acquire session->catalog()->schemaLock() first,
00158     another thread could step in and get the global read lock before we
00159     reach wait_if_global_read_lock(). If this thread tries the same as we
00160     (admin a db), it would then go and wait on session->catalog()->schemaLock()...
00161     Furthermore wait_if_global_read_lock() checks if the current thread
00162     has the global read lock and refuses the operation with
00163     ER_CANT_UPDATE_WITH_READLOCK if applicable.
00164   */
00165   if ((session.wait_if_global_read_lock(false, true)))
00166     return false;
00167 
00168   bool success;
00169   {
00170     boost::mutex::scoped_lock scopedLock(session.catalog().schemaLock());
00171 
00172     identifier::Schema schema_idenifier(schema_message.name());
00173     if (not plugin::StorageEngine::doesSchemaExist(schema_idenifier))
00174     {
00175       my_error(ER_SCHEMA_DOES_NOT_EXIST, schema_idenifier);
00176       return false;
00177     }
00178 
00179     /* Change options if current database is being altered. */
00180     success= plugin::StorageEngine::alterSchema(schema_message);
00181 
00182     if (success)
00183     {
00184       transaction_services.alterSchema(session, original_schema, schema_message);
00185       session.my_ok(1);
00186     }
00187     else
00188     {
00189       my_error(ER_ALTER_SCHEMA, schema_idenifier);
00190     }
00191   }
00192   session.startWaitingGlobalReadLock();
00193 
00194   return success;
00195 }
00196 
00197 
00198 /*
00199   Drop all tables in a database and the database itself
00200 
00201   SYNOPSIS
00202     rm_db()
00203     session     Thread handle
00204     db      Database name in the case given by user
00205             It's already validated and set to lower case
00206                         (if needed) when we come here
00207     if_exists   Don't give error if database doesn't exists
00208     silent    Don't generate errors
00209 
00210   RETURN
00211     false ok (Database dropped)
00212     ERROR Error
00213 */
00214 
00215 bool drop(Session &session, identifier::Schema &schema_identifier, const bool if_exists)
00216 {
00217   bool error= false;
00218 
00219   /*
00220     Do not drop database if another thread is holding read lock.
00221     Wait for global read lock before acquiring session->catalog()->schemaLock().
00222     After wait_if_global_read_lock() we have protection against another
00223     global read lock. If we would acquire session->catalog()->schemaLock() first,
00224     another thread could step in and get the global read lock before we
00225     reach wait_if_global_read_lock(). If this thread tries the same as we
00226     (admin a db), it would then go and wait on session->catalog()->schemaLock()...
00227     Furthermore wait_if_global_read_lock() checks if the current thread
00228     has the global read lock and refuses the operation with
00229     ER_CANT_UPDATE_WITH_READLOCK if applicable.
00230   */
00231   if (session.wait_if_global_read_lock(false, true))
00232   {
00233     return true;
00234   }
00235 
00236   do
00237   {
00238     boost::mutex::scoped_lock scopedLock(session.catalog().schemaLock());
00239     message::schema::shared_ptr message= plugin::StorageEngine::getSchemaDefinition(schema_identifier);
00240 
00241     /* See if the schema exists */
00242     if (not message)
00243     {
00244       if (if_exists)
00245       {
00246         std::string path;
00247         schema_identifier.getSQLPath(path);
00248 
00249         push_warning_printf(&session, DRIZZLE_ERROR::WARN_LEVEL_NOTE,
00250                             ER_DB_DROP_EXISTS, ER(ER_DB_DROP_EXISTS),
00251                             path.c_str());
00252       }
00253       else
00254       {
00255         error= true;
00256         my_error(ER_DB_DROP_EXISTS, schema_identifier);
00257         break;
00258       }
00259     }
00260     else
00261     {
00262       error= plugin::StorageEngine::dropSchema(session, schema_identifier, *message);
00263     }
00264 
00265   } while (0);
00266 
00267   /*
00268     If this database was the client's selected database, we silently
00269     change the client's selected database to nothing (to have an empty
00270     SELECT DATABASE() in the future). For this we free() session->db and set
00271     it to 0.
00272   */
00273   if (not error and schema_identifier.compare(*session.schema()))
00274     change_db_impl(session);
00275 
00276   session.startWaitingGlobalReadLock();
00277 
00278   return error;
00279 }
00280 
00343 bool change(Session &session, identifier::Schema &schema_identifier)
00344 {
00345 
00346   if (not plugin::Authorization::isAuthorized(*session.user(), schema_identifier))
00347   {
00348     /* Error message is set in isAuthorized */
00349     return true;
00350   }
00351 
00352   if (not check(session, schema_identifier))
00353   {
00354     my_error(ER_WRONG_DB_NAME, schema_identifier);
00355 
00356     return true;
00357   }
00358 
00359   if (not plugin::StorageEngine::doesSchemaExist(schema_identifier))
00360   {
00361     my_error(ER_BAD_DB_ERROR, schema_identifier);
00362 
00363     /* The operation failed. */
00364 
00365     return true;
00366   }
00367 
00368   change_db_impl(session, schema_identifier);
00369 
00370   return false;
00371 }
00372 
00384 static void change_db_impl(Session &session, identifier::Schema &schema_identifier)
00385 {
00386   /* 1. Change current database in Session. */
00387 
00388 #if 0
00389   if (new_db_name == NULL)
00390   {
00391     /*
00392       Session::set_db() does all the job -- it frees previous database name and
00393       sets the new one.
00394     */
00395 
00396     session->set_db(NULL, 0);
00397   }
00398   else
00399 #endif
00400   {
00401     /*
00402       Here we already have a copy of database name to be used in Session. So,
00403       we just call Session::reset_db(). Since Session::reset_db() does not releases
00404       the previous database name, we should do it explicitly.
00405     */
00406 
00407     session.set_db(schema_identifier.getSchemaName());
00408   }
00409 }
00410 
00411 static void change_db_impl(Session &session)
00412 {
00413   session.set_db(string());
00414 }
00415 
00416 /*
00417   Check if database name is valid
00418 
00419   SYNPOSIS
00420     check()
00421     org_name    Name of database and length
00422 
00423   RETURN
00424     false error
00425     true ok
00426 */
00427 
00428 bool check(Session &session, identifier::Schema &schema_identifier)
00429 {
00430   if (not plugin::Authorization::isAuthorized(*session.user(), schema_identifier))
00431   {
00432     return false;
00433   }
00434 
00435   return schema_identifier.isValid();
00436 }
00437 
00438 } /* namespace schema */
00439 
00440 } /* namespace drizzled */