~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/table_identifier.cc

OK, Sun Studio still didn't like that...seems to think that inline means something different than other compilers think it is...

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
2
 *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3
3
 *
4
 
 *  Copyright (C) 2009 Sun Microsystems, Inc.
 
4
 *  Copyright (C) 2009 Sun Microsystems
5
5
 *
6
6
 *  This program is free software; you can redistribute it and/or modify
7
7
 *  it under the terms of the GNU General Public License as published by
18
18
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
19
 */
20
20
 
21
 
#include <config.h>
 
21
#include "config.h"
22
22
 
23
23
#include <assert.h>
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>
 
24
 
 
25
#include "drizzled/table_identifier.h"
 
26
#include "drizzled/session.h"
 
27
#include "drizzled/current_session.h"
 
28
#include "drizzled/internal/my_sys.h"
 
29
#include "drizzled/data_home.h"
36
30
 
37
31
#include <algorithm>
38
32
#include <sstream>
39
33
#include <cstdio>
40
34
 
41
 
#include <boost/thread.hpp>
42
 
 
43
35
using namespace std;
44
36
 
45
37
namespace drizzled
46
38
{
47
39
 
48
 
class Table;
49
 
 
50
 
extern std::string drizzle_tmpdir;
 
40
extern char *drizzle_tmpdir;
51
41
extern pid_t current_pid;
52
42
 
53
 
namespace identifier {
54
 
class Schema;
55
 
 
56
43
static const char hexchars[]= "0123456789abcdef";
57
44
 
 
45
static bool tablename_to_filename(const char *from, char *to, size_t to_length);
 
46
static size_t build_tmptable_filename(std::string &path);
 
47
 
58
48
/*
59
49
  Translate a cursor name to a table name (WL #1324).
60
50
 
67
57
  RETURN
68
58
    Table name length.
69
59
*/
70
 
uint32_t Table::filename_to_tablename(const char *from, char *to, uint32_t to_length)
 
60
uint32_t filename_to_tablename(const char *from, char *to, uint32_t to_length)
71
61
{
72
62
  uint32_t length= 0;
73
63
 
122
112
    path length on success, 0 on failure
123
113
*/
124
114
 
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
 
  uint32_t x;
141
 
  x= ++counter;
142
 
 
143
 
  return x;
144
 
}
145
 
 
146
 
#endif
147
 
 
148
 
size_t Table::build_tmptable_filename(std::string &buffer)
 
115
static size_t build_tmptable_filename(std::string &buffer)
149
116
{
150
117
  size_t tmpdir_length;
151
118
  ostringstream post_tmpdir_str;
152
119
 
 
120
  Session *session= current_session;
 
121
 
153
122
  buffer.append(drizzle_tmpdir);
154
123
  tmpdir_length= buffer.length();
155
124
 
156
125
  post_tmpdir_str << "/" << TMP_FILE_PREFIX << current_pid;
157
 
  post_tmpdir_str << pthread_self() << "-" << get_counter();
 
126
  post_tmpdir_str << session->thread_id << session->tmp_table++;
158
127
 
159
128
  buffer.append(post_tmpdir_str.str());
160
129
 
163
132
  return buffer.length();
164
133
}
165
134
 
166
 
size_t Table::build_tmptable_filename(std::vector<char> &buffer)
167
 
{
168
 
  ostringstream post_tmpdir_str;
169
 
 
170
 
  post_tmpdir_str << drizzle_tmpdir << "/" << TMP_FILE_PREFIX << current_pid;
171
 
  post_tmpdir_str << pthread_self() << "-" << get_counter();
172
 
 
173
 
  buffer.resize(post_tmpdir_str.str().length() + 1);
174
 
  memcpy(&buffer[0], post_tmpdir_str.str().c_str(), post_tmpdir_str.str().size());
175
 
  buffer[post_tmpdir_str.str().size()]= 0;
176
 
 
177
 
  return buffer.size();
178
 
}
179
 
 
180
 
 
181
135
/*
182
136
  Creates path to a cursor: drizzle_data_dir/db/table.ext
183
137
 
189
143
     db                         Database name
190
144
     table_name                 Table name
191
145
     ext                        File extension.
192
 
     flags                      table_name is temporary, do not change.
 
146
     flags                      FN_FROM_IS_TMP or FN_TO_IS_TMP
 
147
                                table_name is temporary, do not change.
193
148
 
194
149
  NOTES
195
150
 
210
165
    path length on success, 0 on failure
211
166
*/
212
167
 
213
 
size_t Table::build_table_filename(std::string &in_path, const std::string &in_db, const std::string &in_table_name, bool is_tmp)
 
168
size_t build_table_filename(std::string &path, const char *db, const char *table_name, bool is_tmp)
214
169
{
 
170
  char dbbuff[FN_REFLEN];
 
171
  char tbbuff[FN_REFLEN];
215
172
  bool conversion_error= false;
216
173
 
217
 
  conversion_error= util::tablename_to_filename(in_db, in_path);
 
174
  memset(tbbuff, 0, sizeof(tbbuff));
 
175
  if (is_tmp) // FN_FROM_IS_TMP | FN_TO_IS_TMP
 
176
  {
 
177
    strncpy(tbbuff, table_name, sizeof(tbbuff));
 
178
  }
 
179
  else
 
180
  {
 
181
    conversion_error= tablename_to_filename(table_name, tbbuff, sizeof(tbbuff));
 
182
    if (conversion_error)
 
183
    {
 
184
      errmsg_printf(ERRMSG_LVL_ERROR,
 
185
                    _("Table name cannot be encoded and fit within filesystem "
 
186
                      "name length restrictions."));
 
187
      return 0;
 
188
    }
 
189
  }
 
190
  memset(dbbuff, 0, sizeof(dbbuff));
 
191
  conversion_error= tablename_to_filename(db, dbbuff, sizeof(dbbuff));
218
192
  if (conversion_error)
219
193
  {
220
 
    errmsg_printf(error::ERROR,
 
194
    errmsg_printf(ERRMSG_LVL_ERROR,
221
195
                  _("Schema name cannot be encoded and fit within filesystem "
222
196
                    "name length restrictions."));
223
197
    return 0;
224
198
  }
225
 
 
226
 
  in_path.append(FN_ROOTDIR);
227
 
 
228
 
  if (is_tmp) // It a conversion tmp
 
199
   
 
200
 
 
201
  int rootdir_len= strlen(FN_ROOTDIR);
 
202
  path.append(drizzle_data_home);
 
203
  ssize_t without_rootdir= path.length() - rootdir_len;
 
204
 
 
205
  /* Don't add FN_ROOTDIR if dirzzle_data_home already includes it */
 
206
  if (without_rootdir >= 0)
229
207
  {
230
 
    in_path.append(in_table_name);
 
208
    const char *tmp= path.c_str() + without_rootdir;
 
209
 
 
210
    if (memcmp(tmp, FN_ROOTDIR, rootdir_len) != 0)
 
211
      path.append(FN_ROOTDIR);
231
212
  }
232
 
  else
 
213
 
 
214
  path.append(dbbuff);
 
215
  path.append(FN_ROOTDIR);
 
216
  path.append(tbbuff);
 
217
 
 
218
  return path.length();
 
219
}
 
220
 
 
221
 
 
222
/*
 
223
  Translate a table name to a cursor name (WL #1324).
 
224
 
 
225
  SYNOPSIS
 
226
    tablename_to_filename()
 
227
      from                      The table name
 
228
      to                OUT     The cursor name
 
229
      to_length                 The size of the cursor name buffer.
 
230
 
 
231
  RETURN
 
232
    true if errors happen. false on success.
 
233
*/
 
234
static bool tablename_to_filename(const char *from, char *to, size_t to_length)
 
235
{
 
236
  
 
237
  size_t length= 0;
 
238
  for (; *from  && length < to_length; length++, from++)
233
239
  {
234
 
    conversion_error= util::tablename_to_filename(in_table_name, in_path);
235
 
    if (conversion_error)
 
240
    if ((*from >= '0' && *from <= '9') ||
 
241
        (*from >= 'A' && *from <= 'Z') ||
 
242
        (*from >= 'a' && *from <= 'z') ||
 
243
/* OSX defines an extra set of high-bit and multi-byte characters
 
244
   that cannot be used on the filesystem. Instead of trying to sort
 
245
   those out, we'll just escape encode all high-bit-set chars on OSX.
 
246
   It won't really hurt anything - it'll just make some filenames ugly. */
 
247
#if !defined(TARGET_OS_OSX)
 
248
        ((unsigned char)*from >= 128) ||
 
249
#endif
 
250
        (*from == '_') ||
 
251
        (*from == ' ') ||
 
252
        (*from == '-'))
236
253
    {
237
 
      errmsg_printf(error::ERROR,
238
 
                    _("Table name cannot be encoded and fit within filesystem "
239
 
                      "name length restrictions."));
240
 
      return 0;
 
254
      to[length]= *from;
 
255
      continue;
241
256
    }
242
 
  }
243
257
   
244
 
  return in_path.length();
245
 
}
246
 
 
247
 
Table::Table(const drizzled::Table &table) :
248
 
  identifier::Schema(table.getShare()->getSchemaName()),
249
 
  type(table.getShare()->getTableType()),
250
 
  table_name(table.getShare()->getTableName())
251
 
{
252
 
  if (type == message::Table::TEMPORARY)
253
 
    path= table.getShare()->getPath();
254
 
 
255
 
  init();
256
 
}
257
 
 
258
 
void Table::init()
259
 
{
260
 
  switch (type) {
261
 
  case message::Table::FUNCTION:
262
 
  case message::Table::STANDARD:
263
 
    assert(path.size() == 0);
264
 
    build_table_filename(path, getSchemaName(), table_name, false);
265
 
    break;
266
 
  case message::Table::INTERNAL:
267
 
    assert(path.size() == 0);
268
 
    build_table_filename(path, getSchemaName(), table_name, true);
269
 
    break;
270
 
  case message::Table::TEMPORARY:
271
 
    if (path.empty())
272
 
    {
 
258
    if (length + 3 >= to_length)
 
259
      return true;
 
260
 
 
261
    /* We need to escape this char in a way that can be reversed */
 
262
    to[length++]= '@';
 
263
    to[length++]= hexchars[(*from >> 4) & 15];
 
264
    to[length]= hexchars[(*from) & 15];
 
265
  }
 
266
 
 
267
  if (internal::check_if_legal_tablename(to) &&
 
268
      length + 4 < to_length)
 
269
  {
 
270
    memcpy(to + length, "@@@", 4);
 
271
    length+= 3;
 
272
  }
 
273
  return false;
 
274
}
 
275
 
 
276
void TableIdentifier::primeLower()
 
277
{
 
278
  if (lower_db.empty())
 
279
  {
 
280
    lower_db.append(db);
 
281
    lower_table_name.append(table_name);
 
282
 
 
283
    std::transform(lower_table_name.begin(), lower_table_name.end(),
 
284
                   lower_table_name.begin(), ::tolower);
 
285
 
 
286
    std::transform(lower_db.begin(), lower_db.end(),
 
287
                   lower_db.begin(), ::tolower);
 
288
  }
 
289
}
 
290
 
 
291
 
 
292
const std::string &TableIdentifier::getPath()
 
293
{
 
294
  if (path.empty())
 
295
  {
 
296
    primeLower();
 
297
    switch (type) {
 
298
    case message::Table::STANDARD:
 
299
      build_table_filename(path, lower_db.c_str(), lower_table_name.c_str(), false);
 
300
      break;
 
301
    case message::Table::INTERNAL:
 
302
      build_table_filename(path, lower_db.c_str(), lower_table_name.c_str(), true);
 
303
      break;
 
304
    case message::Table::TEMPORARY:
273
305
      build_tmptable_filename(path);
274
 
    }
275
 
    break;
276
 
  }
277
 
 
278
 
  switch (type) {
279
 
  case message::Table::FUNCTION:
280
 
  case message::Table::STANDARD:
281
 
  case message::Table::INTERNAL:
282
 
    break;
283
 
  case message::Table::TEMPORARY:
284
 
    {
285
 
      size_t pos;
286
 
 
287
 
      pos= path.find("tmp/#sql");
288
 
      if (pos != std::string::npos) 
289
 
      {
290
 
        key_path= path.substr(pos);
291
 
      }
292
 
    }
293
 
    break;
294
 
  }
295
 
 
296
 
  util::insensitive_hash hasher;
297
 
  hash_value= hasher(path);
298
 
 
299
 
  std::string tb_name(getTableName());
300
 
  std::transform(tb_name.begin(), tb_name.end(), tb_name.begin(), ::tolower);
301
 
 
302
 
  key.set(getKeySize(), getSchemaName(), tb_name);
303
 
}
304
 
 
305
 
 
306
 
const std::string &Table::getPath() const
307
 
{
 
306
      break;
 
307
    case message::Table::FUNCTION:
 
308
      path.append(db);
 
309
      path.append(".");
 
310
      path.append(table_name);
 
311
      break;
 
312
    }
 
313
    assert(path.length()); // TODO throw exception, this is a possibility
 
314
  }
 
315
 
308
316
  return path;
309
317
}
310
318
 
311
 
const std::string &Table::getKeyPath() const
312
 
{
313
 
  if (key_path.empty())
314
 
    return path;
315
 
 
316
 
  return key_path;
317
 
}
318
 
 
319
 
void Table::getSQLPath(std::string &sql_path) const  // @todo this is just used for errors, we should find a way to optimize it
320
 
{
321
 
  switch (type) {
322
 
  case message::Table::FUNCTION:
323
 
  case message::Table::STANDARD:
324
 
    sql_path.append(getSchemaName());
325
 
    sql_path.append(".");
326
 
    sql_path.append(table_name);
327
 
    break;
328
 
  case message::Table::INTERNAL:
329
 
    sql_path.append("temporary.");
330
 
    sql_path.append(table_name);
331
 
    break;
332
 
  case message::Table::TEMPORARY:
333
 
    sql_path.append(getSchemaName());
334
 
    sql_path.append(".#");
335
 
    sql_path.append(table_name);
336
 
    break;
337
 
  }
338
 
}
339
 
 
340
 
bool Table::isValid() const
341
 
{
342
 
  if (not identifier::Schema::isValid())
343
 
    return false;
344
 
 
345
 
  bool error= false;
346
 
  do
347
 
  {
348
 
    if (table_name.empty())
349
 
    {
350
 
      error= true;
351
 
      break;
352
 
    }
353
 
 
354
 
    if (table_name.size() > NAME_LEN)
355
 
    {
356
 
      error= true;
357
 
      break;
358
 
    }
359
 
 
360
 
    if (table_name.at(table_name.length() -1) == ' ')
361
 
    {
362
 
      error= true;
363
 
      break;
364
 
    }
365
 
 
366
 
    if (table_name.at(0) == '.')
367
 
    {
368
 
      error= true;
369
 
      break;
370
 
    }
371
 
 
372
 
    {
373
 
      const CHARSET_INFO * const cs= &my_charset_utf8mb4_general_ci;
374
 
 
375
 
      int well_formed_error;
376
 
      uint32_t res= cs->cset->well_formed_len(cs, table_name.c_str(), table_name.c_str() + table_name.length(),
377
 
                                              NAME_CHAR_LEN, &well_formed_error);
378
 
      if (well_formed_error or table_name.length() != res)
379
 
      {
380
 
        error= true;
381
 
        break;
382
 
      }
383
 
    }
384
 
  } while (0);
385
 
 
386
 
  if (error)
387
 
  {
388
 
    std::string name;
389
 
 
390
 
    getSQLPath(name);
391
 
    my_error(ER_WRONG_TABLE_NAME, MYF(0), name.c_str());
392
 
 
393
 
    return false;
394
 
  }
395
 
 
396
 
  return true;
397
 
}
398
 
 
399
 
 
400
 
void Table::copyToTableMessage(message::Table &message) const
 
319
 
 
320
const std::string &TableIdentifier::getSQLPath()
 
321
{
 
322
  if (sql_path.empty())
 
323
  {
 
324
    switch (type) {
 
325
    case message::Table::FUNCTION:
 
326
    case message::Table::STANDARD:
 
327
      sql_path.append(db);
 
328
      sql_path.append(".");
 
329
      sql_path.append(table_name);
 
330
      break;
 
331
    case message::Table::INTERNAL:
 
332
      sql_path.append("temporary.");
 
333
      sql_path.append(table_name);
 
334
      break;
 
335
    case message::Table::TEMPORARY:
 
336
      sql_path.append(db);
 
337
      sql_path.append(".#");
 
338
      sql_path.append(table_name);
 
339
      break;
 
340
    }
 
341
  }
 
342
 
 
343
  return sql_path;
 
344
}
 
345
 
 
346
 
 
347
void TableIdentifier::copyToTableMessage(message::Table &message)
401
348
{
402
349
  message.set_name(table_name);
403
 
  message.set_schema(getSchemaName());
404
 
}
405
 
 
406
 
void Table::Key::set(size_t resize_arg, const std::string &a, const std::string &b)
407
 
{
408
 
  key_buffer.resize(resize_arg);
409
 
 
410
 
  std::copy(a.begin(), a.end(), key_buffer.begin());
411
 
  std::copy(b.begin(), b.end(), key_buffer.begin() + a.length() + 1);
412
 
 
413
 
  util::sensitive_hash hasher;
414
 
  hash_value= hasher(key_buffer);
415
 
}
416
 
 
417
 
std::size_t hash_value(Table const& b)
418
 
{
419
 
  return b.getHashValue();
420
 
}
421
 
 
422
 
std::size_t hash_value(Table::Key const& b)
423
 
{
424
 
  return b.getHashValue();
425
 
}
426
 
 
427
 
 
428
 
std::ostream& operator<<(std::ostream& output, Table::const_reference identifier)
429
 
{
430
 
  output << "Table:(";
431
 
  output <<  identifier.getSchemaName();
432
 
  output << ", ";
433
 
  output << identifier.getTableName();
434
 
  output << ", ";
435
 
  output << message::type(identifier.getType());
436
 
  output << ", ";
437
 
  output << identifier.getPath();
438
 
  output << ", ";
439
 
  output << identifier.getHashValue();
440
 
  output << ")";
441
 
 
442
 
  return output;  // for multiple << operators.
443
 
}
444
 
 
445
 
} /* namespace identifier */
 
350
  message.set_schema(db);
 
351
}
 
352
 
446
353
} /* namespace drizzled */