Drizzled Public API Documentation

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 <assert.h>
00024 #include <boost/lexical_cast.hpp>
00025 #include <drizzled/identifier.h>
00026 #include <drizzled/internal/my_sys.h>
00027 
00028 #include <drizzled/error.h>
00029 #include <drizzled/errmsg_print.h>
00030 #include <drizzled/gettext.h>
00031 
00032 #include <drizzled/table.h>
00033 
00034 #include <drizzled/util/string.h>
00035 #include <drizzled/util/tablename_to_filename.h>
00036 
00037 #include <algorithm>
00038 #include <sstream>
00039 #include <cstdio>
00040 
00041 #include <boost/thread.hpp>
00042 
00043 using namespace std;
00044 
00045 namespace drizzled
00046 {
00047 
00048 class Table;
00049 
00050 extern std::string drizzle_tmpdir;
00051 extern pid_t current_pid;
00052 
00053 namespace identifier {
00054 class Schema;
00055 
00056 static const char hexchars[]= "0123456789abcdef";
00057 
00058 /*
00059   Translate a cursor name to a table name (WL #1324).
00060 
00061   SYNOPSIS
00062     filename_to_tablename()
00063       from                      The cursor name
00064       to                OUT     The table name
00065       to_length                 The size of the table name buffer.
00066 
00067   RETURN
00068     Table name length.
00069 */
00070 uint32_t Table::filename_to_tablename(const char *from, char *to, uint32_t to_length)
00071 {
00072   uint32_t length= 0;
00073 
00074   if (!memcmp(from, TMP_FILE_PREFIX, TMP_FILE_PREFIX_LENGTH))
00075   {
00076     /* Temporary table name. */
00077     length= strlen(strncpy(to, from, to_length));
00078   }
00079   else
00080   {
00081     for (; *from  && length < to_length; length++, from++)
00082     {
00083       if (*from != '@')
00084       {
00085         to[length]= *from;
00086         continue;
00087       }
00088       /* We've found an escaped char - skip the @ */
00089       from++;
00090       to[length]= 0;
00091       /* There will be a two-position hex-char version of the char */
00092       for (int x=1; x >= 0; x--)
00093       {
00094         if (*from >= '0' && *from <= '9')
00095           to[length] += ((*from++ - '0') << (4 * x));
00096         else if (*from >= 'a' && *from <= 'f')
00097           to[length] += ((*from++ - 'a' + 10) << (4 * x));
00098       }
00099       /* Backup because we advanced extra in the inner loop */
00100       from--;
00101     } 
00102   }
00103 
00104   return length;
00105 }
00106 
00107 /*
00108   Creates path to a cursor: drizzle_tmpdir/#sql1234_12_1.ext
00109 
00110   SYNOPSIS
00111    build_tmptable_filename()
00112      session                    The thread handle.
00113      buff                       Where to write result
00114      bufflen                    buff size
00115 
00116   NOTES
00117 
00118     Uses current_pid, thread_id, and tmp_table counter to create
00119     a cursor name in drizzle_tmpdir.
00120 
00121   RETURN
00122     path length on success, 0 on failure
00123 */
00124 
00125 #ifdef _GLIBCXX_HAVE_TLS 
00126 __thread uint32_t counter= 0;
00127 
00128 static uint32_t get_counter()
00129 {
00130   return ++counter;
00131 }
00132 
00133 #else
00134 boost::mutex counter_mutex;
00135 static uint32_t counter= 1;
00136 
00137 static uint32_t get_counter()
00138 {
00139   boost::mutex::scoped_lock lock(counter_mutex);
00140   uint32_t x;
00141   x= ++counter;
00142 
00143   return x;
00144 }
00145 
00146 #endif
00147 
00148 size_t Table::build_tmptable_filename(std::string &buffer)
00149 {
00150   size_t tmpdir_length;
00151   ostringstream post_tmpdir_str;
00152 
00153   buffer.append(drizzle_tmpdir);
00154   tmpdir_length= buffer.length();
00155 
00156   post_tmpdir_str << "/" << TMP_FILE_PREFIX << current_pid;
00157   post_tmpdir_str << pthread_self() << "-" << get_counter();
00158 
00159   buffer.append(post_tmpdir_str.str());
00160 
00161   transform(buffer.begin() + tmpdir_length, buffer.end(), buffer.begin() + tmpdir_length, ::tolower);
00162 
00163   return buffer.length();
00164 }
00165 
00166 size_t Table::build_tmptable_filename(std::vector<char> &buffer)
00167 {
00168   ostringstream post_tmpdir_str;
00169 
00170   post_tmpdir_str << drizzle_tmpdir << "/" << TMP_FILE_PREFIX << current_pid;
00171   post_tmpdir_str << pthread_self() << "-" << get_counter();
00172 
00173   buffer.resize(post_tmpdir_str.str().length() + 1);
00174   memcpy(&buffer[0], post_tmpdir_str.str().c_str(), post_tmpdir_str.str().size());
00175   buffer[post_tmpdir_str.str().size()]= 0;
00176 
00177   return buffer.size();
00178 }
00179 
00180 
00181 /*
00182   Creates path to a cursor: drizzle_data_dir/db/table.ext
00183 
00184   SYNOPSIS
00185    build_table_filename()
00186      buff                       Where to write result
00187                                 This may be the same as table_name.
00188      bufflen                    buff size
00189      db                         Database name
00190      table_name                 Table name
00191      ext                        File extension.
00192      flags                      table_name is temporary, do not change.
00193 
00194   NOTES
00195 
00196     Uses database and table name, and extension to create
00197     a cursor name in drizzle_data_dir. Database and table
00198     names are converted from system_charset_info into "fscs".
00199     Unless flags indicate a temporary table name.
00200     'db' is always converted.
00201     'ext' is not converted.
00202 
00203     The conversion suppression is required for ALTER Table. This
00204     statement creates intermediate tables. These are regular
00205     (non-temporary) tables with a temporary name. Their path names must
00206     be derivable from the table name. So we cannot use
00207     build_tmptable_filename() for them.
00208 
00209   RETURN
00210     path length on success, 0 on failure
00211 */
00212 
00213 size_t Table::build_table_filename(std::string &in_path, const std::string &in_db, const std::string &in_table_name, bool is_tmp)
00214 {
00215   bool conversion_error= false;
00216 
00217   conversion_error= util::tablename_to_filename(in_db, in_path);
00218   if (conversion_error)
00219   {
00220     errmsg_printf(error::ERROR,
00221                   _("Schema name cannot be encoded and fit within filesystem "
00222                     "name length restrictions."));
00223     return 0;
00224   }
00225 
00226   in_path.append(FN_ROOTDIR);
00227 
00228   if (is_tmp) // It a conversion tmp
00229   {
00230     in_path.append(in_table_name);
00231   }
00232   else
00233   {
00234     conversion_error= util::tablename_to_filename(in_table_name, in_path);
00235     if (conversion_error)
00236     {
00237       errmsg_printf(error::ERROR,
00238                     _("Table name cannot be encoded and fit within filesystem "
00239                       "name length restrictions."));
00240       return 0;
00241     }
00242   }
00243    
00244   return in_path.length();
00245 }
00246 
00247 Table::Table(const drizzled::Table &table) :
00248   identifier::Schema(table.getShare()->getSchemaName()),
00249   type(table.getShare()->getTableType()),
00250   table_name(table.getShare()->getTableName())
00251 {
00252   if (type == message::Table::TEMPORARY)
00253     path= table.getShare()->getPath();
00254 
00255   init();
00256 }
00257 
00258 void Table::init()
00259 {
00260   switch (type) {
00261   case message::Table::FUNCTION:
00262   case message::Table::STANDARD:
00263     assert(path.size() == 0);
00264     build_table_filename(path, getSchemaName(), table_name, false);
00265     break;
00266   case message::Table::INTERNAL:
00267     assert(path.size() == 0);
00268     build_table_filename(path, getSchemaName(), table_name, true);
00269     break;
00270   case message::Table::TEMPORARY:
00271     if (path.empty())
00272     {
00273       build_tmptable_filename(path);
00274     }
00275     break;
00276   }
00277 
00278   switch (type) {
00279   case message::Table::FUNCTION:
00280   case message::Table::STANDARD:
00281   case message::Table::INTERNAL:
00282     break;
00283   case message::Table::TEMPORARY:
00284     {
00285       size_t pos;
00286 
00287       pos= path.find("tmp/#sql");
00288       if (pos != std::string::npos) 
00289       {
00290         key_path= path.substr(pos);
00291       }
00292     }
00293     break;
00294   }
00295 
00296   util::insensitive_hash hasher;
00297   hash_value= hasher(path);
00298 
00299   std::string tb_name(getTableName());
00300   std::transform(tb_name.begin(), tb_name.end(), tb_name.begin(), ::tolower);
00301 
00302   key.set(getKeySize(), getSchemaName(), tb_name);
00303 }
00304 
00305 
00306 const std::string &Table::getPath() const
00307 {
00308   return path;
00309 }
00310 
00311 const std::string &Table::getKeyPath() const
00312 {
00313   if (key_path.empty())
00314     return path;
00315 
00316   return key_path;
00317 }
00318 
00319 void Table::getSQLPath(std::string &sql_path) const  // @todo this is just used for errors, we should find a way to optimize it
00320 {
00321   switch (type) {
00322   case message::Table::FUNCTION:
00323   case message::Table::STANDARD:
00324     sql_path.append(getSchemaName());
00325     sql_path.append(".");
00326     sql_path.append(table_name);
00327     break;
00328   case message::Table::INTERNAL:
00329     sql_path.append("temporary.");
00330     sql_path.append(table_name);
00331     break;
00332   case message::Table::TEMPORARY:
00333     sql_path.append(getSchemaName());
00334     sql_path.append(".#");
00335     sql_path.append(table_name);
00336     break;
00337   }
00338 }
00339 
00340 bool Table::isValid() const
00341 {
00342   if (not identifier::Schema::isValid())
00343     return false;
00344 
00345   bool error= false;
00346   do
00347   {
00348     if (table_name.empty())
00349     {
00350       error= true;
00351       break;
00352     }
00353 
00354     if (table_name.size() > NAME_LEN)
00355     {
00356       error= true;
00357       break;
00358     }
00359 
00360     if (table_name.at(table_name.length() -1) == ' ')
00361     {
00362       error= true;
00363       break;
00364     }
00365 
00366     if (table_name.at(0) == '.')
00367     {
00368       error= true;
00369       break;
00370     }
00371 
00372     {
00373       const CHARSET_INFO * const cs= &my_charset_utf8mb4_general_ci;
00374 
00375       int well_formed_error;
00376       uint32_t res= cs->cset->well_formed_len(cs, table_name.c_str(), table_name.c_str() + table_name.length(),
00377                                               NAME_CHAR_LEN, &well_formed_error);
00378       if (well_formed_error or table_name.length() != res)
00379       {
00380         error= true;
00381         break;
00382       }
00383     }
00384   } while (0);
00385 
00386   if (error)
00387   {
00388     std::string name;
00389 
00390     getSQLPath(name);
00391     my_error(ER_WRONG_TABLE_NAME, MYF(0), name.c_str());
00392 
00393     return false;
00394   }
00395 
00396   return true;
00397 }
00398 
00399 
00400 void Table::copyToTableMessage(message::Table &message) const
00401 {
00402   message.set_name(table_name);
00403   message.set_schema(getSchemaName());
00404 }
00405 
00406 void Table::Key::set(size_t resize_arg, const std::string &a, const std::string &b)
00407 {
00408   key_buffer.resize(resize_arg);
00409 
00410   std::copy(a.begin(), a.end(), key_buffer.begin());
00411   std::copy(b.begin(), b.end(), key_buffer.begin() + a.length() + 1);
00412 
00413   util::sensitive_hash hasher;
00414   hash_value= hasher(key_buffer);
00415 }
00416 
00417 std::size_t hash_value(Table const& b)
00418 {
00419   return b.getHashValue();
00420 }
00421 
00422 std::size_t hash_value(Table::Key const& b)
00423 {
00424   return b.getHashValue();
00425 }
00426 
00427 
00428 std::ostream& operator<<(std::ostream& output, Table::const_reference identifier)
00429 {
00430   output << "Table:(";
00431   output <<  identifier.getSchemaName();
00432   output << ", ";
00433   output << identifier.getTableName();
00434   output << ", ";
00435   output << message::type(identifier.getType());
00436   output << ", ";
00437   output << identifier.getPath();
00438   output << ", ";
00439   output << identifier.getHashValue();
00440   output << ")";
00441 
00442   return output;  // for multiple << operators.
00443 }
00444 
00445 } /* namespace identifier */
00446 } /* namespace drizzled */