~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/identifier/table.cc

  • Committer: Andrew Hutchings
  • Date: 2011-01-21 11:23:19 UTC
  • mto: (2100.1.1 build)
  • mto: This revision was merged to the branch mainline in revision 2101.
  • Revision ID: andrew@linuxjedi.co.uk-20110121112319-nj1cvg0yt3nnf2rr
Add errors page to drizzle client docs
Add link to specific error in migration docs
Minor changes to migration docs

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