Drizzled Public API Documentation

statement_transform.cc
Go to the documentation of this file.
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  * Copyright (C) 2010 Jay Pipes
6  *
7  * Authors:
8  *
9  * Jay Pipes <jaypipes@gmail.com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; version 2 of the License.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23  */
24 
32 #include <config.h>
33 
34 #include <boost/lexical_cast.hpp>
35 
36 #include <drizzled/charset.h>
37 #include <drizzled/message.h>
39 #include <drizzled/message/transaction.pb.h>
40 #include <drizzled/message/access.h>
41 
42 #include <string>
43 #include <vector>
44 #include <sstream>
45 #include <cstdio>
46 
47 using namespace std;
48 
49 namespace drizzled
50 {
51 
52 namespace message
53 {
54 
55 static void escapeEmbeddedQuotes(string &s, const char quote='\'')
56 {
57  string::iterator it;
58 
59  for (it= s.begin(); it != s.end(); ++it)
60  {
61  if (*it == quote)
62  {
63  it= s.insert(it, quote);
64  ++it; // advance back to the quote
65  }
66  }
67 }
68 
69 /* Incredibly similar to append_unescaped() in table.cc, but for std::string */
70 static void append_escaped_string(std::string *res, const std::string &input, const char quote='\'')
71 {
72  const char *pos= input.c_str();
73  const char *end= input.c_str()+input.length();
74  res->push_back(quote);
75 
76  for (; pos != end ; pos++)
77  {
78  uint32_t mblen;
79  if (use_mb(default_charset_info) &&
80  (mblen= my_ismbchar(default_charset_info, pos, end)))
81  {
82  res->append(pos, mblen);
83  pos+= mblen - 1;
84  if (pos >= end)
85  break;
86  continue;
87  }
88 
89  switch (*pos) {
90  case 0: /* Must be escaped for 'mysql' */
91  res->push_back('\\');
92  res->push_back('0');
93  break;
94  case '\n': /* Must be escaped for logs */
95  res->push_back('\\');
96  res->push_back('n');
97  break;
98  case '\r':
99  res->push_back('\\'); /* This gives better readability */
100  res->push_back('r');
101  break;
102  case '\\':
103  res->push_back('\\'); /* Because of the sql syntax */
104  res->push_back('\\');
105  break;
106  default:
107  if (*pos == quote) /* SQL syntax for quoting a quote */
108  {
109  res->push_back(quote);
110  res->push_back(quote);
111  }
112  else
113  res->push_back(*pos);
114  break;
115  }
116  }
117  res->push_back(quote);
118 }
119 
120 enum TransformSqlError
121 transformStatementToSql(const Statement &source,
122  vector<string> &sql_strings,
123  enum TransformSqlVariant sql_variant,
124  bool already_in_transaction)
125 {
126  TransformSqlError error= NONE;
127 
128  switch (source.type())
129  {
130  case Statement::ROLLBACK_STATEMENT:
131  {
132  break;
133  }
134  case Statement::ROLLBACK:
135  {
136  sql_strings.push_back("ROLLBACK");
137  break;
138  }
139  case Statement::INSERT:
140  {
141  if (! source.has_insert_header())
142  {
143  error= MISSING_HEADER;
144  return error;
145  }
146  if (! source.has_insert_data())
147  {
148  error= MISSING_DATA;
149  return error;
150  }
151 
152  const InsertHeader &insert_header= source.insert_header();
153  const InsertData &insert_data= source.insert_data();
154  size_t num_keys= insert_data.record_size();
155 
156  if (num_keys > 1 && ! already_in_transaction)
157  sql_strings.push_back("START TRANSACTION");
158 
159  for (size_t x= 0; x < num_keys; ++x)
160  {
161  string destination;
162 
163  error= transformInsertRecordToSql(insert_header,
164  insert_data.record(x),
165  destination,
166  sql_variant);
167  if (error != NONE)
168  break;
169 
170  sql_strings.push_back(destination);
171  }
172 
173  if (num_keys > 1 && ! already_in_transaction)
174  {
175  if (error == NONE)
176  sql_strings.push_back("COMMIT");
177  else
178  sql_strings.push_back("ROLLBACK");
179  }
180  }
181  break;
182  case Statement::UPDATE:
183  {
184  if (! source.has_update_header())
185  {
186  error= MISSING_HEADER;
187  return error;
188  }
189  if (! source.has_update_data())
190  {
191  error= MISSING_DATA;
192  return error;
193  }
194 
195  const UpdateHeader &update_header= source.update_header();
196  const UpdateData &update_data= source.update_data();
197  size_t num_keys= update_data.record_size();
198  size_t x;
199 
200  if (num_keys > 1 && ! already_in_transaction)
201  sql_strings.push_back("START TRANSACTION");
202 
203  for (x= 0; x < num_keys; ++x)
204  {
205  string destination;
206 
207  error= transformUpdateRecordToSql(update_header,
208  update_data.record(x),
209  destination,
210  sql_variant);
211  if (error != NONE)
212  break;
213 
214  sql_strings.push_back(destination);
215  }
216 
217  if (num_keys > 1 && ! already_in_transaction)
218  {
219  if (error == NONE)
220  sql_strings.push_back("COMMIT");
221  else
222  sql_strings.push_back("ROLLBACK");
223  }
224  }
225  break;
226  case Statement::DELETE:
227  {
228  if (! source.has_delete_header())
229  {
230  error= MISSING_HEADER;
231  return error;
232  }
233  if (! source.has_delete_data())
234  {
235  error= MISSING_DATA;
236  return error;
237  }
238 
239  const DeleteHeader &delete_header= source.delete_header();
240  const DeleteData &delete_data= source.delete_data();
241  size_t num_keys= delete_data.record_size();
242  size_t x;
243 
244  if (num_keys > 1 && ! already_in_transaction)
245  sql_strings.push_back("START TRANSACTION");
246 
247  for (x= 0; x < num_keys; ++x)
248  {
249  string destination;
250 
251  error= transformDeleteRecordToSql(delete_header,
252  delete_data.record(x),
253  destination,
254  sql_variant);
255  if (error != NONE)
256  break;
257 
258  sql_strings.push_back(destination);
259  }
260 
261  if (num_keys > 1 && ! already_in_transaction)
262  {
263  if (error == NONE)
264  sql_strings.push_back("COMMIT");
265  else
266  sql_strings.push_back("ROLLBACK");
267  }
268  }
269  break;
270  case Statement::CREATE_TABLE:
271  {
272  assert(source.has_create_table_statement());
273  string destination;
274  error= transformCreateTableStatementToSql(source.create_table_statement(),
275  destination,
276  sql_variant);
277  sql_strings.push_back(destination);
278  }
279  break;
280  case Statement::TRUNCATE_TABLE:
281  {
282  assert(source.has_truncate_table_statement());
283  string destination;
284  error= transformTruncateTableStatementToSql(source.truncate_table_statement(),
285  destination,
286  sql_variant);
287  sql_strings.push_back(destination);
288  }
289  break;
290  case Statement::DROP_TABLE:
291  {
292  assert(source.has_drop_table_statement());
293  string destination;
294  error= transformDropTableStatementToSql(source.drop_table_statement(),
295  destination,
296  sql_variant);
297  sql_strings.push_back(destination);
298  }
299  break;
300  case Statement::CREATE_SCHEMA:
301  {
302  assert(source.has_create_schema_statement());
303  string destination;
304  error= transformCreateSchemaStatementToSql(source.create_schema_statement(),
305  destination,
306  sql_variant);
307  sql_strings.push_back(destination);
308  }
309  break;
310  case Statement::DROP_SCHEMA:
311  {
312  assert(source.has_drop_schema_statement());
313  string destination;
314  error= transformDropSchemaStatementToSql(source.drop_schema_statement(),
315  destination,
316  sql_variant);
317  sql_strings.push_back(destination);
318  }
319  break;
320  case Statement::ALTER_SCHEMA:
321  {
322  assert(source.has_alter_schema_statement());
323  string destination;
324  error= transformAlterSchemaStatementToSql(source.alter_schema_statement(),
325  destination,
326  sql_variant);
327  sql_strings.push_back(destination);
328  }
329  break;
330  case Statement::SET_VARIABLE:
331  {
332  assert(source.has_set_variable_statement());
333  string destination;
334  error= transformSetVariableStatementToSql(source.set_variable_statement(),
335  destination,
336  sql_variant);
337  sql_strings.push_back(destination);
338  }
339  break;
340  case Statement::RAW_SQL:
341  {
342  if (source.has_raw_sql_schema())
343  {
344  string destination("USE ");
345  destination.append(source.raw_sql_schema());
346  sql_strings.push_back(destination);
347  }
348  sql_strings.push_back(source.sql());
349  }
350  break;
351  default:
352  sql_strings.push_back(source.sql());
353  break;
354  }
355  return error;
356 }
357 
358 enum TransformSqlError
359 transformInsertHeaderToSql(const InsertHeader &header,
360  string &destination,
361  enum TransformSqlVariant sql_variant)
362 {
363  char quoted_identifier= '`';
364  if (sql_variant == ANSI)
365  quoted_identifier= '"';
366 
367  destination.assign("INSERT INTO ", 12);
368  destination.push_back(quoted_identifier);
369  destination.append(header.table_metadata().schema_name());
370  destination.push_back(quoted_identifier);
371  destination.push_back('.');
372  destination.push_back(quoted_identifier);
373  destination.append(header.table_metadata().table_name());
374  destination.push_back(quoted_identifier);
375  destination.append(" (", 2);
376 
377  /* Add field list to SQL string... */
378  size_t num_fields= header.field_metadata_size();
379  size_t x;
380 
381  for (x= 0; x < num_fields; ++x)
382  {
383  const FieldMetadata &field_metadata= header.field_metadata(x);
384  if (x != 0)
385  destination.push_back(',');
386 
387  destination.push_back(quoted_identifier);
388  destination.append(field_metadata.name());
389  destination.push_back(quoted_identifier);
390  }
391 
392  return NONE;
393 }
394 
395 enum TransformSqlError
396 transformInsertRecordToSql(const InsertHeader &header,
397  const InsertRecord &record,
398  string &destination,
399  enum TransformSqlVariant sql_variant)
400 {
401  enum TransformSqlError error= transformInsertHeaderToSql(header,
402  destination,
403  sql_variant);
404 
405  destination.append(") VALUES (");
406 
407  /* Add insert values */
408  size_t num_fields= header.field_metadata_size();
409  size_t x;
410  bool should_quote_field_value= false;
411 
412  for (x= 0; x < num_fields; ++x)
413  {
414  if (x != 0)
415  destination.push_back(',');
416 
417  const FieldMetadata &field_metadata= header.field_metadata(x);
418 
419  if (record.is_null(x))
420  {
421  should_quote_field_value= false;
422  }
423  else
424  {
425  should_quote_field_value= shouldQuoteFieldValue(field_metadata.type());
426  }
427 
428  if (should_quote_field_value)
429  destination.push_back('\'');
430 
431  if (record.is_null(x))
432  {
433  destination.append("NULL");
434  }
435  else
436  {
437  if (field_metadata.type() == Table::Field::BLOB)
438  {
439  /*
440  * We do this here because BLOB data is returned
441  * in a string correctly, but calling append()
442  * without a length will result in only the string
443  * up to a \0 being output here.
444  */
445  string raw_data(record.insert_value(x));
446  destination.append(raw_data.c_str(), raw_data.size());
447  }
448  else
449  {
450  string tmp(record.insert_value(x));
451  escapeEmbeddedQuotes(tmp);
452  destination.append(tmp);
453  }
454  }
455 
456  if (should_quote_field_value)
457  destination.push_back('\'');
458  }
459  destination.push_back(')');
460 
461  return error;
462 }
463 
464 enum TransformSqlError
465 transformInsertStatementToSql(const InsertHeader &header,
466  const InsertData &data,
467  string &destination,
468  enum TransformSqlVariant sql_variant)
469 {
470  enum TransformSqlError error= transformInsertHeaderToSql(header,
471  destination,
472  sql_variant);
473 
474  destination.append(") VALUES (", 10);
475 
476  /* Add insert values */
477  size_t num_records= data.record_size();
478  size_t num_fields= header.field_metadata_size();
479  size_t x, y;
480  bool should_quote_field_value= false;
481 
482  for (x= 0; x < num_records; ++x)
483  {
484  if (x != 0)
485  destination.append("),(", 3);
486 
487  for (y= 0; y < num_fields; ++y)
488  {
489  if (y != 0)
490  destination.push_back(',');
491 
492  const FieldMetadata &field_metadata= header.field_metadata(y);
493 
494  should_quote_field_value= shouldQuoteFieldValue(field_metadata.type());
495 
496  if (should_quote_field_value)
497  destination.push_back('\'');
498 
499  if (field_metadata.type() == Table::Field::BLOB)
500  {
501  /*
502  * We do this here because BLOB data is returned
503  * in a string correctly, but calling append()
504  * without a length will result in only the string
505  * up to a \0 being output here.
506  */
507  string raw_data(data.record(x).insert_value(y));
508  destination.append(raw_data.c_str(), raw_data.size());
509  }
510  else
511  {
512  string tmp(data.record(x).insert_value(y));
513  escapeEmbeddedQuotes(tmp);
514  destination.append(tmp);
515  }
516 
517  if (should_quote_field_value)
518  destination.push_back('\'');
519  }
520  }
521  destination.push_back(')');
522 
523  return error;
524 }
525 
526 enum TransformSqlError
527 transformUpdateHeaderToSql(const UpdateHeader &header,
528  string &destination,
529  enum TransformSqlVariant sql_variant)
530 {
531  char quoted_identifier= '`';
532  if (sql_variant == ANSI)
533  quoted_identifier= '"';
534 
535  destination.assign("UPDATE ", 7);
536  destination.push_back(quoted_identifier);
537  destination.append(header.table_metadata().schema_name());
538  destination.push_back(quoted_identifier);
539  destination.push_back('.');
540  destination.push_back(quoted_identifier);
541  destination.append(header.table_metadata().table_name());
542  destination.push_back(quoted_identifier);
543  destination.append(" SET ", 5);
544 
545  return NONE;
546 }
547 
548 enum TransformSqlError
549 transformUpdateRecordToSql(const UpdateHeader &header,
550  const UpdateRecord &record,
551  string &destination,
552  enum TransformSqlVariant sql_variant)
553 {
554  enum TransformSqlError error= transformUpdateHeaderToSql(header,
555  destination,
556  sql_variant);
557 
558  char quoted_identifier= '`';
559  if (sql_variant == ANSI)
560  quoted_identifier= '"';
561 
562  /* Add field SET list to SQL string... */
563  size_t num_set_fields= header.set_field_metadata_size();
564  size_t x;
565  bool should_quote_field_value= false;
566 
567  for (x= 0; x < num_set_fields; ++x)
568  {
569  const FieldMetadata &field_metadata= header.set_field_metadata(x);
570  if (x != 0)
571  destination.push_back(',');
572 
573  destination.push_back(quoted_identifier);
574  destination.append(field_metadata.name());
575  destination.push_back(quoted_identifier);
576  destination.push_back('=');
577 
578  if (record.is_null(x))
579  {
580  should_quote_field_value= false;
581  }
582  else
583  {
584  should_quote_field_value= shouldQuoteFieldValue(field_metadata.type());
585  }
586 
587  if (should_quote_field_value)
588  destination.push_back('\'');
589 
590  if (record.is_null(x))
591  {
592  destination.append("NULL");
593  }
594  else
595  {
596  if (field_metadata.type() == Table::Field::BLOB)
597  {
598  /*
599  * We do this here because BLOB data is returned
600  * in a string correctly, but calling append()
601  * without a length will result in only the string
602  * up to a \0 being output here.
603  */
604  string raw_data(record.after_value(x));
605  destination.append(raw_data.c_str(), raw_data.size());
606  }
607  else
608  {
609  string tmp(record.after_value(x));
610  escapeEmbeddedQuotes(tmp);
611  destination.append(tmp);
612  }
613  }
614 
615  if (should_quote_field_value)
616  destination.push_back('\'');
617  }
618 
619  size_t num_key_fields= header.key_field_metadata_size();
620 
621  destination.append(" WHERE ", 7);
622  for (x= 0; x < num_key_fields; ++x)
623  {
624  const FieldMetadata &field_metadata= header.key_field_metadata(x);
625 
626  if (x != 0)
627  destination.append(" AND ", 5); /* Always AND condition with a multi-column PK */
628 
629  destination.push_back(quoted_identifier);
630  destination.append(field_metadata.name());
631  destination.push_back(quoted_identifier);
632 
633  destination.push_back('=');
634 
635  should_quote_field_value= shouldQuoteFieldValue(field_metadata.type());
636 
637  if (should_quote_field_value)
638  destination.push_back('\'');
639 
640  if (field_metadata.type() == Table::Field::BLOB)
641  {
642  /*
643  * We do this here because BLOB data is returned
644  * in a string correctly, but calling append()
645  * without a length will result in only the string
646  * up to a \0 being output here.
647  */
648  string raw_data(record.key_value(x));
649  destination.append(raw_data.c_str(), raw_data.size());
650  }
651  else
652  {
653  destination.append(record.key_value(x));
654  }
655 
656  if (should_quote_field_value)
657  destination.push_back('\'');
658  }
659 
660  return error;
661 }
662 
663 enum TransformSqlError
664 transformDeleteHeaderToSql(const DeleteHeader &header,
665  string &destination,
666  enum TransformSqlVariant sql_variant)
667 {
668  char quoted_identifier= '`';
669  if (sql_variant == ANSI)
670  quoted_identifier= '"';
671 
672  destination.assign("DELETE FROM ", 12);
673  destination.push_back(quoted_identifier);
674  destination.append(header.table_metadata().schema_name());
675  destination.push_back(quoted_identifier);
676  destination.push_back('.');
677  destination.push_back(quoted_identifier);
678  destination.append(header.table_metadata().table_name());
679  destination.push_back(quoted_identifier);
680 
681  return NONE;
682 }
683 
684 enum TransformSqlError
685 transformDeleteRecordToSql(const DeleteHeader &header,
686  const DeleteRecord &record,
687  string &destination,
688  enum TransformSqlVariant sql_variant)
689 {
690  enum TransformSqlError error= transformDeleteHeaderToSql(header,
691  destination,
692  sql_variant);
693  char quoted_identifier= '`';
694  if (sql_variant == ANSI)
695  quoted_identifier= '"';
696 
697  /* Add WHERE clause to SQL string... */
698  uint32_t num_key_fields= header.key_field_metadata_size();
699  uint32_t x;
700  bool should_quote_field_value= false;
701 
702  destination.append(" WHERE ", 7);
703  for (x= 0; x < num_key_fields; ++x)
704  {
705  const FieldMetadata &field_metadata= header.key_field_metadata(x);
706 
707  if (x != 0)
708  destination.append(" AND ", 5); /* Always AND condition with a multi-column PK */
709 
710  destination.push_back(quoted_identifier);
711  destination.append(field_metadata.name());
712  destination.push_back(quoted_identifier);
713 
714  destination.push_back('=');
715 
716  should_quote_field_value= shouldQuoteFieldValue(field_metadata.type());
717 
718  if (should_quote_field_value)
719  destination.push_back('\'');
720 
721  if (field_metadata.type() == Table::Field::BLOB)
722  {
723  /*
724  * We do this here because BLOB data is returned
725  * in a string correctly, but calling append()
726  * without a length will result in only the string
727  * up to a \0 being output here.
728  */
729  string raw_data(record.key_value(x));
730  destination.append(raw_data.c_str(), raw_data.size());
731  }
732  else
733  {
734  string tmp(record.key_value(x));
735  escapeEmbeddedQuotes(tmp);
736  destination.append(tmp);
737  }
738 
739  if (should_quote_field_value)
740  destination.push_back('\'');
741  }
742 
743  return error;
744 }
745 
746 enum TransformSqlError
747 transformDeleteStatementToSql(const DeleteHeader &header,
748  const DeleteData &data,
749  string &destination,
750  enum TransformSqlVariant sql_variant)
751 {
752  enum TransformSqlError error= transformDeleteHeaderToSql(header,
753  destination,
754  sql_variant);
755  char quoted_identifier= '`';
756  if (sql_variant == ANSI)
757  quoted_identifier= '"';
758 
759  /* Add WHERE clause to SQL string... */
760  uint32_t num_key_fields= header.key_field_metadata_size();
761  uint32_t num_key_records= data.record_size();
762  uint32_t x, y;
763  bool should_quote_field_value= false;
764 
765  destination.append(" WHERE ", 7);
766  for (x= 0; x < num_key_records; ++x)
767  {
768  if (x != 0)
769  destination.append(" OR ", 4); /* Always OR condition for multiple key records */
770 
771  if (num_key_fields > 1)
772  destination.push_back('(');
773 
774  for (y= 0; y < num_key_fields; ++y)
775  {
776  const FieldMetadata &field_metadata= header.key_field_metadata(y);
777 
778  if (y != 0)
779  destination.append(" AND ", 5); /* Always AND condition with a multi-column PK */
780 
781  destination.push_back(quoted_identifier);
782  destination.append(field_metadata.name());
783  destination.push_back(quoted_identifier);
784 
785  destination.push_back('=');
786 
787  should_quote_field_value= shouldQuoteFieldValue(field_metadata.type());
788 
789  if (should_quote_field_value)
790  destination.push_back('\'');
791 
792  if (field_metadata.type() == Table::Field::BLOB)
793  {
794  /*
795  * We do this here because BLOB data is returned
796  * in a string correctly, but calling append()
797  * without a length will result in only the string
798  * up to a \0 being output here.
799  */
800  string raw_data(data.record(x).key_value(y));
801  destination.append(raw_data.c_str(), raw_data.size());
802  }
803  else
804  {
805  string tmp(data.record(x).key_value(y));
806  escapeEmbeddedQuotes(tmp);
807  destination.append(tmp);
808  }
809 
810  if (should_quote_field_value)
811  destination.push_back('\'');
812  }
813  if (num_key_fields > 1)
814  destination.push_back(')');
815  }
816  return error;
817 }
818 
819 enum TransformSqlError
820 transformAlterSchemaStatementToSql(const AlterSchemaStatement &statement,
821  string &destination,
822  enum TransformSqlVariant sql_variant)
823 {
824  const Schema &before= statement.before();
825  const Schema &after= statement.after();
826 
827  /* Make sure we are given the before and after for the same object */
828  if (before.uuid() != after.uuid())
829  return UUID_MISMATCH;
830 
831  char quoted_identifier= '`';
832  if (sql_variant == ANSI)
833  quoted_identifier= '"';
834 
835  destination.append("ALTER SCHEMA ");
836  destination.push_back(quoted_identifier);
837  destination.append(before.name());
838  destination.push_back(quoted_identifier);
839 
840  /*
841  * Diff our schemas. Currently, only collation can change so a
842  * diff of the two structures is not really necessary.
843  */
844  destination.append(" COLLATE = ");
845  destination.append(after.collation());
846 
847  return NONE;
848 }
849 
850 enum TransformSqlError
851 transformDropSchemaStatementToSql(const DropSchemaStatement &statement,
852  string &destination,
853  enum TransformSqlVariant sql_variant)
854 {
855  char quoted_identifier= '`';
856  if (sql_variant == ANSI)
857  quoted_identifier= '"';
858 
859  destination.append("DROP SCHEMA ", 12);
860  destination.push_back(quoted_identifier);
861  destination.append(statement.schema_name());
862  destination.push_back(quoted_identifier);
863 
864  return NONE;
865 }
866 
867 enum TransformSqlError
868 transformCreateSchemaStatementToSql(const CreateSchemaStatement &statement,
869  string &destination,
870  enum TransformSqlVariant sql_variant)
871 {
872  char quoted_identifier= '`';
873  if (sql_variant == ANSI)
874  quoted_identifier= '"';
875 
876  const Schema &schema= statement.schema();
877 
878  destination.append("CREATE SCHEMA ");
879  destination.push_back(quoted_identifier);
880  destination.append(schema.name());
881  destination.push_back(quoted_identifier);
882 
883  if (schema.has_collation())
884  {
885  destination.append(" COLLATE ");
886  destination.append(schema.collation());
887  }
888 
889  if (not message::is_replicated(schema))
890  {
891  destination.append(" REPLICATE = FALSE");
892  }
893 
894  if (message::has_definer(schema))
895  {
896  destination.append(" DEFINER ");
897  destination.push_back('\'');
898  destination.append(message::definer(schema));
899  destination.push_back('\'');
900  }
901 
902  return NONE;
903 }
904 
905 enum TransformSqlError
906 transformDropTableStatementToSql(const DropTableStatement &statement,
907  string &destination,
908  enum TransformSqlVariant sql_variant)
909 {
910  char quoted_identifier= '`';
911  if (sql_variant == ANSI)
912  quoted_identifier= '"';
913 
914  const TableMetadata &table_metadata= statement.table_metadata();
915 
916  destination.append("DROP TABLE ");
917 
918  /* Add the IF EXISTS clause if necessary */
919  if (statement.has_if_exists_clause() &&
920  statement.if_exists_clause() == true)
921  {
922  destination.append("IF EXISTS ");
923  }
924 
925  destination.push_back(quoted_identifier);
926  destination.append(table_metadata.schema_name());
927  destination.push_back(quoted_identifier);
928  destination.push_back('.');
929  destination.push_back(quoted_identifier);
930  destination.append(table_metadata.table_name());
931  destination.push_back(quoted_identifier);
932 
933  return NONE;
934 }
935 
936 enum TransformSqlError
937 transformTruncateTableStatementToSql(const TruncateTableStatement &statement,
938  string &destination,
939  enum TransformSqlVariant sql_variant)
940 {
941  char quoted_identifier= '`';
942  if (sql_variant == ANSI)
943  quoted_identifier= '"';
944 
945  const TableMetadata &table_metadata= statement.table_metadata();
946 
947  destination.append("TRUNCATE TABLE ");
948  destination.push_back(quoted_identifier);
949  destination.append(table_metadata.schema_name());
950  destination.push_back(quoted_identifier);
951  destination.push_back('.');
952  destination.push_back(quoted_identifier);
953  destination.append(table_metadata.table_name());
954  destination.push_back(quoted_identifier);
955 
956  return NONE;
957 }
958 
959 enum TransformSqlError
960 transformSetVariableStatementToSql(const SetVariableStatement &statement,
961  string &destination,
962  enum TransformSqlVariant sql_variant)
963 {
964  (void) sql_variant;
965  const FieldMetadata &variable_metadata= statement.variable_metadata();
966  bool should_quote_field_value= shouldQuoteFieldValue(variable_metadata.type());
967 
968  destination.append("SET GLOBAL "); /* Only global variables are replicated */
969  destination.append(variable_metadata.name());
970  destination.push_back('=');
971 
972  if (should_quote_field_value)
973  destination.push_back('\'');
974 
975  destination.append(statement.variable_value());
976 
977  if (should_quote_field_value)
978  destination.push_back('\'');
979 
980  return NONE;
981 }
982 
983 enum TransformSqlError
984 transformCreateTableStatementToSql(const CreateTableStatement &statement,
985  string &destination,
986  enum TransformSqlVariant sql_variant)
987 {
988  return transformTableDefinitionToSql(statement.table(), destination, sql_variant);
989 }
990 
991 enum TransformSqlError
992 transformTableDefinitionToSql(const Table &table,
993  string &destination,
994  enum TransformSqlVariant sql_variant, bool with_schema)
995 {
996  char quoted_identifier= '`';
997  if (sql_variant == ANSI)
998  quoted_identifier= '"';
999 
1000  destination.append("CREATE ");
1001 
1002  if (table.type() == Table::TEMPORARY)
1003  destination.append("TEMPORARY ");
1004 
1005  destination.append("TABLE ");
1006  if (with_schema)
1007  {
1008  append_escaped_string(&destination, table.schema(), quoted_identifier);
1009  destination.push_back('.');
1010  }
1011  append_escaped_string(&destination, table.name(), quoted_identifier);
1012  destination.append(" (\n");
1013 
1014  enum TransformSqlError result= NONE;
1015  size_t num_fields= table.field_size();
1016  for (size_t x= 0; x < num_fields; ++x)
1017  {
1018  const Table::Field &field= table.field(x);
1019 
1020  if (x != 0)
1021  destination.append(",\n");
1022 
1023  destination.append(" ");
1024 
1025  result= transformFieldDefinitionToSql(field, destination, sql_variant);
1026 
1027  if (result != NONE)
1028  return result;
1029  }
1030 
1031  size_t num_indexes= table.indexes_size();
1032 
1033  if (num_indexes > 0)
1034  destination.append(",\n");
1035 
1036  for (size_t x= 0; x < num_indexes; ++x)
1037  {
1038  const message::Table::Index &index= table.indexes(x);
1039 
1040  if (x != 0)
1041  destination.append(",\n");
1042 
1043  result= transformIndexDefinitionToSql(index, table, destination, sql_variant);
1044 
1045  if (result != NONE)
1046  return result;
1047  }
1048 
1049  size_t num_foreign_keys= table.fk_constraint_size();
1050 
1051  if (num_foreign_keys > 0)
1052  destination.append(",\n");
1053 
1054  for (size_t x= 0; x < num_foreign_keys; ++x)
1055  {
1056  const message::Table::ForeignKeyConstraint &fkey= table.fk_constraint(x);
1057 
1058  if (x != 0)
1059  destination.append(",\n");
1060 
1061  result= transformForeignKeyConstraintDefinitionToSql(fkey, table, destination, sql_variant);
1062 
1063  if (result != NONE)
1064  return result;
1065  }
1066 
1067  destination.append("\n)");
1068 
1069  /* Add ENGINE = " clause */
1070  if (table.has_engine())
1071  {
1072  destination.append(" ENGINE=");
1073  destination.append(table.engine().name());
1074 
1075  size_t num_engine_options= table.engine().options_size();
1076  if (num_engine_options > 0)
1077  destination.append(" ", 1);
1078  for (size_t x= 0; x < num_engine_options; ++x)
1079  {
1080  const Engine::Option &option= table.engine().options(x);
1081  destination.append(option.name());
1082  destination.append("='");
1083  destination.append(option.state());
1084  destination.append("'");
1085  if (x != num_engine_options-1)
1086  {
1087  destination.append(", ");
1088  }
1089  }
1090  }
1091 
1092  if (table.has_options())
1093  (void) transformTableOptionsToSql(table.options(), destination, sql_variant);
1094 
1095  if (not message::is_replicated(table))
1096  {
1097  destination.append(" REPLICATE = FALSE");
1098  }
1099 
1100  if (message::has_definer(table))
1101  {
1102  destination.append(" DEFINER ");
1103  destination.push_back('\'');
1104  destination.append(message::definer(table));
1105  destination.push_back('\'');
1106  }
1107 
1108  return NONE;
1109 }
1110 
1111 enum TransformSqlError
1112 transformTableOptionsToSql(const Table::TableOptions &options,
1113  string &destination,
1114  enum TransformSqlVariant sql_variant)
1115 {
1116  if (sql_variant == ANSI)
1117  return NONE; /* ANSI does not support table options... */
1118 
1119  if (options.has_comment())
1120  {
1121  destination.append(" COMMENT=");
1122  append_escaped_string(&destination, options.comment());
1123  }
1124 
1125  if (options.has_collation())
1126  {
1127  destination.append(" COLLATE = ");
1128  destination.append(options.collation());
1129  }
1130 
1131  if (options.has_data_file_name())
1132  {
1133  destination.append("\nDATA_FILE_NAME = '");
1134  destination.append(options.data_file_name());
1135  destination.push_back('\'');
1136  }
1137 
1138  if (options.has_index_file_name())
1139  {
1140  destination.append("\nINDEX_FILE_NAME = '");
1141  destination.append(options.index_file_name());
1142  destination.push_back('\'');
1143  }
1144 
1145  if (options.has_max_rows())
1146  {
1147  destination.append("\nMAX_ROWS = ");
1148  destination.append(boost::lexical_cast<string>(options.max_rows()));
1149  }
1150 
1151  if (options.has_min_rows())
1152  {
1153  destination.append("\nMIN_ROWS = ");
1154  destination.append(boost::lexical_cast<string>(options.min_rows()));
1155  }
1156 
1157  if (options.has_user_set_auto_increment_value()
1158  && options.has_auto_increment_value())
1159  {
1160  destination.append(" AUTO_INCREMENT=");
1161  destination.append(boost::lexical_cast<string>(options.auto_increment_value()));
1162  }
1163 
1164  if (options.has_avg_row_length())
1165  {
1166  destination.append("\nAVG_ROW_LENGTH = ");
1167  destination.append(boost::lexical_cast<string>(options.avg_row_length()));
1168  }
1169 
1170  if (options.has_checksum() && options.checksum())
1171  destination.append("\nCHECKSUM = TRUE");
1172 
1173  if (options.has_page_checksum() && options.page_checksum())
1174  destination.append("\nPAGE_CHECKSUM = TRUE");
1175 
1176  return NONE;
1177 }
1178 
1179 enum TransformSqlError
1180 transformIndexDefinitionToSql(const Table::Index &index,
1181  const Table &table,
1182  string &destination,
1183  enum TransformSqlVariant sql_variant)
1184 {
1185  char quoted_identifier= '`';
1186  if (sql_variant == ANSI)
1187  quoted_identifier= '"';
1188 
1189  destination.append(" ", 2);
1190 
1191  if (index.is_primary())
1192  destination.append("PRIMARY ");
1193  else if (index.is_unique())
1194  destination.append("UNIQUE ");
1195 
1196  destination.append("KEY ", 4);
1197  if (! (index.is_primary() && index.name().compare("PRIMARY")==0))
1198  {
1199  destination.push_back(quoted_identifier);
1200  destination.append(index.name());
1201  destination.push_back(quoted_identifier);
1202  destination.append(" (", 2);
1203  }
1204  else
1205  destination.append("(", 1);
1206 
1207  size_t num_parts= index.index_part_size();
1208  for (size_t x= 0; x < num_parts; ++x)
1209  {
1210  const Table::Index::IndexPart &part= index.index_part(x);
1211  const Table::Field &field= table.field(part.fieldnr());
1212 
1213  if (x != 0)
1214  destination.push_back(',');
1215 
1216  destination.push_back(quoted_identifier);
1217  destination.append(field.name());
1218  destination.push_back(quoted_identifier);
1219 
1220  /*
1221  * If the index part's field type is VARCHAR or TEXT
1222  * then check for a prefix length then is different
1223  * from the field's full length...
1224  */
1225  if (field.type() == Table::Field::VARCHAR ||
1226  field.type() == Table::Field::BLOB)
1227  {
1228  if (part.has_compare_length())
1229  {
1230  if (part.compare_length() != field.string_options().length())
1231  {
1232  destination.push_back('(');
1233  destination.append(boost::lexical_cast<string>(part.compare_length()));
1234  destination.push_back(')');
1235  }
1236  }
1237  }
1238  }
1239  destination.push_back(')');
1240 
1241  switch (index.type())
1242  {
1243  case Table::Index::UNKNOWN_INDEX:
1244  break;
1245  case Table::Index::BTREE:
1246  destination.append(" USING BTREE");
1247  break;
1248  case Table::Index::RTREE:
1249  destination.append(" USING RTREE");
1250  break;
1251  case Table::Index::HASH:
1252  destination.append(" USING HASH");
1253  break;
1254  case Table::Index::FULLTEXT:
1255  destination.append(" USING FULLTEXT");
1256  break;
1257  }
1258 
1259  if (index.has_comment())
1260  {
1261  destination.append(" COMMENT ");
1262  append_escaped_string(&destination, index.comment());
1263  }
1264 
1265  return NONE;
1266 }
1267 
1268 static void transformForeignKeyOptionToSql(Table::ForeignKeyConstraint::ForeignKeyOption opt, string &destination)
1269 {
1270  switch (opt)
1271  {
1272  case Table::ForeignKeyConstraint::OPTION_RESTRICT:
1273  destination.append("RESTRICT");
1274  break;
1275  case Table::ForeignKeyConstraint::OPTION_CASCADE:
1276  destination.append("CASCADE");
1277  break;
1278  case Table::ForeignKeyConstraint::OPTION_SET_NULL:
1279  destination.append("SET NULL");
1280  break;
1281  case Table::ForeignKeyConstraint::OPTION_UNDEF:
1282  case Table::ForeignKeyConstraint::OPTION_NO_ACTION:
1283  destination.append("NO ACTION");
1284  break;
1285  case Table::ForeignKeyConstraint::OPTION_SET_DEFAULT:
1286  destination.append("SET DEFAULT");
1287  break;
1288  }
1289 }
1290 
1291 enum TransformSqlError
1292 transformForeignKeyConstraintDefinitionToSql(const Table::ForeignKeyConstraint &fkey,
1293  const Table &,
1294  string &destination,
1295  enum TransformSqlVariant sql_variant)
1296 {
1297  char quoted_identifier= '`';
1298  if (sql_variant == ANSI)
1299  quoted_identifier= '"';
1300 
1301  destination.append(" ");
1302 
1303  if (fkey.has_name())
1304  {
1305  destination.append("CONSTRAINT ");
1306  append_escaped_string(&destination, fkey.name(), quoted_identifier);
1307  destination.append(" ", 1);
1308  }
1309 
1310  destination.append("FOREIGN KEY (");
1311 
1312  for (ssize_t x= 0; x < fkey.column_names_size(); ++x)
1313  {
1314  if (x != 0)
1315  destination.append(", ");
1316 
1317  append_escaped_string(&destination, fkey.column_names(x),
1318  quoted_identifier);
1319  }
1320 
1321  destination.append(") REFERENCES ");
1322 
1323  append_escaped_string(&destination, fkey.references_table_name(),
1324  quoted_identifier);
1325  destination.append(" (");
1326 
1327  for (ssize_t x= 0; x < fkey.references_columns_size(); ++x)
1328  {
1329  if (x != 0)
1330  destination.append(", ");
1331 
1332  append_escaped_string(&destination, fkey.references_columns(x),
1333  quoted_identifier);
1334  }
1335 
1336  destination.push_back(')');
1337 
1338  if (fkey.has_update_option() and fkey.update_option() != Table::ForeignKeyConstraint::OPTION_UNDEF)
1339  {
1340  destination.append(" ON UPDATE ");
1341  transformForeignKeyOptionToSql(fkey.update_option(), destination);
1342  }
1343 
1344  if (fkey.has_delete_option() and fkey.delete_option() != Table::ForeignKeyConstraint::OPTION_UNDEF)
1345  {
1346  destination.append(" ON DELETE ");
1347  transformForeignKeyOptionToSql(fkey.delete_option(), destination);
1348  }
1349 
1350  return NONE;
1351 }
1352 
1353 enum TransformSqlError
1354 transformFieldDefinitionToSql(const Table::Field &field,
1355  string &destination,
1356  enum TransformSqlVariant sql_variant)
1357 {
1358  char quoted_identifier= '`';
1359  char quoted_default;
1360 
1361  if (sql_variant == ANSI)
1362  quoted_identifier= '"';
1363 
1364  if (sql_variant == DRIZZLE)
1365  quoted_default= '\'';
1366  else
1367  quoted_default= quoted_identifier;
1368 
1369  append_escaped_string(&destination, field.name(), quoted_identifier);
1370 
1371  Table::Field::FieldType field_type= field.type();
1372 
1373  switch (field_type)
1374  {
1375  case Table::Field::DOUBLE:
1376  destination.append(" DOUBLE");
1377  if (field.has_numeric_options()
1378  && field.numeric_options().has_precision())
1379  {
1380  stringstream ss;
1381  ss << "(" << field.numeric_options().precision() << ",";
1382  ss << field.numeric_options().scale() << ")";
1383  destination.append(ss.str());
1384  }
1385  break;
1386  case Table::Field::VARCHAR:
1387  {
1388  if (field.string_options().has_collation()
1389  && field.string_options().collation().compare("binary") == 0)
1390  destination.append(" VARBINARY(");
1391  else
1392  destination.append(" VARCHAR(");
1393 
1394  destination.append(boost::lexical_cast<string>(field.string_options().length()));
1395  destination.append(")");
1396  }
1397  break;
1398  case Table::Field::BLOB:
1399  {
1400  if (field.string_options().has_collation()
1401  && field.string_options().collation().compare("binary") == 0)
1402  destination.append(" BLOB");
1403  else
1404  destination.append(" TEXT");
1405  }
1406  break;
1407  case Table::Field::ENUM:
1408  {
1409  size_t num_field_values= field.enumeration_values().field_value_size();
1410  destination.append(" ENUM(");
1411  for (size_t x= 0; x < num_field_values; ++x)
1412  {
1413  const string &type= field.enumeration_values().field_value(x);
1414 
1415  if (x != 0)
1416  destination.push_back(',');
1417 
1418  destination.push_back('\'');
1419  destination.append(type);
1420  destination.push_back('\'');
1421  }
1422  destination.push_back(')');
1423  break;
1424  }
1425  case Table::Field::UUID:
1426  destination.append(" UUID");
1427  break;
1428  case Table::Field::IPV6:
1429  destination.append(" IPV6");
1430  break;
1431  case Table::Field::BOOLEAN:
1432  destination.append(" BOOLEAN");
1433  break;
1434  case Table::Field::INTEGER:
1435  destination.append(" INT");
1436  break;
1437  case Table::Field::BIGINT:
1438  if (field.has_constraints() and
1439  field.constraints().is_unsigned())
1440  {
1441  destination.append(" BIGINT UNSIGNED");
1442  }
1443  else
1444  {
1445  destination.append(" BIGINT");
1446  }
1447  break;
1448  case Table::Field::DECIMAL:
1449  {
1450  destination.append(" DECIMAL(");
1451  stringstream ss;
1452  ss << field.numeric_options().precision() << ",";
1453  ss << field.numeric_options().scale() << ")";
1454  destination.append(ss.str());
1455  }
1456  break;
1457  case Table::Field::DATE:
1458  destination.append(" DATE");
1459  break;
1460 
1461  case Table::Field::EPOCH:
1462  if (field.time_options().microseconds())
1463  {
1464  destination.append(" TIMESTAMP(6)");
1465  }
1466  else
1467  {
1468  destination.append(" TIMESTAMP");
1469  }
1470  break;
1471 
1472  case Table::Field::DATETIME:
1473  destination.append(" DATETIME");
1474  break;
1475  case Table::Field::TIME:
1476  destination.append(" TIME");
1477  break;
1478  }
1479 
1480  if (field.type() == Table::Field::BLOB ||
1481  field.type() == Table::Field::VARCHAR)
1482  {
1483  if (field.string_options().has_collation()
1484  && field.string_options().collation().compare("binary"))
1485  {
1486  destination.append(" COLLATE ");
1487  destination.append(field.string_options().collation());
1488  }
1489  }
1490 
1491  if (field.has_constraints() and field.constraints().is_unique())
1492  {
1493  destination.append(" UNIQUE");
1494  }
1495 
1496  if (field.has_constraints() && field.constraints().is_notnull())
1497  {
1498  destination.append(" NOT NULL");
1499  }
1500  else if (field.type() == Table::Field::EPOCH)
1501  {
1502  destination.append(" NULL");
1503  }
1504 
1505  if (field.type() == Table::Field::INTEGER ||
1506  field.type() == Table::Field::BIGINT)
1507  {
1508  /* AUTO_INCREMENT must be after NOT NULL */
1509  if (field.has_numeric_options() &&
1510  field.numeric_options().is_autoincrement())
1511  {
1512  destination.append(" AUTO_INCREMENT");
1513  }
1514  }
1515 
1516  if (field.options().has_default_value())
1517  {
1518  destination.append(" DEFAULT ");
1519  append_escaped_string(&destination, field.options().default_value());
1520  }
1521  else if (field.options().has_default_expression())
1522  {
1523  destination.append(" DEFAULT ");
1524  destination.append(field.options().default_expression());
1525  }
1526  else if (field.options().has_default_bin_value())
1527  {
1528  const string &v= field.options().default_bin_value();
1529  if (v.length() == 0)
1530  {
1531  destination.append(" DEFAULT ''");
1532  }
1533  else
1534  {
1535  destination.append(" DEFAULT 0x");
1536  for (size_t x= 0; x < v.length(); x++)
1537  {
1538  char hex[3];
1539  snprintf(hex, sizeof(hex), "%.2X", *(v.c_str() + x));
1540  destination.append(hex, 2);
1541  }
1542  }
1543  }
1544  else if (field.options().has_default_null()
1545  && field.options().default_null()
1546  && field.type() != Table::Field::BLOB)
1547  {
1548  destination.append(" DEFAULT NULL");
1549  }
1550 
1551  if (field.has_options() && field.options().has_update_expression())
1552  {
1553  destination.append(" ON UPDATE ");
1554  destination.append(field.options().update_expression());
1555  }
1556 
1557  if (field.has_comment())
1558  {
1559  destination.append(" COMMENT ");
1560  append_escaped_string(&destination, field.comment(), quoted_default);
1561  }
1562  return NONE;
1563 }
1564 
1565 bool shouldQuoteFieldValue(Table::Field::FieldType in_type)
1566 {
1567  switch (in_type)
1568  {
1569  case Table::Field::DOUBLE:
1570  case Table::Field::DECIMAL:
1571  case Table::Field::INTEGER:
1572  case Table::Field::BIGINT:
1573  return false;
1574  default:
1575  return true;
1576  }
1577 }
1578 
1579 Table::Field::FieldType internalFieldTypeToFieldProtoType(enum enum_field_types type)
1580 {
1581  switch (type) {
1582  case DRIZZLE_TYPE_LONG:
1583  return Table::Field::INTEGER;
1584  case DRIZZLE_TYPE_DOUBLE:
1585  return Table::Field::DOUBLE;
1586  case DRIZZLE_TYPE_NULL:
1587  assert(false); /* Not a user definable type */
1588  return Table::Field::INTEGER; /* unreachable */
1589  case DRIZZLE_TYPE_MICROTIME:
1590  case DRIZZLE_TYPE_TIMESTAMP:
1591  return Table::Field::EPOCH;
1592  case DRIZZLE_TYPE_LONGLONG:
1593  return Table::Field::BIGINT;
1594  case DRIZZLE_TYPE_DATETIME:
1595  return Table::Field::DATETIME;
1596  case DRIZZLE_TYPE_TIME:
1597  return Table::Field::TIME;
1598  case DRIZZLE_TYPE_DATE:
1599  return Table::Field::DATE;
1600  case DRIZZLE_TYPE_VARCHAR:
1601  return Table::Field::VARCHAR;
1602  case DRIZZLE_TYPE_DECIMAL:
1603  return Table::Field::DECIMAL;
1604  case DRIZZLE_TYPE_ENUM:
1605  return Table::Field::ENUM;
1606  case DRIZZLE_TYPE_BLOB:
1607  return Table::Field::BLOB;
1608  case DRIZZLE_TYPE_UUID:
1609  return Table::Field::UUID;
1610  case DRIZZLE_TYPE_BOOLEAN:
1611  return Table::Field::BOOLEAN;
1612  case DRIZZLE_TYPE_IPV6:
1613  return Table::Field::IPV6;
1614  }
1615 
1616  assert(false);
1617  return Table::Field::INTEGER; /* unreachable */
1618 }
1619 
1620 bool transactionContainsBulkSegment(const Transaction &transaction)
1621 {
1622  size_t num_statements= transaction.statement_size();
1623  if (num_statements == 0)
1624  return false;
1625 
1626  /*
1627  * Only INSERT, UPDATE, and DELETE statements can possibly
1628  * have bulk segments. So, we loop through the statements
1629  * checking for segment_id > 1 in those specific submessages.
1630  */
1631  size_t x;
1632  for (x= 0; x < num_statements; ++x)
1633  {
1634  const Statement &statement= transaction.statement(x);
1635  Statement::Type type= statement.type();
1636 
1637  switch (type)
1638  {
1639  case Statement::INSERT:
1640  if (statement.insert_data().segment_id() > 1)
1641  return true;
1642  break;
1643  case Statement::UPDATE:
1644  if (statement.update_data().segment_id() > 1)
1645  return true;
1646  break;
1647  case Statement::DELETE:
1648  if (statement.delete_data().segment_id() > 1)
1649  return true;
1650  break;
1651  default:
1652  break;
1653  }
1654  }
1655  return false;
1656 }
1657 
1658 } /* namespace message */
1659 } /* namespace drizzled */
Definition: schema.h:30