Drizzled Public API Documentation

table_proto.cc
1 /* Copyright (C) 2000-2006 MySQL AB
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 #include <config.h>
17 #include <drizzled/error.h>
18 #include <drizzled/session.h>
19 #include <drizzled/sql_table.h>
21 
22 #include <drizzled/plugin/storage_engine.h>
23 
24 #include <drizzled/internal/my_sys.h>
25 #include <drizzled/typelib.h>
26 #include <drizzled/util/test.h>
27 
28 /* For proto */
29 #include <string>
30 #include <fstream>
31 #include <fcntl.h>
32 #include <drizzled/message/schema.h>
33 #include <drizzled/message/table.h>
34 #include <google/protobuf/io/zero_copy_stream.h>
35 #include <google/protobuf/io/zero_copy_stream_impl.h>
36 #include <google/protobuf/message.h>
37 
38 #include <drizzled/table_proto.h>
39 #include <drizzled/charset.h>
40 #include <drizzled/create_field.h>
41 #include <drizzled/function/time/typecast.h>
42 
43 using namespace std;
44 
45 namespace drizzled {
46 
47 static
48 bool fill_table_proto(const identifier::Table& identifier,
49  message::Table &table_proto,
50  List<CreateField> &create_fields,
51  HA_CREATE_INFO *create_info,
52  uint32_t keys,
53  KeyInfo *key_info)
54 {
55  CreateField *field_arg;
56  List<CreateField>::iterator it(create_fields.begin());
57  message::Table::TableOptions *table_options= table_proto.mutable_options();
58 
59  if (create_fields.size() > MAX_FIELDS)
60  {
61  my_error(ER_TOO_MANY_FIELDS, MYF(0), ER(ER_TOO_MANY_FIELDS));
62  return true;
63  }
64 
65  assert(strcmp(table_proto.engine().name().c_str(),
66  create_info->db_type->getName().c_str())==0);
67 
68  message::schema::shared_ptr schema_message= plugin::StorageEngine::getSchemaDefinition(identifier);
69 
70  if (schema_message and not message::is_replicated(*schema_message))
71  {
72  message::set_is_replicated(table_proto, false);
73  }
74 
75  int field_number= 0;
76  bool use_existing_fields= table_proto.field_size() > 0;
77  while ((field_arg= it++))
78  {
79  message::Table::Field *attribute;
80 
81  /* some (one) code path for CREATE TABLE fills the proto
82  out more than the others, so we already have partially
83  filled out Field messages */
84 
85  if (use_existing_fields)
86  {
87  attribute= table_proto.mutable_field(field_number++);
88  }
89  else
90  {
91  /* Other code paths still have to fill out the proto */
92  attribute= table_proto.add_field();
93 
94  if (field_arg->flags & NOT_NULL_FLAG)
95  {
96  attribute->mutable_constraints()->set_is_notnull(true);
97  }
98 
99  if (field_arg->flags & UNSIGNED_FLAG and
100  (field_arg->sql_type == DRIZZLE_TYPE_LONGLONG or field_arg->sql_type == DRIZZLE_TYPE_LONG))
101  {
102  field_arg->sql_type= DRIZZLE_TYPE_LONGLONG;
103  attribute->mutable_constraints()->set_is_unsigned(true);
104  }
105 
106  attribute->set_name(field_arg->field_name);
107  }
108 
109  assert(((field_arg->flags & NOT_NULL_FLAG)) == attribute->constraints().is_notnull());
110  assert(strcmp(attribute->name().c_str(), field_arg->field_name)==0);
111 
112 
113  message::Table::Field::FieldType parser_type= attribute->type();
114 
115  if (field_arg->sql_type == DRIZZLE_TYPE_NULL)
116  {
117  my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), table_proto.name().c_str(), -1);
118  return true;
119  }
120 
121  if (field_arg->flags & UNSIGNED_FLAG and
122  (field_arg->sql_type == DRIZZLE_TYPE_LONGLONG or field_arg->sql_type == DRIZZLE_TYPE_LONG))
123  {
124  message::Table::Field::FieldConstraints *constraints= attribute->mutable_constraints();
125 
126  field_arg->sql_type= DRIZZLE_TYPE_LONGLONG;
127  constraints->set_is_unsigned(true);
128  }
129 
130  attribute->set_type(message::internalFieldTypeToFieldProtoType(field_arg->sql_type));
131 
132  switch (attribute->type()) {
133  case message::Table::Field::BIGINT:
134  case message::Table::Field::INTEGER:
135  case message::Table::Field::DATE:
136  case message::Table::Field::DATETIME:
137  case message::Table::Field::UUID:
138  case message::Table::Field::IPV6:
139  case message::Table::Field::TIME:
140  case message::Table::Field::BOOLEAN:
141  break;
142  case message::Table::Field::DOUBLE:
143  {
144  /*
145  * For DOUBLE, we only add a specific scale and precision iff
146  * the fixed decimal point has been specified...
147  */
148  if (field_arg->decimals != NOT_FIXED_DEC)
149  {
150  message::Table::Field::NumericFieldOptions *numeric_field_options;
151 
152  numeric_field_options= attribute->mutable_numeric_options();
153 
154  numeric_field_options->set_precision(field_arg->length);
155  numeric_field_options->set_scale(field_arg->decimals);
156  }
157  }
158  break;
159  case message::Table::Field::VARCHAR:
160  {
161  message::Table::Field::StringFieldOptions *string_field_options;
162 
163  string_field_options= attribute->mutable_string_options();
164 
165  if (! use_existing_fields || string_field_options->length()==0)
166  string_field_options->set_length(field_arg->length
167  / field_arg->charset->mbmaxlen);
168  else
169  assert((uint32_t)string_field_options->length() == (uint32_t)(field_arg->length / field_arg->charset->mbmaxlen));
170 
171  if (! string_field_options->has_collation())
172  {
173  string_field_options->set_collation_id(field_arg->charset->number);
174  string_field_options->set_collation(field_arg->charset->name);
175  }
176  break;
177  }
178  case message::Table::Field::DECIMAL:
179  {
180  message::Table::Field::NumericFieldOptions *numeric_field_options;
181 
182  numeric_field_options= attribute->mutable_numeric_options();
183  /* This is magic, I hate magic numbers -Brian */
184  numeric_field_options->set_precision(field_arg->length + ( field_arg->decimals ? -2 : -1));
185  numeric_field_options->set_scale(field_arg->decimals);
186  break;
187  }
188  case message::Table::Field::ENUM:
189  {
190  message::Table::Field::EnumerationValues *enumeration_options;
191 
192  assert(field_arg->interval);
193 
194  enumeration_options= attribute->mutable_enumeration_values();
195 
196  for (uint32_t pos= 0; pos < field_arg->interval->count; pos++)
197  {
198  const char *src= field_arg->interval->type_names[pos];
199 
200  enumeration_options->add_field_value(src);
201  }
202  enumeration_options->set_collation_id(field_arg->charset->number);
203  enumeration_options->set_collation(field_arg->charset->name);
204  break;
205  }
206 
207  case message::Table::Field::BLOB:
208  {
209  message::Table::Field::StringFieldOptions *string_field_options;
210 
211  string_field_options= attribute->mutable_string_options();
212  string_field_options->set_collation_id(field_arg->charset->number);
213  string_field_options->set_collation(field_arg->charset->name);
214  }
215 
216  break;
217 
218  case message::Table::Field::EPOCH:
219  {
220  if (field_arg->sql_type == DRIZZLE_TYPE_MICROTIME)
221  attribute->mutable_time_options()->set_microseconds(true);
222  }
223 
224  break;
225  }
226 
227  assert (!use_existing_fields || parser_type == attribute->type());
228 
229 #ifdef NOTDONE
230  field_constraints= attribute->mutable_constraints();
231  constraints->set_is_nullable(field_arg->def->null_value);
232 #endif
233 
234  if (not field_arg->comment.empty())
235  {
236  uint32_t tmp_len= system_charset_info->cset->charpos(system_charset_info,
237  field_arg->comment.begin(),
238  field_arg->comment.end(),
239  COLUMN_COMMENT_MAXLEN);
240 
241  if (tmp_len < field_arg->comment.size())
242  {
243  my_error(ER_WRONG_STRING_LENGTH, MYF(0), field_arg->comment.data(), "COLUMN COMMENT", (uint32_t) COLUMN_COMMENT_MAXLEN);
244  return true;
245  }
246 
247  if (not use_existing_fields)
248  attribute->set_comment(field_arg->comment.data());
249 
250  assert(strcmp(attribute->comment().c_str(), field_arg->comment.data())==0);
251  }
252 
253  if (field_arg->unireg_check == Field::NEXT_NUMBER)
254  {
255  message::Table::Field::NumericFieldOptions *field_options;
256  field_options= attribute->mutable_numeric_options();
257  field_options->set_is_autoincrement(true);
258  }
259 
260  if (field_arg->unireg_check == Field::TIMESTAMP_DN_FIELD
261  || field_arg->unireg_check == Field::TIMESTAMP_DNUN_FIELD)
262  {
263  message::Table::Field::FieldOptions *field_options;
264  field_options= attribute->mutable_options();
265  field_options->set_default_expression("CURRENT_TIMESTAMP");
266  }
267 
268  if (field_arg->unireg_check == Field::TIMESTAMP_UN_FIELD
269  || field_arg->unireg_check == Field::TIMESTAMP_DNUN_FIELD)
270  {
271  message::Table::Field::FieldOptions *field_options;
272  field_options= attribute->mutable_options();
273  field_options->set_update_expression("CURRENT_TIMESTAMP");
274  }
275 
276  if (field_arg->def == NULL && not attribute->constraints().is_notnull())
277  {
278  message::Table::Field::FieldOptions *field_options;
279  field_options= attribute->mutable_options();
280 
281  field_options->set_default_null(true);
282  }
283  if (field_arg->def)
284  {
285  message::Table::Field::FieldOptions *field_options;
286  field_options= attribute->mutable_options();
287 
288  if (field_arg->def->is_null())
289  {
290  field_options->set_default_null(true);
291  }
292  else
293  {
294  String d;
295  String *default_value= field_arg->def->val_str(&d);
296 
297  assert(default_value);
298 
299  if ((field_arg->sql_type==DRIZZLE_TYPE_VARCHAR
300  || field_arg->sql_type==DRIZZLE_TYPE_BLOB)
301  && ((field_arg->length / field_arg->charset->mbmaxlen)
302  < default_value->length()))
303  {
304  my_error(ER_INVALID_DEFAULT, MYF(0), field_arg->field_name);
305  return true;
306  }
307 
308  if (field::isDateTime(field_arg->sql_type))
309  {
310  type::Time ltime;
311 
312  if (field_arg->def->get_date(ltime, TIME_FUZZY_DATE))
313  {
314  my_error(ER_INVALID_DATETIME_VALUE, MYF(ME_FATALERROR),
315  default_value->c_str());
316  return true;
317  }
318 
319  /* We now do the casting down to the appropriate type.
320 
321  Yes, this implicit casting is balls.
322  It was previously done on reading the proto back in,
323  but we really shouldn't store the bogus things in the proto,
324  and instead do the casting behaviour here.
325 
326  the timestamp errors are taken care of elsewhere.
327  */
328 
329  if (field_arg->sql_type == DRIZZLE_TYPE_DATETIME)
330  {
331  Item *typecast= new Item_datetime_typecast(field_arg->def);
332  typecast->quick_fix_field();
333  typecast->val_str(default_value);
334  }
335  else if (field_arg->sql_type == DRIZZLE_TYPE_DATE)
336  {
337  Item *typecast= new Item_date_typecast(field_arg->def);
338  typecast->quick_fix_field();
339  typecast->val_str(default_value);
340  }
341  }
342 
343  if ((field_arg->sql_type == DRIZZLE_TYPE_VARCHAR && field_arg->charset == &my_charset_bin)
344  || (field_arg->sql_type == DRIZZLE_TYPE_BLOB && field_arg->charset == &my_charset_bin))
345  {
346  field_options->set_default_bin_value(string(default_value->c_ptr(), default_value->length()));
347  }
348  else
349  {
350  field_options->set_default_value(default_value->c_ptr());
351  }
352  }
353  }
354 
355  assert(field_arg->unireg_check == Field::NONE
356  || field_arg->unireg_check == Field::NEXT_NUMBER
357  || field_arg->unireg_check == Field::TIMESTAMP_DN_FIELD
358  || field_arg->unireg_check == Field::TIMESTAMP_UN_FIELD
359  || field_arg->unireg_check == Field::TIMESTAMP_DNUN_FIELD);
360 
361  }
362 
363  assert(! use_existing_fields || (field_number == table_proto.field_size()));
364 
365  if (create_info->table_options & HA_OPTION_PACK_RECORD)
366  table_options->set_pack_record(true);
367 
368  if (table_options->has_comment() && table_options->comment().length() == 0)
369  table_options->clear_comment();
370 
371  if (table_options->has_comment())
372  {
373  uint32_t tmp_len;
374  tmp_len= system_charset_info->cset->charpos(system_charset_info,
375  table_options->comment().c_str(),
376  table_options->comment().c_str() +
377  table_options->comment().length(),
378  TABLE_COMMENT_MAXLEN);
379 
380  if (tmp_len < table_options->comment().length())
381  {
382  my_error(ER_WRONG_STRING_LENGTH, MYF(0),
383  table_options->comment().c_str(),"Table COMMENT",
384  (uint32_t) TABLE_COMMENT_MAXLEN);
385  return true;
386  }
387  }
388 
389  if (create_info->default_table_charset)
390  {
391  table_options->set_collation_id(create_info->default_table_charset->number);
392  table_options->set_collation(create_info->default_table_charset->name);
393  }
394 
395  if (create_info->used_fields & HA_CREATE_USED_AUTO)
396  table_options->set_has_user_set_auto_increment_value(true);
397  else
398  table_options->set_has_user_set_auto_increment_value(false);
399 
400  if (create_info->auto_increment_value)
401  table_options->set_auto_increment_value(create_info->auto_increment_value);
402 
403  for (uint32_t i= 0; i < keys; i++)
404  {
405  message::Table::Index *idx= table_proto.add_indexes();
406 
407  assert(test(key_info[i].flags & HA_USES_COMMENT) == (key_info[i].comment.size() > 0));
408 
409  idx->set_name(key_info[i].name);
410  idx->set_key_length(key_info[i].key_length);
411  idx->set_is_primary(is_primary_key(key_info[i].name));
412 
413  switch(key_info[i].algorithm)
414  {
415  case HA_KEY_ALG_HASH:
416  idx->set_type(message::Table::Index::HASH);
417  break;
418 
419  case HA_KEY_ALG_BTREE:
420  idx->set_type(message::Table::Index::BTREE);
421  break;
422 
423  case HA_KEY_ALG_UNDEF:
424  {
425  idx->set_type(create_info->db_type->default_index_type());
426  break;
427  }
428  }
429 
430  if (key_info[i].flags & HA_NOSAME)
431  idx->set_is_unique(true);
432  else
433  idx->set_is_unique(false);
434 
435  message::Table::Index::Options *index_options= idx->mutable_options();
436 
437  if (key_info[i].flags & HA_USES_BLOCK_SIZE)
438  index_options->set_key_block_size(key_info[i].block_size);
439 
440  if (key_info[i].flags & HA_PACK_KEY)
441  index_options->set_pack_key(true);
442 
443  if (key_info[i].flags & HA_BINARY_PACK_KEY)
444  index_options->set_binary_pack_key(true);
445 
446  if (key_info[i].flags & HA_VAR_LENGTH_PART)
447  index_options->set_var_length_key(true);
448 
449  if (key_info[i].flags & HA_NULL_PART_KEY)
450  index_options->set_null_part_key(true);
451 
452  if (key_info[i].flags & HA_KEY_HAS_PART_KEY_SEG)
453  index_options->set_has_partial_segments(true);
454 
455  if (key_info[i].flags & HA_GENERATED_KEY)
456  index_options->set_auto_generated_key(true);
457 
458  if (key_info[i].flags & HA_USES_COMMENT)
459  {
460  uint32_t tmp_len;
461  tmp_len= system_charset_info->cset->charpos(system_charset_info,
462  key_info[i].comment.begin(),
463  key_info[i].comment.end(),
464  TABLE_COMMENT_MAXLEN);
465 
466  if (tmp_len < key_info[i].comment.size())
467  {
468  my_error(ER_WRONG_STRING_LENGTH, MYF(0),
469  key_info[i].comment.data(), "Index COMMENT",
470  (uint32_t) TABLE_COMMENT_MAXLEN);
471  return true;
472  }
473 
474  idx->set_comment(key_info[i].comment.data());
475  }
476  static const uint64_t unknown_index_flag= (HA_NOSAME | HA_PACK_KEY |
477  HA_USES_BLOCK_SIZE |
478  HA_BINARY_PACK_KEY |
479  HA_VAR_LENGTH_PART |
480  HA_NULL_PART_KEY |
481  HA_KEY_HAS_PART_KEY_SEG |
482  HA_GENERATED_KEY |
483  HA_USES_COMMENT);
484  if (key_info[i].flags & ~unknown_index_flag)
485  abort(); // Invalid (unknown) index flag.
486 
487  for(unsigned int j=0; j< key_info[i].key_parts; j++)
488  {
489  message::Table::Index::IndexPart *idxpart;
490  const int fieldnr= key_info[i].key_part[j].fieldnr;
491  int mbmaxlen= 1;
492 
493  idxpart= idx->add_index_part();
494 
495  idxpart->set_fieldnr(fieldnr);
496 
497  if (table_proto.field(fieldnr).type() == message::Table::Field::VARCHAR
498  || table_proto.field(fieldnr).type() == message::Table::Field::BLOB)
499  {
500  uint32_t collation_id;
501 
502  if (table_proto.field(fieldnr).string_options().has_collation_id())
503  collation_id= table_proto.field(fieldnr).string_options().collation_id();
504  else
505  collation_id= table_proto.options().collation_id();
506 
507  const charset_info_st *cs= get_charset(collation_id);
508 
509  mbmaxlen= cs->mbmaxlen;
510  }
511 
512  idxpart->set_compare_length(key_info[i].key_part[j].length / mbmaxlen);
513  }
514  }
515 
516  if (not table_proto.IsInitialized())
517  {
518  my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
519  table_proto.name().c_str(),
520  table_proto.InitializationErrorString().c_str());
521 
522  return true;
523  }
524 
525  /*
526  Here we test to see if we can validate the Table Message before we continue.
527  We do this by serializing the protobuffer.
528  */
529  {
530  string tmp_string;
531 
532  try {
533  table_proto.SerializeToString(&tmp_string);
534  }
535 
536  catch (...)
537  {
538  my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
539  table_proto.name().c_str(),
540  table_proto.InitializationErrorString().c_str());
541 
542  return true;
543  }
544  }
545 
546  return false;
547 }
548 
549 /*
550  Create a table definition proto file and the tables
551 
552  SYNOPSIS
553  rea_create_table()
554  session Thread handler
555  path Name of file (including database, without .frm)
556  db Data base name
557  table_name Table name
558  create_info create info parameters
559  create_fields Fields to create
560  keys number of keys to create
561  key_info Keys to create
562 
563  RETURN
564  0 ok
565  1 error
566 */
567 
568 bool rea_create_table(Session *session,
569  const identifier::Table &identifier,
570  message::Table &table_proto,
571  HA_CREATE_INFO *create_info,
572  List<CreateField> &create_fields,
573  uint32_t keys, KeyInfo *key_info)
574 {
575  assert(table_proto.has_name());
576 
577  if (fill_table_proto(identifier,
578  table_proto, create_fields, create_info,
579  keys, key_info))
580  {
581  return false;
582  }
583 
584  assert(table_proto.name() == identifier.getTableName());
585 
586  if (not plugin::StorageEngine::createTable(*session,
587  identifier,
588  table_proto))
589  {
590  return false;
591  }
592 
593  return true;
594 
595 } /* rea_create_table */
596 
597 } /* namespace drizzled */
598 
TODO: Rename this file - func.h is stupid.