~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/message/statement_transform.cc

  • Committer: Brian Aker
  • Date: 2008-06-28 01:01:34 UTC
  • Revision ID: brian@tangent.org-20080628010134-n44ixrdgb6yexhkb
Remove my_pthread_getspecific_ptr()

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