~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/message/statement_transform.cc

Reverted 1103

Show diffs side-by-side

added added

removed removed

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