~ubuntu-branches/ubuntu/quantal/mysql-workbench/quantal

« back to all changes in this revision

Viewing changes to library/sql-parser/source/myx_statement_parser.cpp

  • Committer: Package Import Robot
  • Author(s): Dmitry Smirnov
  • Date: 2012-03-01 21:57:30 UTC
  • Revision ID: package-import@ubuntu.com-20120301215730-o7y8av8y38n162ro
Tags: upstream-5.2.38+dfsg
ImportĀ upstreamĀ versionĀ 5.2.38+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
#include <iostream>
 
3
#include <sstream>
 
4
#include <fstream>
 
5
 
 
6
#include <string.h>
 
7
 
 
8
#include "myx_statement_parser.h"
 
9
 
 
10
#include "mysql_version.h"
 
11
#include "my_global.h"
 
12
#include "my_sys.h"
 
13
#include "sql_string.h"
 
14
#include "unireg.h"
 
15
 
 
16
#include "structs.h"
 
17
#include <m_ctype.h>
 
18
#include <hash.h>
 
19
 
 
20
#include "sql_lex.h"
 
21
 
 
22
namespace mysql_parser
 
23
{
 
24
 
 
25
MyxStatementParser::MyxStatementParser(CHARSET_INFO *charset)
 
26
  : cs(charset), eof_hit(false)
 
27
{
 
28
  delim= ";";
 
29
  char_buffer= new char[CHAR_BUFFER_SIZE];
 
30
  char_buffer_e= char_buffer_b= char_buffer + CHAR_BUFFER_SIZE;
 
31
}
 
32
 
 
33
MyxStatementParser::~MyxStatementParser()
 
34
{
 
35
  delete[] char_buffer;
 
36
}
 
37
 
 
38
static void replace_string(std::string &text, const std::string &from, const std::string &to)
 
39
{
 
40
  std::string::size_type from_len= from.length();
 
41
  std::string::size_type p= text.find(from);
 
42
  while (p != std::string::npos)
 
43
  {
 
44
    text.replace(p, from_len, to);
 
45
    p= text.find(from, p);
 
46
  }
 
47
}
 
48
 
 
49
static bool is_empty_statement(const std::string& str)
 
50
{
 
51
  int i= 0;
 
52
  while(str[i] != 0)
 
53
    if(str[i++] > ' ')
 
54
      return false;
 
55
 
 
56
  return true;
 
57
}
 
58
 
 
59
int MyxStatementParser::fill_buffer(std::istream& is)
 
60
{
 
61
  char *input_start= std::copy(char_buffer_b, char_buffer_e, char_buffer);
 
62
  int len= (input_start - char_buffer);
 
63
  is.read(input_start, CHAR_BUFFER_SIZE - len);
 
64
  int gc= (int) is.gcount();
 
65
  char_buffer_b= char_buffer;
 
66
  char_buffer_e= char_buffer + len + gc;
 
67
  return gc;
 
68
}
 
69
 
 
70
int MyxStatementParser::buffer_eof(std::istream& is)
 
71
{
 
72
  return eof_hit;
 
73
}
 
74
 
 
75
int MyxStatementParser::get_next_char(std::istream& is, int *len, int count_lines)
 
76
{
 
77
  if(char_buffer_e - char_buffer_b < 4)
 
78
    fill_buffer(is);
 
79
 
 
80
  if(char_buffer_e == char_buffer_b)
 
81
  {
 
82
    eof_hit= true;
 
83
    *len= 0;
 
84
    return -1;
 
85
  }
 
86
 
 
87
  *len= 1;
 
88
  unsigned int c;
 
89
 
 
90
  if (my_mbcharlen(cs, *char_buffer_b) > 1)
 
91
  {
 
92
    static unsigned int mask[3]= { 0x0000FFFF, 0x00FFFFFF, 0xFFFFFFFF };
 
93
    
 
94
    *len= my_ismbchar(cs, char_buffer_b, char_buffer_e);
 
95
    c= *(unsigned int *)char_buffer_b;
 
96
    char_buffer_b += *len;
 
97
    c&= mask[*len - 2];
 
98
  }
 
99
  else
 
100
  {
 
101
    c= *char_buffer_b++;
 
102
  }
 
103
 
 
104
  if (count_lines)
 
105
  {
 
106
    switch (c)
 
107
    {
 
108
    case '\r':
 
109
      {
 
110
        int len;
 
111
        if ('\n' == peek_next_char(is, &len))
 
112
          break;
 
113
      }
 
114
    case '\n':
 
115
      ++_total_lc;
 
116
      _symbols_since_newline= 0;
 
117
      break;
 
118
    default:
 
119
      _symbols_since_newline += *len;
 
120
      break;
 
121
    }
 
122
  }
 
123
 
 
124
  return c;
 
125
}
 
126
 
 
127
int MyxStatementParser::peek_next_char(std::istream& is, int *len)
 
128
{
 
129
  int c= get_next_char(is, len, 0);
 
130
  char_buffer_b -= *len;
 
131
  return c;
 
132
}
 
133
 
 
134
void MyxStatementParser::add_char_to_buffer(std::string& buffer, int c, int len) const
 
135
{
 
136
  unsigned uc= (unsigned)c;
 
137
 
 
138
  switch(len) // all cases fall through
 
139
  {
 
140
  case 4:
 
141
    buffer += (char)((uc & 0xFF000000) >> 24);
 
142
  case 3:
 
143
    buffer += (char)((uc & 0x00FF0000) >> 16);
 
144
  case 2:
 
145
    buffer += (char)((uc & 0x0000FF00) >> 8);
 
146
  case 1:
 
147
    buffer += (char)((uc & 0x000000FF) >> 0);
 
148
  }
 
149
}
 
150
 
 
151
void MyxStatementParser::process(std::istream& is, process_sql_statement_callback cb, void *arg, int mode)
 
152
{
 
153
  static const char *kwd= "DELIMITER";
 
154
  
 
155
  int c;
 
156
  ParserState state= start, prevState;
 
157
  std::string stmt_buffer;
 
158
  std::string delim_buffer;
 
159
  char strchar;
 
160
  _stmt_boffset= 0;
 
161
  _stmt_first_line_first_symbol_pos= 0;
 
162
  _symbols_since_newline= 0;
 
163
  _total_lc= 0;
 
164
 
 
165
  int len;
 
166
  bool m, can_be_kwd;
 
167
  int p;
 
168
 
 
169
  while(!buffer_eof(is) && !parser_is_stopped) {
 
170
    switch(state) {
 
171
    case eos:
 
172
      break;
 
173
    
 
174
    case start:
 
175
      stmt_buffer.clear();
 
176
      c= get_next_char(is, &len);
 
177
      while(my_isspace(cs, c) || (c == '\n') || (c == '\r')) {
 
178
        add_char_to_buffer(stmt_buffer, c, len);
 
179
        c= get_next_char(is, &len);
 
180
      }
 
181
      add_char_to_buffer(stmt_buffer, c, len);
 
182
      if(kwd[0] == my_toupper(cs, c)) {
 
183
        state= delimkwd;
 
184
      } else if(c == '`') {
 
185
        strchar= '`';
 
186
        state= str;
 
187
      } else if(c == '\'') {
 
188
        strchar= '\'';
 
189
        state= str;
 
190
      } else if(c == '"') {
 
191
        strchar= '"';
 
192
        state= str;
 
193
      } else if((c == '/') && (peek_next_char(is, &len) == '*')) {
 
194
        prevState= start;
 
195
        state= mlcomment;
 
196
      } else if((c == '-') && (peek_next_char(is, &len) == '-')) {
 
197
        prevState= start;
 
198
        state= comment1;
 
199
      } else if(c == '#') {
 
200
        prevState= start;
 
201
        state= comment2;
 
202
      } else if(c == delim[0]) {
 
203
        state= delimtok;
 
204
      } else {
 
205
        state= stmt;
 
206
      }
 
207
      continue;
 
208
    
 
209
    case delimkwd:
 
210
      m= true;
 
211
      for (int i= 1; kwd[i] != '\0'; i++) {
 
212
        c= peek_next_char(is, &len);
 
213
        if(my_toupper(cs, c) != kwd[i]) {
 
214
          m= false;
 
215
          break;
 
216
        }
 
217
        else
 
218
        {
 
219
          c= get_next_char(is, &len);
 
220
          add_char_to_buffer(stmt_buffer, c, len);        
 
221
        }
 
222
      }
 
223
      if(!m) {
 
224
        //state= stmt;
 
225
        //continue;
 
226
        goto stmtlabel;
 
227
      }
 
228
      c= get_next_char(is, &len);
 
229
      add_char_to_buffer(stmt_buffer, c, len);
 
230
      if(!my_isspace(cs, c)) {
 
231
        state= stmt;
 
232
        continue;
 
233
      }
 
234
      c= peek_next_char(is, &len);
 
235
      while(my_isspace(cs, c)) {
 
236
        c= get_next_char(is, &len);
 
237
        add_char_to_buffer(stmt_buffer, c, len);
 
238
        c= peek_next_char(is, &len);
 
239
      }
 
240
      if((c == '\r') || (c == '\n')) {
 
241
        add_char_to_buffer(stmt_buffer, c, len);
 
242
        state= stmt;
 
243
        continue;
 
244
      }
 
245
      delim_buffer.clear();
 
246
      _stmt_boffset+= stmt_buffer.size();
 
247
      while((c != '\r') && (c != '\n') /*&& !my_isspace(cs, c)*/ && !buffer_eof(is)) 
 
248
      {
 
249
        c= get_next_char(is, &len);
 
250
        _stmt_boffset+= len;
 
251
        delim_buffer+= c;
 
252
        c= peek_next_char(is, &len);
 
253
      }
 
254
      //if(delim_buffer.length() > delim.length()) 
 
255
      //{
 
256
      //  if(delim_buffer.compare(delim_buffer.length() - delim.length(), delim.length(), delim) == 0)
 
257
      //  {
 
258
      //    delim_buffer.erase(delim_buffer.length() - delim.length());
 
259
      //  }
 
260
      //}
 
261
 
 
262
      // new delimiter
 
263
      if(!delim_buffer.empty())
 
264
      {
 
265
        stmt_buffer.clear();
 
266
        for (size_t n= 0, count= delim_buffer.size(); n < count; ++n)
 
267
        {
 
268
          if (my_isspace(cs, delim_buffer[n]))
 
269
          {
 
270
            delim_buffer.resize(n);
 
271
            break;
 
272
          }
 
273
        }
 
274
        delim= delim_buffer;
 
275
        for(;;)
 
276
        {
 
277
          c= peek_next_char(is, &len);
 
278
          if((c != '\r') && (c != '\n'))
 
279
            break;
 
280
          c= get_next_char(is, &len);
 
281
          _stmt_boffset+= len;
 
282
        }
 
283
        _stmt_first_line_first_symbol_pos= _symbols_since_newline;
 
284
      }
 
285
 
 
286
      state= start;
 
287
      continue;
 
288
 
 
289
    case str:
 
290
      c= get_next_char(is, &len);
 
291
      while((c != strchar) && !buffer_eof(is)) 
 
292
      {
 
293
        add_char_to_buffer(stmt_buffer, c, len);
 
294
        if(c == '\\') 
 
295
        {
 
296
          c= get_next_char(is, &len);
 
297
          add_char_to_buffer(stmt_buffer, c, len);
 
298
        }
 
299
        c= get_next_char(is, &len);
 
300
      }
 
301
      add_char_to_buffer(stmt_buffer, c, len);
 
302
      if(!buffer_eof(is)) 
 
303
      {
 
304
        state= stmt;
 
305
      }
 
306
      continue;
 
307
 
 
308
    case mlcomment:
 
309
      c= get_next_char(is, &len);
 
310
      add_char_to_buffer(stmt_buffer, c, len);
 
311
      if(c != '*') {
 
312
        state= stmt;
 
313
        continue;
 
314
      }
 
315
 
 
316
      p= ' ';
 
317
      while(!buffer_eof(is)) {
 
318
        c= get_next_char(is, &len);
 
319
        add_char_to_buffer(stmt_buffer, c, len);
 
320
        if((c == '/') && (p == '*')) {
 
321
          state= stmt;
 
322
          break;
 
323
        }
 
324
        if(buffer_eof(is))
 
325
        {
 
326
          break;
 
327
        }
 
328
        p= c;
 
329
      }
 
330
      continue;
 
331
 
 
332
    case comment2:
 
333
      c= get_next_char(is, &len);
 
334
      while((c != '\r') && (c != '\n') && !buffer_eof(is)) {
 
335
        add_char_to_buffer(stmt_buffer, c, len);
 
336
        c= get_next_char(is, &len);
 
337
      }
 
338
      add_char_to_buffer(stmt_buffer, c, len);
 
339
      state= stmt;
 
340
      
 
341
      c= peek_next_char(is, &len);
 
342
      while((c == '\r') || (c == '\n'))
 
343
      {
 
344
        c= get_next_char(is, &len);
 
345
        add_char_to_buffer(stmt_buffer, c, len);
 
346
        c= peek_next_char(is, &len);
 
347
      }
 
348
 
 
349
      continue;
 
350
 
 
351
    case comment1:
 
352
      c= get_next_char(is, &len);
 
353
      if(c != '-') {
 
354
        add_char_to_buffer(stmt_buffer, c, len);
 
355
        state= prevState;
 
356
        continue;
 
357
      }
 
358
      while((c != '\r') && (c != '\n') && !buffer_eof(is)) {
 
359
        add_char_to_buffer(stmt_buffer, c, len);
 
360
        c= get_next_char(is, &len);
 
361
      }
 
362
      add_char_to_buffer(stmt_buffer, c, len);
 
363
      state= stmt;
 
364
 
 
365
      c= peek_next_char(is, &len);
 
366
      while((c == '\r') || (c == '\n'))
 
367
      {
 
368
        c= get_next_char(is, &len);
 
369
        add_char_to_buffer(stmt_buffer, c, len);
 
370
        c= peek_next_char(is, &len);
 
371
      }
 
372
      continue;
 
373
 
 
374
    case delimtok:
 
375
      m= true;
 
376
      for(size_t i= 1; i < delim.size(); i++) {
 
377
        c= get_next_char(is, &len);
 
378
        add_char_to_buffer(stmt_buffer, c, len);
 
379
        if(my_toupper(cs, c) != delim[i]) {
 
380
          m= false;
 
381
          break;
 
382
        }
 
383
      }
 
384
      if(!m) {
 
385
        state= stmt;
 
386
        continue;
 
387
      }
 
388
      // new statement is read
 
389
      stmt_buffer.erase(stmt_buffer.length() - delim.length());
 
390
      {
 
391
        int stmt_boffset_inc= stmt_buffer.size() + delim.size();
 
392
        if(!is_empty_statement(stmt_buffer))
 
393
          cb(this, stmt_buffer.c_str(), arg);
 
394
        _stmt_boffset+= stmt_boffset_inc;
 
395
      }
 
396
      stmt_buffer.clear();
 
397
      _stmt_first_line_first_symbol_pos= _symbols_since_newline;
 
398
 
 
399
      state= start;
 
400
      continue;
 
401
 
 
402
    case stmt:
 
403
stmtlabel:
 
404
      can_be_kwd= true;
 
405
 
 
406
      while(!buffer_eof(is)) {
 
407
        c= get_next_char(is, &len);
 
408
        add_char_to_buffer(stmt_buffer, c, len);
 
409
        if(can_be_kwd && (kwd[0] == my_toupper(cs, c))) {
 
410
          prevState= stmt;
 
411
          state= delimkwd;
 
412
          break;
 
413
        } else if(c == '`') {
 
414
          prevState= stmt;
 
415
          strchar= '`';
 
416
          state= str;
 
417
          break;
 
418
        } else if(c == '\'') {
 
419
          prevState= stmt;
 
420
          strchar= '\'';
 
421
          state= str;
 
422
          break;
 
423
        } else if(c == '"') {
 
424
          prevState= stmt;
 
425
          strchar= '"';
 
426
          state= str;
 
427
          break;
 
428
        } else if((c == '/') && (peek_next_char(is, &len) == '*')) {
 
429
          prevState= stmt;
 
430
          state= mlcomment;
 
431
          break;
 
432
        } else if((c == '-') && (peek_next_char(is, &len) == '-')) {
 
433
          prevState= stmt;
 
434
          state= comment1;
 
435
          break;
 
436
        } else if(c == '#') {
 
437
          prevState= stmt;
 
438
          state= comment2;
 
439
          break;
 
440
        } else if(c == delim[0]) {
 
441
          prevState= stmt;
 
442
          state= delimtok;
 
443
          break;
 
444
        }
 
445
        if(c > ' ')
 
446
          can_be_kwd= false;
 
447
      }
 
448
      continue;
 
449
    } // switch
 
450
  }
 
451
 
 
452
  if (parser_is_stopped)
 
453
    return;
 
454
  else if(!(mode & MYX_SPM_DELIMS_REQUIRED)
 
455
    && (stmt_buffer.length() > 0)
 
456
    && !is_empty_statement(stmt_buffer))
 
457
  {
 
458
    int stmt_boffset_inc= stmt_buffer.size();
 
459
    cb(this, stmt_buffer.c_str(), arg);
 
460
    _stmt_boffset+= stmt_boffset_inc;
 
461
  }
 
462
}
 
463
 
 
464
MYX_PUBLIC_FUNC 
 
465
int myx_process_sql_statements(const char *sql, CHARSET_INFO *cs, process_sql_statement_callback cb, void *user_data, int mode)
 
466
{
 
467
  MyxStatementParser p(cs);
 
468
  std::istringstream tmp(sql, std::ios::in | std::ios::binary);
 
469
  p.process(tmp, cb, user_data, mode);
 
470
  return 0;
 
471
}
 
472
 
 
473
MYX_PUBLIC_FUNC 
 
474
int myx_process_sql_statements_from_file(const char *filename, CHARSET_INFO *cs, process_sql_statement_callback cb, void *user_data, int mode)
 
475
{
 
476
  std::ifstream is;
 
477
 
 
478
#if defined(__WIN__) || defined(_WIN32) || defined(_WIN64)
 
479
  std::ios_base::open_mode om= std::ios::in | std::ios::binary;
 
480
  {
 
481
    wchar_t filename_[MAX_PATH+1]= {0};
 
482
    MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_, MAX_PATH);
 
483
    is.open(filename_, std::ios::in | std::ios::binary);
 
484
  }
 
485
#else
 
486
  is.open(filename, std::ios::in | std::ios::binary);
 
487
#endif
 
488
 
 
489
  // Find out size of the file and return if it doesn't contain anything.
 
490
  is.seekg (0, std::ios::end);
 
491
  std::streamoff length = is.tellg();
 
492
  is.seekg (0, std::ios::beg);
 
493
  if (length < 3) // 3 bytes to allow checking for a UTF-8 BOM.
 
494
    return 0;
 
495
 
 
496
  // Check if the file contains a BOM (byte-order-mark). 3 characters for the UTF-8 encoded BOM
 
497
  // +1 for the terminating 0 added by get().
 
498
  char buffer[4];
 
499
  is.get(buffer, 4);
 
500
  if (((unsigned char)buffer[0] != 0xEF) || ((unsigned char)buffer[1] != 0xBB) || ((unsigned char)buffer[2] != 0xBF))
 
501
  {
 
502
    // Not a BOM, so reset the input pointer.
 
503
    is.seekg (0, std::ios::beg);
 
504
  }
 
505
 
 
506
  MyxStatementParser p(cs);
 
507
  p.process(is, cb, user_data, mode);
 
508
  return 0;
 
509
}
 
510
 
 
511
} // namespace mysql_parser