Drizzled Public API Documentation

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 <cassert>
24 #include <boost/lexical_cast.hpp>
25 #include <drizzled/identifier.h>
26 #include <drizzled/internal/my_sys.h>
27 
28 #include <drizzled/error.h>
29 #include <drizzled/errmsg_print.h>
30 #include <drizzled/gettext.h>
31 
32 #include <drizzled/table.h>
33 
34 #include <drizzled/util/string.h>
35 #include <drizzled/util/tablename_to_filename.h>
36 
37 #include <algorithm>
38 #include <sstream>
39 #include <cstdio>
40 
41 #include <boost/thread.hpp>
42 
43 using namespace std;
44 
45 namespace drizzled {
46 
47 extern std::string drizzle_tmpdir;
48 extern pid_t current_pid;
49 
50 namespace identifier {
51 
52 static const char hexchars[]= "0123456789abcdef";
53 
54 /*
55  Translate a cursor name to a table name (WL #1324).
56 
57  SYNOPSIS
58  filename_to_tablename()
59  from The cursor name
60  to OUT The table name
61  to_length The size of the table name buffer.
62 
63  RETURN
64  Table name length.
65 */
66 uint32_t Table::filename_to_tablename(const char *from, char *to, uint32_t to_length)
67 {
68  uint32_t length= 0;
69 
70  if (!memcmp(from, TMP_FILE_PREFIX, TMP_FILE_PREFIX_LENGTH))
71  {
72  /* Temporary table name. */
73  length= strlen(strncpy(to, from, to_length));
74  }
75  else
76  {
77  for (; *from && length < to_length; length++, from++)
78  {
79  if (*from != '@')
80  {
81  to[length]= *from;
82  continue;
83  }
84  /* We've found an escaped char - skip the @ */
85  from++;
86  to[length]= 0;
87  /* There will be a two-position hex-char version of the char */
88  for (int x=1; x >= 0; x--)
89  {
90  if (*from >= '0' && *from <= '9')
91  {
92  to[length] += ((*from++ - '0') << (4 * x));
93  }
94  else if (*from >= 'a' && *from <= 'f')
95  {
96  to[length] += ((*from++ - 'a' + 10) << (4 * x));
97  }
98  }
99  /* Backup because we advanced extra in the inner loop */
100  from--;
101  }
102  }
103 
104  return length;
105 }
106 
107 /*
108  Creates path to a cursor: drizzle_tmpdir/#sql1234_12_1.ext
109 
110  SYNOPSIS
111  build_tmptable_filename()
112  session The thread handle.
113  buff Where to write result
114  bufflen buff size
115 
116  NOTES
117 
118  Uses current_pid, thread_id, and tmp_table counter to create
119  a cursor name in drizzle_tmpdir.
120 
121  RETURN
122  path length on success, 0 on failure
123 */
124 
125 #ifdef _GLIBCXX_HAVE_TLS
126 __thread uint32_t counter= 0;
127 
128 static uint32_t get_counter()
129 {
130  return ++counter;
131 }
132 
133 #else
134 boost::mutex counter_mutex;
135 static uint32_t counter= 1;
136 
137 static uint32_t get_counter()
138 {
139  boost::mutex::scoped_lock lock(counter_mutex);
140  return ++counter;
141 }
142 
143 #endif
144 
145 std::string Table::build_tmptable_filename()
146 {
147  ostringstream os;
148  os << "/" << TMP_FILE_PREFIX << current_pid << pthread_self() << "-" << get_counter();
149  return drizzle_tmpdir + boost::to_lower_copy(os.str());
150 }
151 
152 /*
153  Creates path to a cursor: drizzle_data_dir/db/table.ext
154 
155  SYNOPSIS
156  build_table_filename()
157  buff Where to write result
158  This may be the same as table_name.
159  bufflen buff size
160  db Database name
161  table_name Table name
162  ext File extension.
163  flags table_name is temporary, do not change.
164 
165  NOTES
166 
167  Uses database and table name, and extension to create
168  a cursor name in drizzle_data_dir. Database and table
169  names are converted from system_charset_info into "fscs".
170  Unless flags indicate a temporary table name.
171  'db' is always converted.
172  'ext' is not converted.
173 
174  The conversion suppression is required for ALTER Table. This
175  statement creates intermediate tables. These are regular
176  (non-temporary) tables with a temporary name. Their path names must
177  be derivable from the table name. So we cannot use
178  build_tmptable_filename() for them.
179 
180  RETURN
181  path length on success, 0 on failure
182 */
183 
184 std::string Table::build_table_filename(const std::string &in_db, const std::string &in_table_name, bool is_tmp)
185 {
186  string in_path= util::tablename_to_filename(in_db) + FN_LIBCHAR;
187  return in_path + (is_tmp ? in_table_name : util::tablename_to_filename(in_table_name));
188 }
189 
190 Table::Table(const drizzled::Table &table) :
191  identifier::Schema(str_ref(table.getShare()->getSchemaName())),
192  type(table.getShare()->getTableType()),
193  table_name(table.getShare()->getTableName())
194 {
195  if (type == message::Table::TEMPORARY)
196  {
197  path= table.getShare()->getPath();
198  }
199 
200  init();
201 }
202 
203 Table::Table(const identifier::Schema &schema,
204  const std::string &table_name_arg,
205  Type tmp_arg) :
206  Schema(schema),
207  type(tmp_arg),
208  table_name(table_name_arg)
209 {
210  init();
211 }
212 
213 Table::Table(const std::string &db_arg,
214  const std::string &table_name_arg,
215  Type tmp_arg) :
216  Schema(db_arg),
217  type(tmp_arg),
218  table_name(table_name_arg)
219 {
220  init();
221 }
222 
223 Table::Table(const std::string &schema_name_arg,
224  const std::string &table_name_arg,
225  const std::string &path_arg ) :
226  Schema(schema_name_arg),
227  type(message::Table::TEMPORARY),
228  path(path_arg),
229  table_name(table_name_arg)
230 {
231  init();
232 }
233 
234 void Table::init()
235 {
236  switch (type)
237  {
238  case message::Table::FUNCTION:
239  case message::Table::STANDARD:
240  assert(path.empty());
241  path= build_table_filename(getSchemaName(), table_name, false);
242  break;
243 
244  case message::Table::INTERNAL:
245  assert(path.empty());
246  path= build_table_filename(getSchemaName(), table_name, true);
247  break;
248 
249  case message::Table::TEMPORARY:
250  if (path.empty())
251  {
252  path= build_tmptable_filename();
253  }
254  break;
255  }
256 
257  if (type == message::Table::TEMPORARY)
258  {
259  size_t pos= path.find("tmp/#sql");
260  if (pos != std::string::npos)
261  {
262  key_path= path.substr(pos);
263  }
264  }
265 
266  hash_value= util::insensitive_hash()(path);
267  key.set(getKeySize(), getCatalogName(), getCompareWithSchemaName(), boost::to_lower_copy(std::string(getTableName())));
268 }
269 
270 
271 const std::string &Table::getPath() const
272 {
273  return path;
274 }
275 
276 const std::string &Table::getKeyPath() const
277 {
278  return key_path.empty() ? path : key_path;
279 }
280 
281 std::string Table::getSQLPath() const // @todo this is just used for errors, we should find a way to optimize it
282 {
283  switch (type)
284  {
285  case message::Table::FUNCTION:
286  case message::Table::STANDARD:
287  return getSchemaName() + "." + table_name;
288 
289  case message::Table::INTERNAL:
290  return "temporary." + table_name;
291 
292  case message::Table::TEMPORARY:
293  return getSchemaName() + ".#" + table_name;
294  }
295  assert(false);
296  return "<no table>";
297 }
298 
299 bool Table::isValid() const
300 {
301  if (identifier::Schema::isValid() == false)
302  {
303  return false;
304  }
305 
306  bool error= false;
307  if (table_name.empty()
308  || table_name.size() > NAME_LEN
309  || table_name[table_name.length() - 1] == ' '
310  || table_name[0] == '.')
311  {
312  error= true;
313  }
314  else
315  {
316  const charset_info_st& cs= my_charset_utf8mb4_general_ci;
317  int well_formed_error;
318  uint32_t res= cs.cset->well_formed_len(cs, table_name, NAME_CHAR_LEN, &well_formed_error);
319  if (well_formed_error or table_name.length() != res)
320  {
321  error= true;
322  }
323  }
324 
325  if (error == false)
326  {
327  return true;
328  }
329  my_error(ER_WRONG_TABLE_NAME, MYF(0), getSQLPath().c_str());
330 
331  return false;
332 }
333 
334 void Table::copyToTableMessage(message::Table &message) const
335 {
336  message.set_name(table_name);
337  message.set_schema(getSchemaName());
338 }
339 
340 void Table::Key::set(size_t resize_arg, const std::string &catalog_arg, const std::string &schema_arg, const std::string &table_arg)
341 {
342  key_buffer.resize(resize_arg);
343 
344  schema_offset= catalog_arg.length() +1;
345  table_offset= schema_offset +schema_arg.length() +1;
346 
347  std::copy(catalog_arg.begin(), catalog_arg.end(), key_buffer.begin());
348  std::copy(schema_arg.begin(), schema_arg.end(), key_buffer.begin() +schema_offset);
349  std::copy(table_arg.begin(), table_arg.end(), key_buffer.begin() +table_offset);
350 
351  util::sensitive_hash hasher;
352  hash_value= hasher(key_buffer);
353 }
354 
355 std::size_t hash_value(Table const& b)
356 {
357  return b.getHashValue();
358 }
359 
360 std::size_t hash_value(Table::Key const& b)
361 {
362  return b.getHashValue();
363 }
364 
365 std::ostream& operator<<(std::ostream& output, const Table::Key& arg)
366 {
367  return output << "Key:(" << arg.schema_name() << ", " << arg.table_name() << ", " << arg.hash() << std::endl;
368 }
369 
370 std::ostream& operator<<(std::ostream& output, const Table& identifier)
371 {
372  return output << "Table:(" << identifier.getSchemaName() << ", " << identifier.getTableName() << ", " << message::type(identifier.getType()) << ", " << identifier.getPath() << ", " << identifier.getHashValue() << ")";
373 }
374 
375 } /* namespace identifier */
376 } /* namespace drizzled */