~ubuntu-branches/ubuntu/trusty/drizzle/trusty

« back to all changes in this revision

Viewing changes to plugin/pbxt/src/datadic_xt.cc

  • Committer: Bazaar Package Importer
  • Author(s): Monty Taylor
  • Date: 2010-10-02 14:17:48 UTC
  • mfrom: (1.1.1 upstream)
  • mto: (2.1.17 sid)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20101002141748-m6vbfbfjhrw1153e
Tags: 2010.09.1802-1
* New upstream release.
* Removed pid-file argument hack.
* Updated GPL-2 address to be new address.
* Directly copy in drizzledump.1 since debian doesn't have sphinx 1.0 yet.
* Link to jquery from libjs-jquery. Add it as a depend.
* Add drizzled.8 symlink to the install files.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2005 PrimeBase Technologies GmbH
 
2
 *
 
3
 * PrimeBase XT
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
18
 *
 
19
 * 2006-05-16   Paul McCullagh
 
20
 *
 
21
 * H&G2JCtL
 
22
 *
 
23
 * Implementation of the PBXT internal data dictionary.
 
24
 */
 
25
 
 
26
 
 
27
#include "xt_config.h"
 
28
 
 
29
#ifdef DRIZZLED
 
30
#include <bitset>
 
31
#endif
 
32
 
 
33
#include <ctype.h>
 
34
#include <errno.h>
 
35
 
 
36
#ifdef DEBUG
 
37
#ifdef DRIZZLED
 
38
//#include <drizzled/common_includes.h>
 
39
#else
 
40
#include "mysql_priv.h"
 
41
#endif
 
42
#endif
 
43
 
 
44
#include "pthread_xt.h"
 
45
#include "datadic_xt.h"
 
46
#include "util_xt.h"
 
47
#include "database_xt.h"
 
48
#include "table_xt.h"
 
49
#include "heap_xt.h"
 
50
#include "strutil_xt.h"
 
51
#include "myxt_xt.h"
 
52
#include "hashtab_xt.h"
 
53
 
 
54
/*
 
55
 * -----------------------------------------------------------------------
 
56
 * Lexical analyser
 
57
 */
 
58
 
 
59
#define XT_TK_EOF                               0
 
60
#define XT_TK_IDENTIFIER                1
 
61
#define XT_TK_NUMBER                    2
 
62
#define XT_TK_STRING                    3
 
63
#define XT_TK_PUNCTUATION               4
 
64
 
 
65
#define XT_TK_RESERVER_WORDS    5
 
66
#define XT_TK_PRIMARY                   5
 
67
#define XT_TK_UNIQUE                    6
 
68
#define XT_TK_FULLTEXT                  7
 
69
#define XT_TK_SPATIAL                   8
 
70
#define XT_TK_INDEX                             9
 
71
#define XT_TK_KEY                               10
 
72
#define XT_TK_CHECK                             11
 
73
#define XT_TK_FOREIGN                   12
 
74
#define XT_TK_COLUMN                    13
 
75
#define XT_TK_REFERENCES                14
 
76
#define XT_TK_NOT                               15
 
77
#define XT_TK_NULL                              16
 
78
#define XT_TK_AUTO_INCREMENT    17
 
79
#define XT_TK_COMMENT                   18
 
80
#define XT_TK_DEFAULT                   19
 
81
#define XT_TK_COLLATE                   20
 
82
 
 
83
class XTToken {
 
84
        public: 
 
85
        u_int   tk_type;
 
86
        char    *tk_text;
 
87
        size_t  tk_length;
 
88
 
 
89
        void initCString(u_int type, char *start, char *end);
 
90
        inline char charAt(u_int i) {
 
91
                if (i >= tk_length)
 
92
                        return 0;
 
93
                return toupper(tk_text[i]);
 
94
        }
 
95
        void expectKeyWord(XTThreadPtr self, c_char *keyword);
 
96
        void expectIdentifier(XTThreadPtr self);
 
97
        void expectNumber(XTThreadPtr self);
 
98
        bool isKeyWord(c_char *keyword);
 
99
        bool isReservedWord();
 
100
        bool isReservedWord(u_int word);
 
101
        void identifyReservedWord();
 
102
        bool isEOF();
 
103
        bool isIdentifier();
 
104
        bool isNumber();
 
105
        size_t getString(char *string, size_t len);
 
106
        void getTokenText(char *string, size_t len);
 
107
        XTToken *clone(XTThreadPtr self);
 
108
};
 
109
 
 
110
void XTToken::initCString(u_int type, char *start, char *end)
 
111
{
 
112
        tk_type = type;
 
113
        tk_text = start;
 
114
        tk_length = (size_t) end - (size_t) start;
 
115
}
 
116
 
 
117
bool XTToken::isKeyWord(c_char *keyword)
 
118
{
 
119
        char    *str = tk_text;
 
120
        size_t  len = tk_length;
 
121
        
 
122
        while (len && *keyword) {
 
123
                if (toupper(*keyword) != toupper(*str))
 
124
                        return false;
 
125
                keyword++;
 
126
                str++;
 
127
                len--;
 
128
        }
 
129
        return !len && !*keyword;
 
130
}
 
131
 
 
132
bool XTToken::isReservedWord()
 
133
{
 
134
        return tk_type >= XT_TK_RESERVER_WORDS;
 
135
}
 
136
 
 
137
bool XTToken::isReservedWord(u_int word)
 
138
{
 
139
        return tk_type == word;
 
140
}
 
141
 
 
142
void XTToken::identifyReservedWord()
 
143
{
 
144
        if (tk_type == XT_TK_IDENTIFIER) {
 
145
                switch (charAt(0)) {
 
146
                        case 'A':
 
147
                                if (isKeyWord("AUTO_INCREMENT"))
 
148
                                        tk_type = XT_TK_AUTO_INCREMENT;
 
149
                                break;
 
150
                        case 'C':
 
151
                                switch (charAt(2)) {
 
152
                                        case 'E':
 
153
                                                if (isKeyWord("CHECK"))
 
154
                                                        tk_type = XT_TK_CHECK;
 
155
                                                break;
 
156
                                        case 'L':
 
157
                                                if (isKeyWord("COLUMN"))
 
158
                                                        tk_type = XT_TK_COLUMN;
 
159
                                                else if (isKeyWord("COLLATE"))
 
160
                                                        tk_type = XT_TK_COLLATE;
 
161
                                                break;
 
162
                                        case 'M':
 
163
                                                if (isKeyWord("COMMENT"))
 
164
                                                        tk_type = XT_TK_COMMENT;
 
165
                                                break;
 
166
                                }
 
167
                                break;
 
168
                        case 'D':
 
169
                                if (isKeyWord("DEFAULT"))
 
170
                                        tk_type = XT_TK_DEFAULT;
 
171
                                break;
 
172
                        case 'F':
 
173
                                switch (charAt(1)) {
 
174
                                        case 'O':
 
175
                                                if (isKeyWord("FOREIGN"))
 
176
                                                        tk_type = XT_TK_FOREIGN;
 
177
                                                break;
 
178
                                        case 'U':
 
179
                                                if (isKeyWord("FULLTEXT"))
 
180
                                                        tk_type = XT_TK_FULLTEXT;
 
181
                                                break;
 
182
                                }
 
183
                                break;
 
184
                        case 'I':
 
185
                                if (isKeyWord("INDEX"))
 
186
                                        tk_type = XT_TK_INDEX;
 
187
                                break;
 
188
                        case 'K':
 
189
                                if (isKeyWord("KEY"))
 
190
                                        tk_type = XT_TK_KEY;
 
191
                                break;
 
192
                        case 'N':
 
193
                                switch (charAt(1)) {
 
194
                                        case 'O':
 
195
                                                if (isKeyWord("NOT"))
 
196
                                                        tk_type = XT_TK_NOT;
 
197
                                                break;
 
198
                                        case 'U':
 
199
                                                if (isKeyWord("NULL"))
 
200
                                                        tk_type = XT_TK_NULL;
 
201
                                                break;
 
202
                                }
 
203
                                break;
 
204
                        case 'P':
 
205
                                if (isKeyWord("PRIMARY"))
 
206
                                        tk_type = XT_TK_PRIMARY;
 
207
                                break;
 
208
                        case 'R':
 
209
                                if (isKeyWord("REFERENCES"))
 
210
                                        tk_type = XT_TK_REFERENCES;
 
211
                                break;
 
212
                        case 'S':
 
213
                                if (isKeyWord("SPATIAL"))
 
214
                                        tk_type = XT_TK_SPATIAL;
 
215
                                break;
 
216
                        case 'U':
 
217
                                if (isKeyWord("UNIQUE"))
 
218
                                        tk_type = XT_TK_UNIQUE;
 
219
                                break;                  
 
220
                }
 
221
        }
 
222
}
 
223
 
 
224
bool XTToken::isEOF()
 
225
{
 
226
        return tk_type == XT_TK_EOF;
 
227
}
 
228
 
 
229
bool XTToken::isIdentifier()
 
230
{
 
231
        return tk_type == XT_TK_IDENTIFIER;
 
232
}
 
233
 
 
234
bool XTToken::isNumber()
 
235
{
 
236
        return tk_type == XT_TK_NUMBER;
 
237
}
 
238
 
 
239
/* Return actual, or required string length. */
 
240
size_t XTToken::getString(char *dtext, size_t dsize)
 
241
{
 
242
        char    *buffer = dtext;
 
243
        int             slen;
 
244
        size_t  dlen;
 
245
        char    *stext;
 
246
        char    quote;
 
247
 
 
248
        if ((slen = (int) tk_length) == 0) {
 
249
                *dtext = 0;
 
250
                return 0;
 
251
        }
 
252
        switch (*tk_text) {
 
253
                case '\'':
 
254
                case '"':
 
255
                case '`':
 
256
                        quote = *tk_text;
 
257
                        stext = tk_text+1;
 
258
                        slen -= 2;
 
259
                        dlen = 0;
 
260
                        while (slen > 0) {
 
261
                                if (*stext == '\\') {
 
262
                                        stext++;
 
263
                                        slen--;
 
264
                                        if (slen > 0) {
 
265
                                                switch (*stext) {
 
266
                                                        case '\0':
 
267
                                                                *dtext = 0;
 
268
                                                                break;
 
269
                                                        case '\'':
 
270
                                                                *dtext = '\'';
 
271
                                                                break;
 
272
                                                        case '"':
 
273
                                                                *dtext = '"';
 
274
                                                                break;
 
275
                                                        case 'b':
 
276
                                                                *dtext = '\b';
 
277
                                                                break;
 
278
                                                        case 'n':
 
279
                                                                *dtext = '\n';
 
280
                                                                break;
 
281
                                                        case 'r':
 
282
                                                                *dtext = '\r';
 
283
                                                                break;
 
284
                                                        case 't':
 
285
                                                                *dtext = '\t';
 
286
                                                                break;
 
287
                                                        case 'z':
 
288
                                                                *dtext = (char) 26;
 
289
                                                                break;
 
290
                                                        case '\\':
 
291
                                                                *dtext = '\\';
 
292
                                                                break;
 
293
                                                        default:
 
294
                                                                *dtext = *stext;
 
295
                                                                break;
 
296
                                                }
 
297
                                        }
 
298
                                }
 
299
                                else if (*stext == quote) {
 
300
                                        if (dlen < dsize)
 
301
                                                *dtext = quote;
 
302
                                        stext++;
 
303
                                        slen--;
 
304
                                }
 
305
                                else {
 
306
                                        if (dlen < dsize)
 
307
                                                *dtext = *stext;
 
308
                                }
 
309
                                dtext++;
 
310
                                dlen++;
 
311
                                stext++;
 
312
                                slen--;
 
313
                        }
 
314
                        if (dlen < dsize)
 
315
                                buffer[dlen] = 0;
 
316
                        else if (dsize > 0)
 
317
                                buffer[dsize-1] = 0;
 
318
                        break;
 
319
                default:
 
320
                        if (dsize > 0) {
 
321
                                dlen = dsize-1;
 
322
                                if ((int) dlen > slen)
 
323
                                        dlen = slen;
 
324
                                memcpy(dtext, tk_text, dlen);
 
325
                                dtext[dlen] = 0;
 
326
                        }
 
327
                        dlen = tk_length;
 
328
                        break;
 
329
        }
 
330
        return dlen;
 
331
}
 
332
 
 
333
/* Return the token as a string with ... in it if it is too long
 
334
 */
 
335
void XTToken::getTokenText(char *string, size_t size)
 
336
{
 
337
        if (tk_length == 0 || !tk_text) {
 
338
                xt_strcpy(size, string, "EOF");
 
339
                return;
 
340
        }
 
341
 
 
342
        size--;
 
343
        if (tk_length <= size) {
 
344
                memcpy(string, tk_text, tk_length);
 
345
                string[tk_length] = 0;
 
346
                return;
 
347
        }
 
348
        
 
349
        size = (size - 3) / 2;
 
350
        memcpy(string, tk_text, size);
 
351
        memcpy(string+size, "...", 3);
 
352
        memcpy(string+size+3, tk_text + tk_length - size, size);
 
353
        string[size+3+size] = 0;
 
354
}
 
355
 
 
356
XTToken *XTToken::clone(XTThreadPtr self)
 
357
{
 
358
        XTToken *tk;
 
359
 
 
360
        if (!(tk = new XTToken()))
 
361
                xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
 
362
        tk->initCString(tk_type, tk_text, tk_text + tk_length);
 
363
        return tk;
 
364
}
 
365
 
 
366
void XTToken::expectKeyWord(XTThreadPtr self, c_char *keyword)
 
367
{
 
368
        char    buffer[100];
 
369
 
 
370
        if (isKeyWord(keyword))
 
371
                return;
 
372
        getTokenText(buffer, 100);
 
373
        xt_throw_i2xterr(XT_CONTEXT, XT_ERR_A_EXPECTED_NOT_B, keyword, buffer);
 
374
}
 
375
 
 
376
void XTToken::expectIdentifier(XTThreadPtr self)
 
377
{
 
378
        char buffer[100];
 
379
 
 
380
        if (isIdentifier())
 
381
                return;
 
382
        getTokenText(buffer, 100);
 
383
        xt_throw_i2xterr(XT_CONTEXT, XT_ERR_A_EXPECTED_NOT_B, "Identifier", buffer);
 
384
}
 
385
 
 
386
void XTToken::expectNumber(XTThreadPtr self)
 
387
{
 
388
        char buffer[100];
 
389
 
 
390
        if (isNumber())
 
391
                return;
 
392
        getTokenText(buffer, 100);
 
393
        xt_throw_i2xterr(XT_CONTEXT, XT_ERR_A_EXPECTED_NOT_B, "Value", buffer);
 
394
}
 
395
 
 
396
struct charset_info_st;
 
397
 
 
398
class XTTokenizer {
 
399
        MX_CONST_CHARSET_INFO   *tkn_charset;
 
400
        char                                    *tkn_cstring;
 
401
        char                                    *tkn_curr_pos;
 
402
        XTToken                                 *tkn_current;
 
403
        bool                                    tkn_in_comment;
 
404
 
 
405
        public:
 
406
 
 
407
        XTTokenizer(bool convert, char *cstring) {
 
408
                tkn_charset = myxt_getcharset(convert);
 
409
                tkn_cstring = cstring;
 
410
                tkn_curr_pos = cstring;
 
411
                tkn_current = NULL;
 
412
                tkn_in_comment = FALSE;
 
413
        }
 
414
 
 
415
        virtual ~XTTokenizer(void) {
 
416
                if (tkn_current)
 
417
                        delete tkn_current;
 
418
        }
 
419
 
 
420
        inline bool isSingleChar(int ch)
 
421
        {
 
422
                return  ch != '$' && ch != '_' && myxt_ispunct(tkn_charset, ch);
 
423
        }
 
424
 
 
425
        inline bool isIdentifierChar(int ch)
 
426
        {
 
427
                return  ch && !isSingleChar(ch) && !myxt_isspace(tkn_charset, ch);
 
428
        }
 
429
 
 
430
        inline bool isNumberChar(int ch, int next_ch)
 
431
        {
 
432
                return myxt_isdigit(tkn_charset, ch) || ((ch == '-' || ch == '+') && myxt_isdigit(tkn_charset, next_ch));
 
433
        }
 
434
 
 
435
        XTToken *newToken(XTThreadPtr self, u_int type, char *start, char *end);
 
436
        XTToken *nextToken(XTThreadPtr self);
 
437
        XTToken *nextToken(XTThreadPtr self, c_char *keyword, XTToken *tk);
 
438
};
 
439
 
 
440
XTToken *XTTokenizer::newToken(XTThreadPtr self, u_int type, char *start, char *end)
 
441
{
 
442
        if (!tkn_current) {
 
443
                if (!(tkn_current = new XTToken()))
 
444
                        xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
 
445
        }
 
446
        tkn_current->initCString(type, start, end);
 
447
        if (type == XT_TK_IDENTIFIER)
 
448
                tkn_current->identifyReservedWord();
 
449
        return tkn_current;
 
450
}
 
451
 
 
452
XTToken *XTTokenizer::nextToken(XTThreadPtr self)
 
453
{
 
454
        char    *token_start;
 
455
        u_int   token_type = XT_TK_PUNCTUATION;
 
456
        char    quote;
 
457
        bool    must_be_num;
 
458
 
 
459
        restart:
 
460
 
 
461
        /* Ignore space: */
 
462
        while (*tkn_curr_pos && myxt_isspace(tkn_charset, *tkn_curr_pos)) tkn_curr_pos++;
 
463
 
 
464
        token_start = tkn_curr_pos;
 
465
        switch (*tkn_curr_pos) {
 
466
                case '\0':
 
467
                        return newToken(self, XT_TK_EOF, NULL, NULL);
 
468
                // Comment: # ... EOL
 
469
                case '#':
 
470
                        tkn_curr_pos++;
 
471
                        while (*tkn_curr_pos && *tkn_curr_pos != '\n' && *tkn_curr_pos != '\r') tkn_curr_pos++;
 
472
                        goto restart;
 
473
                case '-':
 
474
                        if (tkn_curr_pos[1] == '-') {
 
475
                                // Comment: -- ... EOL
 
476
                                while (*tkn_curr_pos && *tkn_curr_pos != '\n' && *tkn_curr_pos != '\r') tkn_curr_pos++;
 
477
                                goto restart;
 
478
                        }
 
479
                        if (myxt_isdigit(tkn_charset, tkn_curr_pos[1]))
 
480
                                goto is_number;
 
481
                        tkn_curr_pos++;
 
482
                        break;
 
483
                case '+':
 
484
                        if (myxt_isdigit(tkn_charset, tkn_curr_pos[1]))
 
485
                                goto is_number;
 
486
                        tkn_curr_pos++;
 
487
                        break;
 
488
                case '/':
 
489
                        tkn_curr_pos++;
 
490
                        if (*tkn_curr_pos == '*') {
 
491
                                // Comment: /* ... */
 
492
                                // Look for: /*!99999 ... */  version conditional statements
 
493
                                tkn_curr_pos++;
 
494
                                if (*tkn_curr_pos == '!') {
 
495
                                        tkn_curr_pos++;
 
496
                                        if (isdigit(*tkn_curr_pos)) {
 
497
                                                while (isdigit(*tkn_curr_pos))
 
498
                                                        tkn_curr_pos++;
 
499
                                                tkn_in_comment = true;
 
500
                                                goto restart;
 
501
                                        }
 
502
                                }
 
503
 
 
504
                                while (*tkn_curr_pos && !(*tkn_curr_pos == '*' && *(tkn_curr_pos+1) == '/')) tkn_curr_pos++;
 
505
                                if (*tkn_curr_pos == '*' && *(tkn_curr_pos+1) == '/')
 
506
                                        tkn_curr_pos += 2;
 
507
                                goto restart;
 
508
                        }
 
509
                        break;
 
510
                case '\'':
 
511
                        token_type = XT_TK_STRING;
 
512
                        goto is_string;
 
513
                case '"':
 
514
                case '`':
 
515
                        token_type = XT_TK_IDENTIFIER;
 
516
                        is_string:
 
517
                        quote = *tkn_curr_pos;
 
518
                        tkn_curr_pos++;
 
519
                        while (*tkn_curr_pos) {
 
520
                                if (*tkn_curr_pos == quote) {
 
521
                                        // Doubling the quote means stay in string...
 
522
                                        if (*(tkn_curr_pos + 1) != quote)
 
523
                                                break;
 
524
                                        tkn_curr_pos++;
 
525
                                }
 
526
                                /* TODO: Unless sql_mode == 'NO_BACKSLASH_ESCAPES'!!! */
 
527
                                if (*tkn_curr_pos == '\\') {
 
528
                                        if (*(tkn_curr_pos+1) == quote) {
 
529
                                                if (quote == '"' || quote == '\'')
 
530
                                                        tkn_curr_pos++;
 
531
                                        }
 
532
                                }
 
533
                                tkn_curr_pos++;
 
534
                        }
 
535
                        
 
536
                        if (*tkn_curr_pos == quote)
 
537
                                tkn_curr_pos++;
 
538
                        break;
 
539
                case '$':
 
540
                        goto is_identifier;
 
541
                case '*':
 
542
                        if (tkn_in_comment) {
 
543
                                if (tkn_curr_pos[1] == '/') {
 
544
                                        tkn_in_comment = false;
 
545
                                        tkn_curr_pos += 2;
 
546
                                        goto restart;
 
547
                                }
 
548
                        }
 
549
                        /* No break required! */
 
550
                default:
 
551
                        if (isNumberChar(tkn_curr_pos[0], tkn_curr_pos[1]))
 
552
                                goto is_number;
 
553
 
 
554
                        if (isSingleChar(*tkn_curr_pos)) {
 
555
                                token_type = XT_TK_PUNCTUATION;
 
556
                                // The rest are singles...
 
557
                                tkn_curr_pos++;
 
558
                                break;
 
559
                        }
 
560
                        
 
561
                        is_identifier:
 
562
                        // Identifier (any string of characters that is not punctuation or a space:
 
563
                        token_type = XT_TK_IDENTIFIER;
 
564
                        while (isIdentifierChar(*tkn_curr_pos))
 
565
                                tkn_curr_pos++;
 
566
                        break;
 
567
 
 
568
                        is_number:
 
569
                        must_be_num = false;
 
570
                        token_type = XT_TK_NUMBER;
 
571
 
 
572
                        if (*tkn_curr_pos == '-' || *tkn_curr_pos == '+') {
 
573
                                must_be_num = true;
 
574
                                tkn_curr_pos++;
 
575
                        }
 
576
 
 
577
                        // Number: 9999 [ . 9999 ] [ e/E [+/-] 9999 ]
 
578
                        // However, 9999e or 9999E is an identifier!
 
579
                        while (*tkn_curr_pos && myxt_isdigit(tkn_charset, *tkn_curr_pos)) tkn_curr_pos++;
 
580
                        
 
581
                        if (*tkn_curr_pos == '.') {
 
582
                                must_be_num = true;
 
583
                                tkn_curr_pos++;
 
584
                                while (*tkn_curr_pos && myxt_isdigit(tkn_charset, *tkn_curr_pos)) tkn_curr_pos++;
 
585
                        }
 
586
 
 
587
                        if (*tkn_curr_pos == 'e' || *tkn_curr_pos == 'E') {
 
588
                                tkn_curr_pos++;
 
589
 
 
590
                                if (isNumberChar(tkn_curr_pos[0], tkn_curr_pos[1])) {
 
591
                                        must_be_num = true;
 
592
 
 
593
                                        if (*tkn_curr_pos == '-' || *tkn_curr_pos == '+')
 
594
                                                tkn_curr_pos++;
 
595
                                        while (*tkn_curr_pos && myxt_isdigit(tkn_charset, *tkn_curr_pos))
 
596
                                                tkn_curr_pos++;
 
597
                                }
 
598
                                else if (!must_be_num)
 
599
                                        token_type = XT_TK_IDENTIFIER;
 
600
                        }
 
601
 
 
602
                        if (must_be_num || !isIdentifierChar(*tkn_curr_pos))
 
603
                                break;
 
604
 
 
605
                        /* Crazy, but true. An identifier can start by looking like a number! */
 
606
                        goto is_identifier;
 
607
        }
 
608
 
 
609
        return newToken(self, token_type, token_start, tkn_curr_pos);
 
610
}
 
611
 
 
612
XTToken *XTTokenizer::nextToken(XTThreadPtr self, c_char *keyword, XTToken *tk)
 
613
{
 
614
        tk->expectKeyWord(self, keyword);
 
615
        return nextToken(self);
 
616
}
 
617
 
 
618
/*
 
619
 * -----------------------------------------------------------------------
 
620
 * Parser
 
621
 */
 
622
 
 
623
/*
 
624
        We must parse the following syntax. Note that the constraints
 
625
        may be embedded in a CREATE TABLE/ALTER TABLE statement.
 
626
 
 
627
        [CONSTRAINT symbol] FOREIGN KEY [id] (index_col_name, ...)
 
628
    REFERENCES tbl_name (index_col_name, ...)
 
629
    [ON DELETE {RESTRICT | CASCADE | SET NULL | SET DEFAULT | NO ACTION}]
 
630
    [ON UPDATE {RESTRICT | CASCADE | SET NULL | SET DEFAULT | NO ACTION}]
 
631
*/
 
632
 
 
633
class XTParseTable : public XTObject {
 
634
        public: 
 
635
        void raiseError(XTThreadPtr self, XTToken *tk, int err);
 
636
 
 
637
        private:
 
638
        XTTokenizer                     *pt_tokenizer;
 
639
        XTToken                         *pt_current;
 
640
        XTStringBufferRec       pt_sbuffer;
 
641
 
 
642
        void syntaxError(XTThreadPtr self, XTToken *tk);        
 
643
 
 
644
        void parseIdentifier(XTThreadPtr self, char *name);
 
645
        int parseKeyAction(XTThreadPtr self);   
 
646
        void parseCreateTable(XTThreadPtr self);
 
647
        void parseAddTableItem(XTThreadPtr self);
 
648
        void parseQualifiedName(XTThreadPtr self, char *parent_name, char *name);
 
649
        void parseTableName(XTThreadPtr self, bool alterTable);
 
650
        void parseExpression(XTThreadPtr self, bool allow_reserved);
 
651
        void parseBrackets(XTThreadPtr self);
 
652
        void parseMoveColumn(XTThreadPtr self);
 
653
        
 
654
        /* If old_col_name is NULL, then this column is to be added,
 
655
         * if old_col_name is empty (strlen() = 0) then the column
 
656
         * exists, and should be modified, otherwize the column
 
657
         * given is to be modified.
 
658
         */
 
659
        void parseColumnDefinition(XTThreadPtr self, char *old_col_name);
 
660
        void parseDataType(XTThreadPtr self);
 
661
        void parseReferenceDefinition(XTThreadPtr self, u_int req_cols);
 
662
        void optionalIndexName(XTThreadPtr self);
 
663
        void optionalIndexType(XTThreadPtr self);
 
664
        u_int columnList(XTThreadPtr self, bool index_cols);
 
665
        void parseAlterTable(XTThreadPtr self); 
 
666
        void parseCreateIndex(XTThreadPtr self);
 
667
        void parseDropIndex(XTThreadPtr self);
 
668
 
 
669
        public: 
 
670
        XTParseTable() {
 
671
                pt_tokenizer = NULL;
 
672
                pt_current = NULL;
 
673
                memset(&pt_sbuffer, 0, sizeof(XTStringBufferRec));
 
674
        }
 
675
 
 
676
        virtual void finalize(XTThreadPtr XT_UNUSED(self)) {
 
677
                if (pt_tokenizer)
 
678
                        delete pt_tokenizer;
 
679
                xt_sb_set_size(NULL, &pt_sbuffer, 0);
 
680
        }
 
681
 
 
682
        // Hooks to receive output from the parser:
 
683
        virtual void setTableName(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(name), bool XT_UNUSED(alterTable)) {
 
684
        }
 
685
        virtual void addColumn(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(col_name), char *XT_UNUSED(old_col_name)) {
 
686
        }
 
687
        virtual void setDataType(XTThreadPtr self, char *cstring) {
 
688
                if (cstring) 
 
689
                        xt_free(self, cstring);
 
690
        }
 
691
        virtual void setNull(XTThreadPtr XT_UNUSED(self), bool XT_UNUSED(nullOK)) {
 
692
        }
 
693
        virtual void setAutoInc(XTThreadPtr XT_UNUSED(self), bool XT_UNUSED(autoInc)) {
 
694
        }
 
695
        
 
696
        /* Add a contraint. If lastColumn is TRUE then add the contraint 
 
697
         * to the last column. If not, expect addListedColumn() to be called.
 
698
         */
 
699
        virtual void addConstraint(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(name), u_int XT_UNUSED(type), bool XT_UNUSED(lastColumn)) {
 
700
        }
 
701
        
 
702
        /* Move the last column created. If symbol is NULL then move the column to the
 
703
         * first position, else move it to the position just after the given column.
 
704
         */
 
705
        virtual void moveColumn(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(col_name)) {
 
706
        }
 
707
 
 
708
        virtual void dropColumn(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(col_name)) {
 
709
        }
 
710
 
 
711
        virtual void dropConstraint(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(name), u_int XT_UNUSED(type)) {
 
712
        }
 
713
 
 
714
        virtual void setIndexName(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(name)) {
 
715
        }
 
716
        virtual void addListedColumn(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(index_col_name)) {
 
717
        }
 
718
        virtual void setReferencedTable(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(ref_schema), char *XT_UNUSED(ref_table)) {
 
719
        }
 
720
        virtual void addReferencedColumn(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(index_col_name)) {
 
721
        }
 
722
        virtual void setActions(XTThreadPtr XT_UNUSED(self), int XT_UNUSED(on_delete), int XT_UNUSED(on_update)) {
 
723
        }
 
724
 
 
725
        virtual void parseTable(XTThreadPtr self, bool convert, char *sql);     
 
726
};
 
727
 
 
728
void XTParseTable::raiseError(XTThreadPtr self, XTToken *tk, int err)
 
729
{
 
730
        char buffer[100];
 
731
 
 
732
        tk->getTokenText(buffer, 100);
 
733
        xt_throw_ixterr(XT_CONTEXT, err, buffer);
 
734
}
 
735
 
 
736
void XTParseTable::syntaxError(XTThreadPtr self, XTToken *tk)
 
737
{
 
738
        raiseError(self, tk, XT_ERR_SYNTAX);
 
739
}
 
740
 
 
741
void XTParseTable::parseIdentifier(XTThreadPtr self, char *name)
 
742
{
 
743
        pt_current->expectIdentifier(self);
 
744
        if (name) {
 
745
                if (pt_current->getString(name, XT_IDENTIFIER_NAME_SIZE) >= XT_IDENTIFIER_NAME_SIZE)
 
746
                        raiseError(self, pt_current, XT_ERR_ID_TOO_LONG);
 
747
        }
 
748
        pt_current = pt_tokenizer->nextToken(self);
 
749
}
 
750
 
 
751
int XTParseTable::parseKeyAction(XTThreadPtr self)
 
752
{
 
753
        XTToken *tk;
 
754
 
 
755
        tk = pt_tokenizer->nextToken(self);
 
756
 
 
757
        if (tk->isKeyWord("RESTRICT"))
 
758
                return XT_KEY_ACTION_RESTRICT;
 
759
 
 
760
        if (tk->isKeyWord("CASCADE"))
 
761
                return XT_KEY_ACTION_CASCADE;
 
762
 
 
763
        if (tk->isKeyWord("SET")) {
 
764
                tk = pt_tokenizer->nextToken(self);
 
765
                if (tk->isKeyWord("DEFAULT"))
 
766
                        return XT_KEY_ACTION_SET_DEFAULT;
 
767
                tk->expectKeyWord(self, "NULL");
 
768
                return XT_KEY_ACTION_SET_NULL;
 
769
        }
 
770
 
 
771
        if (tk->isKeyWord("NO")) {
 
772
                tk = pt_tokenizer->nextToken(self);
 
773
                tk->expectKeyWord(self, "ACTION");
 
774
                return XT_KEY_ACTION_NO_ACTION;
 
775
        }
 
776
 
 
777
        syntaxError(self, tk);
 
778
        return 0;
 
779
}
 
780
 
 
781
void XTParseTable::parseTable(XTThreadPtr self, bool convert, char *sql)
 
782
{
 
783
        if (pt_tokenizer)
 
784
                delete pt_tokenizer;
 
785
        pt_tokenizer = new XTTokenizer(convert, sql);
 
786
        if (!pt_tokenizer)
 
787
                xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
 
788
        pt_current = pt_tokenizer->nextToken(self);
 
789
 
 
790
        if (pt_current->isKeyWord("CREATE")) {
 
791
                pt_current = pt_tokenizer->nextToken(self);
 
792
                if (pt_current->isKeyWord("TEMPORARY") || pt_current->isKeyWord("TABLE"))
 
793
                        parseCreateTable(self);
 
794
                else
 
795
                        parseCreateIndex(self);
 
796
        }
 
797
        else if (pt_current->isKeyWord("ALTER"))
 
798
                parseAlterTable(self);
 
799
        else if (pt_current->isKeyWord("DROP"))
 
800
                parseDropIndex(self);
 
801
        else if (pt_current->isKeyWord("TRUNCATE")) {
 
802
                pt_current = pt_tokenizer->nextToken(self);
 
803
                if (pt_current->isKeyWord("TABLE"))
 
804
                        pt_current = pt_tokenizer->nextToken(self);
 
805
                parseTableName(self, true);
 
806
        }
 
807
        else if (pt_current->isKeyWord("OPTIMIZE") || pt_current->isKeyWord("REPAIR")) {
 
808
                /* OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
 
809
                 *
 
810
                 * GOTCHA: This cannot work if more than one table is specified,
 
811
                 * because then I cannot find the source table?!
 
812
                 */
 
813
                pt_current = pt_tokenizer->nextToken(self);
 
814
                while (!pt_current->isEOF() && !pt_current->isKeyWord("TABLE"))
 
815
                        pt_current = pt_tokenizer->nextToken(self);
 
816
                pt_current = pt_tokenizer->nextToken(self);
 
817
                parseTableName(self, true);
 
818
        }
 
819
        else
 
820
                syntaxError(self, pt_current);
 
821
}
 
822
 
 
823
void XTParseTable::parseCreateTable(XTThreadPtr self)
 
824
{
 
825
        if (pt_current->isKeyWord("TEMPORARY"))
 
826
                pt_current = pt_tokenizer->nextToken(self);
 
827
        pt_current = pt_tokenizer->nextToken(self, "TABLE", pt_current);
 
828
        if (pt_current->isKeyWord("IF")) {
 
829
                pt_current = pt_tokenizer->nextToken(self);
 
830
                pt_current = pt_tokenizer->nextToken(self, "NOT", pt_current);
 
831
                pt_current = pt_tokenizer->nextToken(self, "EXISTS", pt_current);
 
832
        }
 
833
 
 
834
        /* Table name is optional (when loading from dictionary)! */
 
835
        if (!pt_current->isKeyWord("("))
 
836
                parseTableName(self, false);
 
837
        else
 
838
                setTableName(self, NULL, false);
 
839
 
 
840
        /* We do not support CREATE ... SELECT! */
 
841
        if (pt_current->isKeyWord("(")) {
 
842
                pt_current = pt_tokenizer->nextToken(self);
 
843
                // Avoid this:
 
844
                // create table t3 (select group_concat(a) as a from t1 where a = 'a') union
 
845
                // (select group_concat(b) as a from t1 where a = 'b');
 
846
                if (pt_current->isKeyWord("SELECT"))
 
847
                        return;
 
848
                
 
849
                /* Allow empty table definition for temporary table. */
 
850
                while (!pt_current->isEOF() && !pt_current->isKeyWord(")")) {
 
851
                        parseAddTableItem(self);
 
852
                        if (!pt_current->isKeyWord(","))
 
853
                                break;
 
854
                        pt_current = pt_tokenizer->nextToken(self);
 
855
                }
 
856
                pt_current = pt_tokenizer->nextToken(self, ")", pt_current);
 
857
        }
 
858
}
 
859
 
 
860
void XTParseTable::parseAddTableItem(XTThreadPtr self)
 
861
{
 
862
        char name[XT_IDENTIFIER_NAME_SIZE];
 
863
 
 
864
        *name = 0;
 
865
        if (pt_current->isKeyWord("CONSTRAINT")) {
 
866
                pt_current = pt_tokenizer->nextToken(self);
 
867
                if (pt_current->isIdentifier())
 
868
                        parseQualifiedName(self, NULL, name);
 
869
        }
 
870
 
 
871
        if (pt_current->isReservedWord(XT_TK_PRIMARY)) {
 
872
                pt_current = pt_tokenizer->nextToken(self);
 
873
                pt_current = pt_tokenizer->nextToken(self, "KEY", pt_current);
 
874
 
 
875
                addConstraint(self, name, XT_DD_KEY_PRIMARY, false);
 
876
                optionalIndexType(self);
 
877
 
 
878
                /* GATCHA: Wierd?! This syntax is used in a test:
 
879
                 * alter table t1 add primary key aaa(tt);
 
880
                 */
 
881
                if (!pt_current->isKeyWord("("))
 
882
                        pt_current = pt_tokenizer->nextToken(self);
 
883
                columnList(self, true);
 
884
        }
 
885
        else if (pt_current->isReservedWord(XT_TK_UNIQUE) ||
 
886
                pt_current->isReservedWord(XT_TK_FULLTEXT) ||
 
887
                pt_current->isReservedWord(XT_TK_SPATIAL) ||
 
888
                pt_current->isReservedWord(XT_TK_INDEX) ||
 
889
                pt_current->isReservedWord(XT_TK_KEY)) {
 
890
                bool is_unique = false;
 
891
 
 
892
                if (pt_current->isReservedWord(XT_TK_FULLTEXT) || pt_current->isReservedWord(XT_TK_SPATIAL))
 
893
                        pt_current = pt_tokenizer->nextToken(self);
 
894
                else if (pt_current->isReservedWord(XT_TK_UNIQUE)) {
 
895
                        pt_current = pt_tokenizer->nextToken(self);
 
896
                        is_unique = true;
 
897
                }
 
898
                if (pt_current->isReservedWord(XT_TK_INDEX) || pt_current->isReservedWord(XT_TK_KEY))
 
899
                        pt_current = pt_tokenizer->nextToken(self);
 
900
 
 
901
                addConstraint(self, name, is_unique ? XT_DD_INDEX_UNIQUE : XT_DD_INDEX, false);
 
902
                optionalIndexName(self);
 
903
                optionalIndexType(self);
 
904
                columnList(self, true);
 
905
        }
 
906
        else if (pt_current->isReservedWord(XT_TK_CHECK)) {
 
907
                pt_current = pt_tokenizer->nextToken(self);
 
908
                parseExpression(self, false);
 
909
        }
 
910
        else if (pt_current->isReservedWord(XT_TK_FOREIGN)) {
 
911
                u_int req_cols;
 
912
 
 
913
                pt_current = pt_tokenizer->nextToken(self);
 
914
                pt_current = pt_tokenizer->nextToken(self, "KEY", pt_current);
 
915
 
 
916
                addConstraint(self, name, XT_DD_KEY_FOREIGN, false);
 
917
                optionalIndexName(self);
 
918
                req_cols = columnList(self, false);
 
919
                /* GOTCHA: According the MySQL manual this is optional, but without domains,
 
920
                 * it is required!
 
921
                 */
 
922
                parseReferenceDefinition(self, req_cols);
 
923
        }
 
924
        else if (pt_current->isKeyWord("(")) {
 
925
                pt_current = pt_tokenizer->nextToken(self);
 
926
                for (;;) {
 
927
                        parseColumnDefinition(self, NULL);
 
928
                        if (!pt_current->isKeyWord(","))
 
929
                                break;
 
930
                        pt_current = pt_tokenizer->nextToken(self);
 
931
                }
 
932
                pt_current = pt_tokenizer->nextToken(self, ")", pt_current);
 
933
        }
 
934
        else {
 
935
                if (pt_current->isReservedWord(XT_TK_COLUMN))
 
936
                        pt_current = pt_tokenizer->nextToken(self);
 
937
                parseColumnDefinition(self, NULL);
 
938
                parseMoveColumn(self);
 
939
        }
 
940
        /* GOTCHA: Support: create table t1 (a int not null, key `a` (a) key_block_size=1024)
 
941
         * and any other undocumented syntax?!
 
942
         */
 
943
        parseExpression(self, true);
 
944
}
 
945
 
 
946
void XTParseTable::parseExpression(XTThreadPtr self, bool allow_reserved)
 
947
{
 
948
        while (!pt_current->isEOF() && !pt_current->isKeyWord(",") &&
 
949
                !pt_current->isKeyWord(")") && (allow_reserved || !pt_current->isReservedWord())) {
 
950
                if (pt_current->isKeyWord("("))
 
951
                        parseBrackets(self);
 
952
                else
 
953
                        pt_current = pt_tokenizer->nextToken(self);
 
954
        }
 
955
}
 
956
 
 
957
void XTParseTable::parseBrackets(XTThreadPtr self)
 
958
{
 
959
        u_int cnt = 1;
 
960
        pt_current = pt_tokenizer->nextToken(self, "(", pt_current);
 
961
        while (cnt) {
 
962
                if (pt_current->isEOF())
 
963
                        break;
 
964
                if (pt_current->isKeyWord("("))
 
965
                        cnt++;
 
966
                if (pt_current->isKeyWord(")"))
 
967
                        cnt--;
 
968
                pt_current = pt_tokenizer->nextToken(self);
 
969
        }
 
970
}
 
971
 
 
972
void XTParseTable::parseMoveColumn(XTThreadPtr self)
 
973
{
 
974
        if (pt_current->isKeyWord("FIRST")) {
 
975
                pt_current = pt_tokenizer->nextToken(self);
 
976
                /* If name is NULL it means move to the front. */
 
977
                moveColumn(self, NULL);
 
978
        }
 
979
        else if (pt_current->isKeyWord("AFTER")) {
 
980
                char    name[XT_IDENTIFIER_NAME_SIZE];
 
981
 
 
982
                pt_current = pt_tokenizer->nextToken(self);
 
983
                parseQualifiedName(self, NULL, name);
 
984
                moveColumn(self, name);
 
985
        }
 
986
}
 
987
 
 
988
void XTParseTable::parseQualifiedName(XTThreadPtr self, char *parent_name, char *name)
 
989
{
 
990
        if (parent_name)
 
991
                parent_name[0] = '\0';
 
992
        /* Should be an identifier by I have this example:
 
993
         * CREATE TABLE t1 ( comment CHAR(32) ASCII NOT NULL, koi8_ru_f CHAR(32) CHARACTER SET koi8r NOT NULL default '' ) CHARSET=latin5;
 
994
         *
 
995
         * COMMENT is elsewhere used as reserved word?!
 
996
         */
 
997
        if (pt_current->getString(name, XT_IDENTIFIER_NAME_SIZE) >= XT_IDENTIFIER_NAME_SIZE)
 
998
                raiseError(self, pt_current, XT_ERR_ID_TOO_LONG);
 
999
        pt_current = pt_tokenizer->nextToken(self);
 
1000
        while (pt_current->isKeyWord(".")) {
 
1001
                if (parent_name)
 
1002
                        xt_strcpy(XT_IDENTIFIER_NAME_SIZE,parent_name, name);
 
1003
                pt_current = pt_tokenizer->nextToken(self);
 
1004
                /* Accept anything after the DOT! */
 
1005
                if (pt_current->getString(name, XT_IDENTIFIER_NAME_SIZE) >= XT_IDENTIFIER_NAME_SIZE)
 
1006
                        raiseError(self, pt_current, XT_ERR_ID_TOO_LONG);
 
1007
                pt_current = pt_tokenizer->nextToken(self);
 
1008
        }
 
1009
}
 
1010
 
 
1011
void XTParseTable::parseTableName(XTThreadPtr self, bool alterTable)
 
1012
{
 
1013
        char name[XT_IDENTIFIER_NAME_SIZE];
 
1014
 
 
1015
        parseQualifiedName(self, NULL, name);
 
1016
        setTableName(self, name, alterTable);
 
1017
}
 
1018
 
 
1019
void XTParseTable::parseColumnDefinition(XTThreadPtr self, char *old_col_name)
 
1020
{
 
1021
        char col_name[XT_IDENTIFIER_NAME_SIZE];
 
1022
 
 
1023
        // column_definition
 
1024
        parseQualifiedName(self, NULL, col_name);
 
1025
        addColumn(self, col_name, old_col_name);
 
1026
        parseDataType(self);
 
1027
 
 
1028
        for (;;) {
 
1029
                if (pt_current->isReservedWord(XT_TK_NOT)) {
 
1030
                        pt_current = pt_tokenizer->nextToken(self);
 
1031
                        pt_current = pt_tokenizer->nextToken(self, "NULL", pt_current);
 
1032
                        setNull(self, false);
 
1033
                }
 
1034
                else if (pt_current->isReservedWord(XT_TK_NULL)) {
 
1035
                        pt_current = pt_tokenizer->nextToken(self);
 
1036
                        setNull(self, true);
 
1037
                }
 
1038
                else if (pt_current->isReservedWord(XT_TK_DEFAULT)) {
 
1039
                        pt_current = pt_tokenizer->nextToken(self);
 
1040
                        /* Possible here [ + | - ] <value> or [ <charset> ] <string> */
 
1041
                        parseExpression(self, false);
 
1042
                }
 
1043
                else if (pt_current->isReservedWord(XT_TK_AUTO_INCREMENT)) {
 
1044
                        pt_current = pt_tokenizer->nextToken(self);
 
1045
                        setAutoInc(self, true);
 
1046
                }
 
1047
                else if (pt_current->isReservedWord(XT_TK_UNIQUE)) {
 
1048
                        pt_current = pt_tokenizer->nextToken(self);
 
1049
                        if (pt_current->isReservedWord(XT_TK_KEY))
 
1050
                                pt_current = pt_tokenizer->nextToken(self);
 
1051
                        addConstraint(self, NULL, XT_DD_INDEX_UNIQUE, true);
 
1052
                }
 
1053
                else if (pt_current->isReservedWord(XT_TK_KEY)) {
 
1054
                        pt_current = pt_tokenizer->nextToken(self);
 
1055
                        addConstraint(self, NULL, XT_DD_INDEX, true);
 
1056
                }
 
1057
                else if (pt_current->isReservedWord(XT_TK_PRIMARY)) {
 
1058
                        pt_current = pt_tokenizer->nextToken(self);
 
1059
                        pt_current = pt_tokenizer->nextToken(self, "KEY", pt_current);
 
1060
                        addConstraint(self, NULL, XT_DD_KEY_PRIMARY, true);
 
1061
                }
 
1062
                else if (pt_current->isReservedWord(XT_TK_COMMENT)) {
 
1063
                        pt_current = pt_tokenizer->nextToken(self);
 
1064
                        pt_current = pt_tokenizer->nextToken(self);
 
1065
                }
 
1066
                else if (pt_current->isReservedWord(XT_TK_REFERENCES)) {
 
1067
                        addConstraint(self, NULL, XT_DD_KEY_FOREIGN, true);
 
1068
                        parseReferenceDefinition(self, 1);
 
1069
                }
 
1070
                else if (pt_current->isReservedWord(XT_TK_CHECK)) {
 
1071
                        pt_current = pt_tokenizer->nextToken(self);
 
1072
                        parseExpression(self, false);
 
1073
                }
 
1074
                /* GOTCHA: Not in the documentation:
 
1075
                 * CREATE TABLE t1 (c varchar(255) NOT NULL COLLATE utf8_general_ci, INDEX (c))
 
1076
                 */
 
1077
                else if (pt_current->isReservedWord(XT_TK_COLLATE)) {
 
1078
                        pt_current = pt_tokenizer->nextToken(self);
 
1079
                        pt_current = pt_tokenizer->nextToken(self);
 
1080
                }
 
1081
                else
 
1082
                        break;
 
1083
        }
 
1084
}
 
1085
 
 
1086
void XTParseTable::parseDataType(XTThreadPtr self)
 
1087
{
 
1088
        /* Not actually implemented because MySQL allows undocumented
 
1089
         * syntax like this:
 
1090
         * create table t1 (c national character varying(10))
 
1091
         */
 
1092
        parseExpression(self, false);
 
1093
        setDataType(self, NULL);
 
1094
}
 
1095
 
 
1096
void XTParseTable::optionalIndexName(XTThreadPtr self)
 
1097
{
 
1098
        // [index_name]
 
1099
        if (!pt_current->isKeyWord("USING") && !pt_current->isKeyWord("(")) {
 
1100
                char name[XT_IDENTIFIER_NAME_SIZE];
 
1101
 
 
1102
                parseIdentifier(self, name);
 
1103
                setIndexName(self, name);
 
1104
        }
 
1105
}
 
1106
 
 
1107
void XTParseTable::optionalIndexType(XTThreadPtr self)
 
1108
{
 
1109
        // USING {BTREE | HASH}
 
1110
        if (pt_current->isKeyWord("USING")) {
 
1111
                pt_current = pt_tokenizer->nextToken(self);
 
1112
                pt_current = pt_tokenizer->nextToken(self);
 
1113
        }
 
1114
}
 
1115
 
 
1116
u_int XTParseTable::columnList(XTThreadPtr self, bool index_cols)
 
1117
{
 
1118
        char    name[XT_IDENTIFIER_NAME_SIZE];
 
1119
        u_int   cols = 0;
 
1120
        
 
1121
        pt_current->expectKeyWord(self, "(");
 
1122
        do {
 
1123
                pt_current = pt_tokenizer->nextToken(self);
 
1124
                parseQualifiedName(self, NULL, name);
 
1125
                addListedColumn(self, name);
 
1126
                cols++;
 
1127
                if (index_cols) {
 
1128
                        if (pt_current->isKeyWord("(")) {
 
1129
                                pt_current = pt_tokenizer->nextToken(self);
 
1130
                                pt_current = pt_tokenizer->nextToken(self);
 
1131
                                pt_current = pt_tokenizer->nextToken(self, ")", pt_current);
 
1132
                        }
 
1133
                        if (pt_current->isKeyWord("ASC"))
 
1134
                                pt_current = pt_tokenizer->nextToken(self);
 
1135
                        else if (pt_current->isKeyWord("DESC"))
 
1136
                                pt_current = pt_tokenizer->nextToken(self);
 
1137
                }
 
1138
        } while (pt_current->isKeyWord(","));
 
1139
        pt_current = pt_tokenizer->nextToken(self, ")", pt_current);
 
1140
        return cols;
 
1141
}
 
1142
 
 
1143
void XTParseTable::parseReferenceDefinition(XTThreadPtr self, u_int req_cols)
 
1144
{
 
1145
        int             on_delete = XT_KEY_ACTION_RESTRICT;
 
1146
        int             on_update = XT_KEY_ACTION_RESTRICT;
 
1147
        char    name[XT_IDENTIFIER_NAME_SIZE];
 
1148
        char    parent_name[XT_IDENTIFIER_NAME_SIZE];
 
1149
        u_int   cols = 0;
 
1150
 
 
1151
        // REFERENCES tbl_name
 
1152
        pt_current = pt_tokenizer->nextToken(self, "REFERENCES", pt_current);
 
1153
        parseQualifiedName(self, parent_name, name);
 
1154
        setReferencedTable(self, parent_name[0] ? parent_name : NULL, name);
 
1155
 
 
1156
        // [ (index_col_name,...) ]
 
1157
        if (pt_current->isKeyWord("(")) {
 
1158
                pt_current->expectKeyWord(self, "(");
 
1159
                do {
 
1160
                        pt_current = pt_tokenizer->nextToken(self);
 
1161
                        parseQualifiedName(self, NULL, name);
 
1162
                        addReferencedColumn(self, name);
 
1163
                        cols++;
 
1164
                        if (cols > req_cols)
 
1165
                                raiseError(self, pt_current, XT_ERR_INCORRECT_NO_OF_COLS);
 
1166
                } while (pt_current->isKeyWord(","));
 
1167
                if (cols != req_cols)
 
1168
                        raiseError(self, pt_current, XT_ERR_INCORRECT_NO_OF_COLS);
 
1169
                pt_current = pt_tokenizer->nextToken(self, ")", pt_current);                    
 
1170
        }
 
1171
        else
 
1172
                addReferencedColumn(self, NULL);
 
1173
 
 
1174
        // [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]
 
1175
        if (pt_current->isKeyWord("MATCH")) {
 
1176
                pt_current = pt_tokenizer->nextToken(self);
 
1177
                pt_current = pt_tokenizer->nextToken(self);
 
1178
        }
 
1179
 
 
1180
        // [ON DELETE {RESTRICT | CASCADE | SET NULL | SET DEFAULT | NO ACTION}]
 
1181
        // [ON UPDATE {RESTRICT | CASCADE | SET NULL | SET DEFAULT | NO ACTION}]
 
1182
        while (pt_current->isKeyWord("ON")) {
 
1183
                pt_current = pt_tokenizer->nextToken(self);
 
1184
                if (pt_current->isKeyWord("DELETE"))
 
1185
                        on_delete = parseKeyAction(self);
 
1186
                else if (pt_current->isKeyWord("UPDATE"))
 
1187
                        on_update = parseKeyAction(self);
 
1188
                else
 
1189
                        syntaxError(self, pt_current);
 
1190
                pt_current = pt_tokenizer->nextToken(self);
 
1191
        }
 
1192
 
 
1193
        setActions(self, on_delete, on_update);
 
1194
}
 
1195
 
 
1196
void XTParseTable::parseAlterTable(XTThreadPtr self)
 
1197
{
 
1198
        char name[XT_IDENTIFIER_NAME_SIZE];
 
1199
 
 
1200
        pt_current = pt_tokenizer->nextToken(self, "ALTER", pt_current);
 
1201
        if (pt_current->isKeyWord("IGNORE"))
 
1202
                pt_current = pt_tokenizer->nextToken(self);
 
1203
        pt_current = pt_tokenizer->nextToken(self, "TABLE", pt_current);
 
1204
        parseTableName(self, true);
 
1205
        for (;;) {
 
1206
                if (pt_current->isKeyWord("ADD")) {
 
1207
                        pt_current = pt_tokenizer->nextToken(self);
 
1208
                        parseAddTableItem(self);
 
1209
                }
 
1210
                else if (pt_current->isKeyWord("ALTER")) {
 
1211
                        pt_current = pt_tokenizer->nextToken(self);
 
1212
                        if (pt_current->isReservedWord(XT_TK_COLUMN))
 
1213
                                pt_current = pt_tokenizer->nextToken(self);
 
1214
                        pt_current->expectIdentifier(self);
 
1215
                        pt_current = pt_tokenizer->nextToken(self);
 
1216
                        if (pt_current->isKeyWord("SET")) {
 
1217
                                pt_current = pt_tokenizer->nextToken(self);
 
1218
                                pt_current = pt_tokenizer->nextToken(self, "DEFAULT", pt_current);
 
1219
                                pt_current = pt_tokenizer->nextToken(self);
 
1220
                        }
 
1221
                        else if (pt_current->isKeyWord("DROP")) {
 
1222
                                pt_current = pt_tokenizer->nextToken(self);
 
1223
                                pt_current = pt_tokenizer->nextToken(self, "DEFAULT", pt_current);
 
1224
                        }
 
1225
                }
 
1226
                else if (pt_current->isKeyWord("CHANGE")) {
 
1227
                        char old_col_name[XT_IDENTIFIER_NAME_SIZE];
 
1228
 
 
1229
                        pt_current = pt_tokenizer->nextToken(self);
 
1230
                        if (pt_current->isReservedWord(XT_TK_COLUMN))
 
1231
                                pt_current = pt_tokenizer->nextToken(self);
 
1232
 
 
1233
                        parseQualifiedName(self, NULL, old_col_name);
 
1234
                        parseColumnDefinition(self, old_col_name);
 
1235
                        parseMoveColumn(self);
 
1236
                }
 
1237
                else if (pt_current->isKeyWord("MODIFY")) {
 
1238
                        pt_current = pt_tokenizer->nextToken(self);
 
1239
                        if (pt_current->isReservedWord(XT_TK_COLUMN))
 
1240
                                pt_current = pt_tokenizer->nextToken(self);
 
1241
                        parseColumnDefinition(self, NULL);
 
1242
                        parseMoveColumn(self);
 
1243
                }
 
1244
                else if (pt_current->isKeyWord("DROP")) {
 
1245
                        pt_current = pt_tokenizer->nextToken(self);
 
1246
                        if (pt_current->isReservedWord(XT_TK_PRIMARY)) {
 
1247
                                pt_current = pt_tokenizer->nextToken(self);
 
1248
                                pt_current = pt_tokenizer->nextToken(self, "KEY", pt_current);
 
1249
                                dropConstraint(self, NULL, XT_DD_KEY_PRIMARY);
 
1250
                        }
 
1251
                        else if (pt_current->isReservedWord(XT_TK_INDEX) || pt_current->isReservedWord(XT_TK_KEY)) {
 
1252
                                pt_current = pt_tokenizer->nextToken(self);
 
1253
                                parseIdentifier(self, name);
 
1254
                                dropConstraint(self, name, XT_DD_INDEX);
 
1255
                        }
 
1256
                        else if (pt_current->isReservedWord(XT_TK_FOREIGN)) {
 
1257
                                pt_current = pt_tokenizer->nextToken(self);
 
1258
                                pt_current = pt_tokenizer->nextToken(self, "KEY", pt_current);
 
1259
                                parseIdentifier(self, name);
 
1260
                                dropConstraint(self, name, XT_DD_KEY_FOREIGN);
 
1261
                        }
 
1262
                        else {
 
1263
                                if (pt_current->isReservedWord(XT_TK_COLUMN))
 
1264
                                        pt_current = pt_tokenizer->nextToken(self);
 
1265
                                parseQualifiedName(self, NULL, name);
 
1266
                                dropColumn(self, name);
 
1267
                        }
 
1268
                }
 
1269
                else if (pt_current->isKeyWord("RENAME")) {
 
1270
                        pt_current = pt_tokenizer->nextToken(self);
 
1271
                        if (pt_current->isKeyWord("TO"))
 
1272
                                pt_current = pt_tokenizer->nextToken(self);
 
1273
                        parseQualifiedName(self, NULL, name);
 
1274
                }
 
1275
                else
 
1276
                        /* Just ignore the syntax until the next , */
 
1277
                        parseExpression(self, true);
 
1278
                if (!pt_current->isKeyWord(","))
 
1279
                        break;
 
1280
                pt_current = pt_tokenizer->nextToken(self);
 
1281
        }
 
1282
}
 
1283
 
 
1284
void XTParseTable::parseCreateIndex(XTThreadPtr self)
 
1285
{
 
1286
        char name[XT_IDENTIFIER_NAME_SIZE];
 
1287
        bool is_unique = false;
 
1288
 
 
1289
        if (pt_current->isReservedWord(XT_TK_UNIQUE)) {
 
1290
                pt_current = pt_tokenizer->nextToken(self);
 
1291
                is_unique = true;
 
1292
        }
 
1293
        else if (pt_current->isReservedWord(XT_TK_FULLTEXT))
 
1294
                pt_current = pt_tokenizer->nextToken(self);
 
1295
        else if (pt_current->isKeyWord("SPACIAL"))
 
1296
                pt_current = pt_tokenizer->nextToken(self);
 
1297
        pt_current = pt_tokenizer->nextToken(self, "INDEX", pt_current);
 
1298
        parseQualifiedName(self, NULL, name);
 
1299
        optionalIndexType(self);
 
1300
        pt_current = pt_tokenizer->nextToken(self, "ON", pt_current);
 
1301
        parseTableName(self, true);
 
1302
        addConstraint(self, NULL, is_unique ? XT_DD_INDEX_UNIQUE : XT_DD_INDEX, false);
 
1303
        setIndexName(self, name);
 
1304
        columnList(self, true);
 
1305
}
 
1306
 
 
1307
void XTParseTable::parseDropIndex(XTThreadPtr self)
 
1308
{
 
1309
        char name[XT_IDENTIFIER_NAME_SIZE];
 
1310
 
 
1311
        pt_current = pt_tokenizer->nextToken(self, "DROP", pt_current);
 
1312
        pt_current = pt_tokenizer->nextToken(self, "INDEX", pt_current);
 
1313
        parseQualifiedName(self, NULL, name);
 
1314
        pt_current = pt_tokenizer->nextToken(self, "ON", pt_current);
 
1315
        parseTableName(self, true);
 
1316
        dropConstraint(self, name, XT_DD_INDEX);
 
1317
}
 
1318
 
 
1319
/*
 
1320
 * -----------------------------------------------------------------------
 
1321
 * Create/Alter table table
 
1322
 */
 
1323
 
 
1324
class XTCreateTable : public XTParseTable {
 
1325
        public:
 
1326
        bool                                    ct_convert;
 
1327
        MX_CONST_CHARSET_INFO   *ct_charset;
 
1328
        XTPathStrPtr                    ct_tab_path;
 
1329
        u_int                                   ct_contraint_no;
 
1330
        XTDDTable                               *ct_curr_table;
 
1331
        XTDDColumn                              *ct_curr_column;
 
1332
        XTDDConstraint                  *ct_curr_constraint;
 
1333
        XTDictionaryRec                 ct_curr_dic;
 
1334
 
 
1335
        XTCreateTable(bool convert, XTPathStrPtr tab_path) : XTParseTable() {
 
1336
                ct_convert = convert;
 
1337
                ct_charset = myxt_getcharset(convert);
 
1338
                ct_tab_path = tab_path;
 
1339
                ct_curr_table = NULL;
 
1340
                ct_curr_column = NULL;
 
1341
                ct_curr_constraint = NULL;
 
1342
                memset(&ct_curr_dic, 0, sizeof(ct_curr_dic));
 
1343
        }
 
1344
 
 
1345
        virtual void finalize(XTThreadPtr self) {
 
1346
                if (ct_curr_table)
 
1347
                        ct_curr_table->release(self);
 
1348
                XTParseTable::finalize(self);
 
1349
        }
 
1350
 
 
1351
        virtual void setTableName(XTThreadPtr self, char *name, bool alterTable);
 
1352
        virtual void addColumn(XTThreadPtr self, char *col_name, char *old_col_name);
 
1353
        virtual void addConstraint(XTThreadPtr self, char *name, u_int type, bool lastColumn);
 
1354
        virtual void dropConstraint(XTThreadPtr self, char *name, u_int type);
 
1355
        virtual void addListedColumn(XTThreadPtr self, char *index_col_name);
 
1356
        virtual void setReferencedTable(XTThreadPtr self, char *ref_schema, char *ref_table);
 
1357
        virtual void addReferencedColumn(XTThreadPtr self, char *index_col_name);
 
1358
        virtual void setActions(XTThreadPtr self, int on_delete, int on_update);
 
1359
 
 
1360
        virtual void parseTable(XTThreadPtr self, bool convert, char *sql);     
 
1361
};
 
1362
 
 
1363
static void ri_free_create_table(XTThreadPtr self, XTCreateTable *ct)
 
1364
{
 
1365
        if (ct)
 
1366
                ct->release(self);
 
1367
}
 
1368
 
 
1369
XTDDTable *xt_ri_create_table(XTThreadPtr self, bool convert, XTPathStrPtr tab_path, char *sql, XTDDTable *start_tab, XTDictionaryPtr source_dic)
 
1370
{
 
1371
        XTCreateTable   *ct;
 
1372
        XTDDTable               *dd_tab;
 
1373
 
 
1374
        if (!(ct = new XTCreateTable(convert, tab_path))) {
 
1375
                if (start_tab)
 
1376
                        start_tab->release(self);
 
1377
                xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
 
1378
        }
 
1379
 
 
1380
        ct->ct_curr_table = start_tab;
 
1381
 
 
1382
        pushr_(ri_free_create_table, ct);
 
1383
 
 
1384
        ct->parseTable(self, convert, sql);
 
1385
        
 
1386
        /* Return the table ... */
 
1387
        dd_tab = ct->ct_curr_table;
 
1388
        ct->ct_curr_table = NULL;
 
1389
        
 
1390
        if (source_dic)
 
1391
                *source_dic = ct->ct_curr_dic;
 
1392
 
 
1393
        freer_();
 
1394
        return dd_tab;
 
1395
}
 
1396
 
 
1397
void XTCreateTable::parseTable(XTThreadPtr self, bool convert, char *sql)
 
1398
{
 
1399
        u_int i;
 
1400
 
 
1401
        ct_contraint_no = 0;
 
1402
        XTParseTable::parseTable(self, convert, sql);
 
1403
 
 
1404
        /* Remove contraints that do not have matching columns. */
 
1405
        for (i=0; i<ct_curr_table->dt_indexes.size();) {
 
1406
                if (!ct_curr_table->dt_indexes.itemAt(i)->attachColumns())
 
1407
                        ct_curr_table->dt_indexes.remove(self, i);
 
1408
                else
 
1409
                        i++;
 
1410
        }
 
1411
 
 
1412
        for (i=0; i<ct_curr_table->dt_fkeys.size(); ) {
 
1413
                if (!ct_curr_table->dt_fkeys.itemAt(i)->attachColumns())
 
1414
                        ct_curr_table->dt_fkeys.remove(self, i);
 
1415
                else
 
1416
                        i++;
 
1417
        }
 
1418
}
 
1419
 
 
1420
void XTCreateTable::setTableName(XTThreadPtr self, char *name, bool alterTable)
 
1421
{
 
1422
        char path[PATH_MAX];
 
1423
 
 
1424
        if (!name)
 
1425
                return;
 
1426
 
 
1427
        xt_strcpy(PATH_MAX, path, ct_tab_path->ps_path);
 
1428
        xt_remove_last_name_of_path(path);
 
1429
 
 
1430
        if (ct_convert) {
 
1431
                char    buffer[XT_IDENTIFIER_NAME_SIZE];
 
1432
                size_t  len;
 
1433
 
 
1434
                myxt_static_convert_identifier(self, ct_charset, name, buffer, XT_IDENTIFIER_NAME_SIZE);
 
1435
                len = strlen(path);
 
1436
                myxt_static_convert_table_name(self, buffer, &path[len], PATH_MAX - len);
 
1437
        }
 
1438
        else
 
1439
                xt_strcat(PATH_MAX, path, name);
 
1440
 
 
1441
        if (alterTable) {
 
1442
                XTTableHPtr     tab;
 
1443
 
 
1444
                /* Find the table... */
 
1445
                pushsr_(tab, xt_heap_release, xt_use_table(self, (XTPathStrPtr) path, FALSE, TRUE));
 
1446
 
 
1447
                /* Clone the foreign key definitions: */
 
1448
                if (tab) {
 
1449
                        if (tab->tab_dic.dic_table) {
 
1450
                                ct_curr_table->dt_fkeys.deleteAll(self);
 
1451
                                ct_curr_table->dt_fkeys.clone(self, &tab->tab_dic.dic_table->dt_fkeys); 
 
1452
                                for (u_int i=0; i<ct_curr_table->dt_fkeys.size(); i++)
 
1453
                                        ct_curr_table->dt_fkeys.itemAt(i)->co_table = ct_curr_table;
 
1454
                        }
 
1455
 
 
1456
                        /* Copy the dictionary! */
 
1457
                        ct_curr_dic = tab->tab_dic;
 
1458
                }
 
1459
 
 
1460
                freer_(); // xt_heap_release(tab)
 
1461
        }
 
1462
}
 
1463
 
 
1464
/*
 
1465
 * old_name is given if the column name was changed.
 
1466
 * NOTE that we built the table desciption from the current MySQL table
 
1467
 * description. This means that all changes to columns and 
 
1468
 * indexes have already been applied.
 
1469
 *
 
1470
 * Our job is to now add the foreign key changes.
 
1471
 * This means we have to note the current column here. It is
 
1472
 * possible to add a FOREIGN KEY contraint directly to a column!
 
1473
 */
 
1474
void XTCreateTable::addColumn(XTThreadPtr self, char *new_name, char *old_name)
 
1475
{
 
1476
        char new_col_name[XT_IDENTIFIER_NAME_SIZE];
 
1477
 
 
1478
        myxt_static_convert_identifier(self, ct_charset, new_name, new_col_name, XT_IDENTIFIER_NAME_SIZE);
 
1479
        ct_curr_column = ct_curr_table->findColumn(new_col_name);
 
1480
        if (old_name) {
 
1481
                char old_col_name[XT_IDENTIFIER_NAME_SIZE];
 
1482
 
 
1483
                myxt_static_convert_identifier(self, ct_charset, old_name, old_col_name, XT_IDENTIFIER_NAME_SIZE);
 
1484
                ct_curr_table->alterColumnName(self, old_col_name, new_col_name);
 
1485
        }
 
1486
}
 
1487
 
 
1488
void XTCreateTable::addConstraint(XTThreadPtr self, char *name, u_int type, bool lastColumn)
 
1489
{
 
1490
        /* We are only interested in foreign keys! */
 
1491
        if (type == XT_DD_KEY_FOREIGN) {
 
1492
                char buffer[50];
 
1493
 
 
1494
                if (!(ct_curr_constraint = new XTDDForeignKey()))
 
1495
                        xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
 
1496
                ct_curr_table->dt_fkeys.append(self, (XTDDForeignKey *) ct_curr_constraint);
 
1497
                ct_curr_constraint->co_table = ct_curr_table;
 
1498
 
 
1499
                if (name && *name)
 
1500
                        ct_curr_constraint->co_name = myxt_convert_identifier(self, ct_charset, name);
 
1501
                else {
 
1502
                        // Generate a default constraint name:
 
1503
                        ct_contraint_no++;
 
1504
                        sprintf(buffer, "FOREIGN_%d", ct_contraint_no);
 
1505
                        ct_curr_constraint->co_name = xt_dup_string(self, buffer);
 
1506
                }
 
1507
 
 
1508
                if (lastColumn && ct_curr_column) {
 
1509
                        /* This constraint has one column, the current column. */
 
1510
                        XTDDColumnRef   *cref;
 
1511
                        char                    *col_name = xt_dup_string(self, ct_curr_column->dc_name);
 
1512
 
 
1513
                        if (!(cref = new XTDDColumnRef())) {
 
1514
                                xt_free(self, col_name);
 
1515
                                xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
 
1516
                        }
 
1517
                        cref->cr_col_name = col_name;
 
1518
                        ct_curr_constraint->co_cols.append(self, cref);
 
1519
                }
 
1520
        }
 
1521
        else
 
1522
                /* Other constraints/indexes do not interest us: */
 
1523
                ct_curr_constraint = NULL;
 
1524
}
 
1525
 
 
1526
void XTCreateTable::dropConstraint(XTThreadPtr self, char *name, u_int type)
 
1527
{
 
1528
        if (type == XT_DD_KEY_FOREIGN && name) {
 
1529
                u_int                   i;
 
1530
                XTDDForeignKey  *fkey;
 
1531
                char                    con_name[XT_IDENTIFIER_NAME_SIZE];
 
1532
 
 
1533
                myxt_static_convert_identifier(self, ct_charset, name, con_name, XT_IDENTIFIER_NAME_SIZE);
 
1534
                for (i=0; i<ct_curr_table->dt_fkeys.size(); i++) {
 
1535
                        fkey = ct_curr_table->dt_fkeys.itemAt(i);
 
1536
                        if (fkey->co_name && myxt_strcasecmp(con_name, fkey->co_name) == 0) {
 
1537
                                ct_curr_table->dt_fkeys.remove(fkey);
 
1538
                                fkey->release(self);
 
1539
                        }
 
1540
                }
 
1541
        }
 
1542
}
 
1543
 
 
1544
void XTCreateTable::addListedColumn(XTThreadPtr self, char *index_col_name)
 
1545
{
 
1546
        if (ct_curr_constraint && ct_curr_constraint->co_type == XT_DD_KEY_FOREIGN) {
 
1547
                XTDDColumnRef   *cref;
 
1548
                char                    *name = myxt_convert_identifier(self, ct_charset, index_col_name);
 
1549
 
 
1550
                if (!(cref = new XTDDColumnRef())) {
 
1551
                        xt_free(self, name);
 
1552
                        xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
 
1553
                }
 
1554
                cref->cr_col_name = name;
 
1555
                ct_curr_constraint->co_cols.append(self, cref);
 
1556
        }
 
1557
}
 
1558
 
 
1559
void XTCreateTable::setReferencedTable(XTThreadPtr self, char *ref_schema, char *ref_table)
 
1560
{
 
1561
        XTDDForeignKey  *fk = (XTDDForeignKey *) ct_curr_constraint;
 
1562
        char                    path[PATH_MAX];
 
1563
 
 
1564
        if (ref_schema) {
 
1565
                xt_strcpy(PATH_MAX,path, ".");
 
1566
                xt_add_dir_char(PATH_MAX, path);
 
1567
                xt_strcat(PATH_MAX, path, ref_schema);
 
1568
                xt_add_dir_char(PATH_MAX, path);
 
1569
                xt_strcat(PATH_MAX, path, ref_table);
 
1570
        } else {
 
1571
                xt_strcpy(PATH_MAX, path, ct_tab_path->ps_path);
 
1572
                xt_remove_last_name_of_path(path);
 
1573
                if (ct_convert) {
 
1574
                        char    buffer[XT_IDENTIFIER_NAME_SIZE];
 
1575
                        size_t  len;
 
1576
 
 
1577
                        myxt_static_convert_identifier(self, ct_charset, ref_table, buffer, XT_IDENTIFIER_NAME_SIZE);
 
1578
                        len = strlen(path);
 
1579
                        myxt_static_convert_table_name(self, buffer, &path[len], PATH_MAX - len);
 
1580
                }
 
1581
                else
 
1582
                        xt_strcat(PATH_MAX, path, ref_table);
 
1583
        }
 
1584
 
 
1585
        fk->fk_ref_tab_name = (XTPathStrPtr) xt_dup_string(self, path);
 
1586
}
 
1587
 
 
1588
/* If the referenced column is NULL, this means 
 
1589
 * duplicate the local column list!
 
1590
 */
 
1591
void XTCreateTable::addReferencedColumn(XTThreadPtr self, char *index_col_name)
 
1592
{
 
1593
        XTDDForeignKey  *fk = (XTDDForeignKey *) ct_curr_constraint;
 
1594
        XTDDColumnRef   *cref;
 
1595
        char                    *name;
 
1596
 
 
1597
        if (index_col_name) {
 
1598
                name = myxt_convert_identifier(self, ct_charset, index_col_name);
 
1599
                if (!(cref = new XTDDColumnRef())) {
 
1600
                        xt_free(self, name);
 
1601
                        xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
 
1602
                }
 
1603
                cref->cr_col_name = name;
 
1604
                fk->fk_ref_cols.append(self, cref);
 
1605
        }
 
1606
        else
 
1607
                fk->fk_ref_cols.clone(self, &fk->co_cols);
 
1608
}
 
1609
 
 
1610
void XTCreateTable::setActions(XTThreadPtr XT_UNUSED(self), int on_delete, int on_update)
 
1611
{
 
1612
        XTDDForeignKey  *fk = (XTDDForeignKey *) ct_curr_constraint;
 
1613
 
 
1614
        fk->fk_on_delete = on_delete;
 
1615
        fk->fk_on_update = on_update;
 
1616
}
 
1617
 
 
1618
/*
 
1619
 * -----------------------------------------------------------------------
 
1620
 * Dictionary methods
 
1621
 */
 
1622
 
 
1623
void XTDDColumn::init(XTThreadPtr self, XTObject *obj) {
 
1624
        XTDDColumn *col = (XTDDColumn *) obj;
 
1625
 
 
1626
        XTObject::init(self, obj);
 
1627
        if (col->dc_name)
 
1628
                dc_name = xt_dup_string(self, col->dc_name);
 
1629
        if (col->dc_data_type)
 
1630
                dc_data_type = xt_dup_string(self, col->dc_data_type);
 
1631
        dc_null_ok = col->dc_null_ok;
 
1632
        dc_auto_inc = col->dc_auto_inc;
 
1633
}
 
1634
 
 
1635
void XTDDColumn::finalize(XTThreadPtr self)
 
1636
{
 
1637
        if (dc_name)
 
1638
                xt_free(self, dc_name);
 
1639
        if (dc_data_type)
 
1640
                xt_free(self, dc_data_type);
 
1641
}
 
1642
 
 
1643
void XTDDColumn::loadString(XTThreadPtr self, XTStringBufferPtr sb)
 
1644
{
 
1645
        xt_sb_concat(self, sb, "`");
 
1646
        xt_sb_concat(self, sb, dc_name);
 
1647
        xt_sb_concat(self, sb, "` ");
 
1648
        if (dc_data_type) {
 
1649
                xt_sb_concat(self, sb, dc_data_type);
 
1650
                if (dc_null_ok)
 
1651
                        xt_sb_concat(self, sb, " NULL");
 
1652
                else
 
1653
                        xt_sb_concat(self, sb, " NOT NULL");
 
1654
                if (dc_auto_inc)
 
1655
                        xt_sb_concat(self, sb, " AUTO_INCREMENT");
 
1656
        }
 
1657
}
 
1658
 
 
1659
void  XTDDColumnRef::init(XTThreadPtr self, XTObject *obj)
 
1660
{
 
1661
        XTDDColumnRef *cr = (XTDDColumnRef *) obj;
 
1662
 
 
1663
        XTObject::init(self, obj);
 
1664
        cr_col_name = xt_dup_string(self, cr->cr_col_name);
 
1665
}
 
1666
 
 
1667
void XTDDColumnRef::finalize(XTThreadPtr self)
 
1668
{
 
1669
        XTObject::finalize(self);
 
1670
        if (cr_col_name) {
 
1671
                xt_free(self, cr_col_name);
 
1672
                cr_col_name = NULL;
 
1673
        }
 
1674
}
 
1675
 
 
1676
void  XTDDConstraint::init(XTThreadPtr self, XTObject *obj)
 
1677
{
 
1678
        XTDDConstraint *co = (XTDDConstraint *) obj;
 
1679
 
 
1680
        XTObject::init(self, obj);
 
1681
        co_type = co->co_type;
 
1682
        if (co->co_name)
 
1683
                co_name = xt_dup_string(self, co->co_name);
 
1684
        if (co->co_ind_name)
 
1685
                co_ind_name = xt_dup_string(self, co->co_ind_name);
 
1686
        co_cols.clone(self, &co->co_cols);
 
1687
}
 
1688
 
 
1689
void XTDDConstraint::loadString(XTThreadPtr self, XTStringBufferPtr sb)
 
1690
{
 
1691
        if (co_name) {
 
1692
                xt_sb_concat(self, sb, "CONSTRAINT `");
 
1693
                xt_sb_concat(self, sb, co_name);
 
1694
                xt_sb_concat(self, sb, "` ");
 
1695
        }
 
1696
        switch (co_type) {
 
1697
                case XT_DD_INDEX:
 
1698
                        xt_sb_concat(self, sb, "INDEX ");
 
1699
                        break;
 
1700
                case XT_DD_INDEX_UNIQUE:
 
1701
                        xt_sb_concat(self, sb, "UNIQUE INDEX ");
 
1702
                        break;
 
1703
                case XT_DD_KEY_PRIMARY:
 
1704
                        xt_sb_concat(self, sb, "PRIMARY KEY ");
 
1705
                        break;
 
1706
                case XT_DD_KEY_FOREIGN:
 
1707
                        xt_sb_concat(self, sb, "FOREIGN KEY ");
 
1708
                        break;          
 
1709
        }
 
1710
        if (co_ind_name) {
 
1711
                xt_sb_concat(self, sb, "`");
 
1712
                xt_sb_concat(self, sb, co_ind_name);
 
1713
                xt_sb_concat(self, sb, "` ");
 
1714
        }
 
1715
        xt_sb_concat(self, sb, "(`");
 
1716
        xt_sb_concat(self, sb, co_cols.itemAt(0)->cr_col_name);
 
1717
        for (u_int i=1; i<co_cols.size(); i++) {
 
1718
                xt_sb_concat(self, sb, "`, `");
 
1719
                xt_sb_concat(self, sb, co_cols.itemAt(i)->cr_col_name);
 
1720
        }
 
1721
        xt_sb_concat(self, sb, "`)");
 
1722
}
 
1723
 
 
1724
void XTDDConstraint::alterColumnName(XTThreadPtr self, char *from_name, char *to_name)
 
1725
{
 
1726
        XTDDColumnRef *col;
 
1727
 
 
1728
        for (u_int i=0; i<co_cols.size(); i++) {
 
1729
                col = co_cols.itemAt(i);
 
1730
                if (myxt_strcasecmp(col->cr_col_name, from_name) == 0) {
 
1731
                        char *name = xt_dup_string(self, to_name);
 
1732
 
 
1733
                        xt_free(self, col->cr_col_name);
 
1734
                        col->cr_col_name = name;
 
1735
                        break;
 
1736
                }
 
1737
        }
 
1738
}
 
1739
 
 
1740
void XTDDConstraint::getColumnList(char *buffer, size_t size)
 
1741
{
 
1742
        if (co_table->dt_table) {
 
1743
                xt_strcpy(size, buffer, "`");
 
1744
                xt_strcat(size, buffer, co_table->dt_table->tab_name->ps_path);
 
1745
                xt_strcat(size, buffer, "` (`");
 
1746
        }
 
1747
        else
 
1748
                xt_strcpy(size, buffer, "(`");
 
1749
        xt_strcat(size, buffer, co_cols.itemAt(0)->cr_col_name);
 
1750
        for (u_int i=1; i<co_cols.size(); i++) {
 
1751
                xt_strcat(size, buffer, "`, `");
 
1752
                xt_strcat(size, buffer, co_cols.itemAt(i)->cr_col_name);
 
1753
        }
 
1754
        xt_strcat(size, buffer, "`)");
 
1755
}
 
1756
 
 
1757
bool XTDDConstraint::sameColumns(XTDDConstraint *co)
 
1758
{
 
1759
        u_int i = 0;
 
1760
 
 
1761
        if (co_cols.size() != co->co_cols.size())
 
1762
                return false;
 
1763
        while (i<co_cols.size()) {
 
1764
                if (myxt_strcasecmp(co_cols.itemAt(i)->cr_col_name, co->co_cols.itemAt(i)->cr_col_name) != 0)
 
1765
                        return false;
 
1766
                i++;
 
1767
        }
 
1768
        return OK;
 
1769
}
 
1770
 
 
1771
bool XTDDConstraint::samePrefixColumns(XTDDConstraint *co)
 
1772
{
 
1773
        u_int i = 0;
 
1774
 
 
1775
        if (co_cols.size() > co->co_cols.size())
 
1776
                return false;
 
1777
        while (i<co_cols.size()) {
 
1778
                if (myxt_strcasecmp(co_cols.itemAt(i)->cr_col_name, co->co_cols.itemAt(i)->cr_col_name) != 0)
 
1779
                        return false;
 
1780
                i++;
 
1781
        }
 
1782
        return OK;
 
1783
}
 
1784
 
 
1785
bool XTDDConstraint::attachColumns()
 
1786
{
 
1787
        XTDDColumn              *col;
 
1788
 
 
1789
        for (u_int i=0; i<co_cols.size(); i++) {
 
1790
                if (!(col = co_table->findColumn(co_cols.itemAt(i)->cr_col_name)))
 
1791
                        return false;
 
1792
                /* If this is a primary key, then the column becomes not-null! */
 
1793
                if (co_type == XT_DD_KEY_PRIMARY)
 
1794
                        col->dc_null_ok = false;
 
1795
        }
 
1796
        return true;
 
1797
}
 
1798
 
 
1799
void XTDDTableRef::finalize(XTThreadPtr self)
 
1800
{
 
1801
        XTDDForeignKey  *fk;
 
1802
 
 
1803
        if ((fk = tr_fkey)) {
 
1804
                tr_fkey = NULL;
 
1805
                fk->removeReference(self);
 
1806
                xt_heap_release(self, fk->co_table->dt_table); /* We referenced the database table, not the foreign key */
 
1807
        }
 
1808
        XTObject::finalize(self);
 
1809
}
 
1810
 
 
1811
bool XTDDTableRef::checkReference(xtWord1 *before_buf, XTThreadPtr thread)
 
1812
{
 
1813
        XTIndexPtr                      loc_ind, ind;
 
1814
        xtBool                          no_null = TRUE;
 
1815
        XTOpenTablePtr          ot;
 
1816
        XTIdxSearchKeyRec       search_key;
 
1817
        xtXactID                        xn_id;
 
1818
        XTXactWaitRec           xw;
 
1819
        bool                            ok = false;
 
1820
 
 
1821
        if (!(loc_ind = tr_fkey->getReferenceIndexPtr()))
 
1822
                return false;
 
1823
 
 
1824
        if (!(ind = tr_fkey->getIndexPtr()))
 
1825
                return false;
 
1826
 
 
1827
        search_key.sk_key_value.sv_flags = 0;
 
1828
        search_key.sk_key_value.sv_rec_id = 0;
 
1829
        search_key.sk_key_value.sv_row_id = 0;
 
1830
        search_key.sk_key_value.sv_key = search_key.sk_key_buf;
 
1831
        search_key.sk_key_value.sv_length = myxt_create_foreign_key_from_row(loc_ind, search_key.sk_key_buf, before_buf, ind, &no_null);
 
1832
        search_key.sk_on_key = FALSE;
 
1833
 
 
1834
        if (!no_null)
 
1835
                return true;
 
1836
 
 
1837
        /* Search for the key in the child (referencing) table: */
 
1838
        if (!(ot = xt_db_open_table_using_tab(tr_fkey->co_table->dt_table, thread)))
 
1839
                return false;
 
1840
 
 
1841
        retry:
 
1842
        if (!xt_idx_search(ot, ind, &search_key))
 
1843
                goto done;
 
1844
                
 
1845
        while (ot->ot_curr_rec_id && search_key.sk_on_key) {
 
1846
                switch (xt_tab_maybe_committed(ot, ot->ot_curr_rec_id, &xn_id, &ot->ot_curr_row_id, &ot->ot_curr_updated)) {
 
1847
                        case XT_MAYBE:                          
 
1848
                                xw.xw_xn_id = xn_id;
 
1849
                                if (!xt_xn_wait_for_xact(thread, &xw, NULL))
 
1850
                                        goto done;
 
1851
                                goto retry;
 
1852
                        case XT_ERR:
 
1853
                                goto done;
 
1854
                        case TRUE:
 
1855
                                /* We found a matching child: */
 
1856
                                xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_ROW_IS_REFERENCED, tr_fkey->co_name);
 
1857
                                goto done;
 
1858
                        case FALSE:
 
1859
                                if (!xt_idx_next(ot, ind, &search_key))
 
1860
                                        goto done;
 
1861
                                break;
 
1862
                }
 
1863
        }
 
1864
 
 
1865
        /* No matching children, all OK: */
 
1866
        ok = true;
 
1867
 
 
1868
        done:
 
1869
        if (ot->ot_ind_rhandle) {
 
1870
                xt_ind_release_handle(ot->ot_ind_rhandle, FALSE, thread);
 
1871
                ot->ot_ind_rhandle = NULL;
 
1872
        }
 
1873
        xt_db_return_table_to_pool_ns(ot);
 
1874
        return ok;
 
1875
}
 
1876
 
 
1877
/*
 
1878
 * A row has been deleted or updated (after_buf non-NULL), check if it is referenced by the foreign key table.
 
1879
 * If it is referenced, then we need to follow the specified action.
 
1880
 */
 
1881
bool XTDDTableRef::modifyRow(XTOpenTablePtr XT_UNUSED(ref_ot), xtWord1 *before_buf, xtWord1 *after_buf, XTThreadPtr thread)
 
1882
{
 
1883
        XTIndexPtr                      loc_ind, ind;
 
1884
        xtBool                          no_null = TRUE;
 
1885
        XTOpenTablePtr          ot;
 
1886
        XTIdxSearchKeyRec       search_key;
 
1887
        xtXactID                        xn_id;
 
1888
        int                                     action = after_buf ? tr_fkey->fk_on_update : tr_fkey->fk_on_delete;
 
1889
        u_int                           after_key_len = 0;
 
1890
        xtWord1                         *after_key = NULL;
 
1891
        XTInfoBufferRec         after_info;
 
1892
        XTXactWaitRec           xw;
 
1893
 
 
1894
        after_info.ib_free = FALSE;
 
1895
 
 
1896
        if (!(loc_ind = tr_fkey->getReferenceIndexPtr()))
 
1897
                return false;
 
1898
 
 
1899
        if (!(ind = tr_fkey->getIndexPtr()))
 
1900
                return false;
 
1901
 
 
1902
        search_key.sk_key_value.sv_flags = 0;
 
1903
        search_key.sk_key_value.sv_rec_id = 0;
 
1904
        search_key.sk_key_value.sv_row_id = 0;
 
1905
        search_key.sk_key_value.sv_key = search_key.sk_key_buf;
 
1906
        search_key.sk_key_value.sv_length = myxt_create_foreign_key_from_row(loc_ind, search_key.sk_key_buf, before_buf, ind, &no_null);
 
1907
        search_key.sk_on_key = FALSE;
 
1908
 
 
1909
        if (!no_null)
 
1910
                return true;
 
1911
 
 
1912
        if (after_buf) {
 
1913
                if (!(after_key = (xtWord1 *) xt_malloc_ns(XT_INDEX_MAX_KEY_SIZE)))
 
1914
                        return false;
 
1915
                after_key_len = myxt_create_foreign_key_from_row(loc_ind, after_key, after_buf, ind, NULL);
 
1916
                
 
1917
                /* Check whether the key value has changed, if not, we have nothing
 
1918
                 * to do here!
 
1919
                 */
 
1920
                if (myxt_compare_key(ind, 0, search_key.sk_key_value.sv_length,
 
1921
                        search_key.sk_key_value.sv_key, after_key) == 0)
 
1922
                        goto success;
 
1923
 
 
1924
        }
 
1925
 
 
1926
        /* Search for the key in the child (referencing) table: */
 
1927
        if (!(ot = xt_db_open_table_using_tab(tr_fkey->co_table->dt_table, thread)))
 
1928
                goto failed;
 
1929
 
 
1930
        retry:
 
1931
        if (!xt_idx_search(ot, ind, &search_key))
 
1932
                goto failed_2;
 
1933
                
 
1934
        while (ot->ot_curr_rec_id && search_key.sk_on_key) {
 
1935
                switch (xt_tab_maybe_committed(ot, ot->ot_curr_rec_id, &xn_id, &ot->ot_curr_row_id, &ot->ot_curr_updated)) {
 
1936
                        case XT_MAYBE:
 
1937
                                xw.xw_xn_id = xn_id;
 
1938
                                if (!xt_xn_wait_for_xact(thread, &xw, NULL))
 
1939
                                        goto failed_2;
 
1940
                                goto retry;
 
1941
                        case XT_ERR:
 
1942
                                goto failed_2;
 
1943
                        case TRUE:
 
1944
                                /* We found a matching child: */
 
1945
                                switch (action) {
 
1946
                                        case XT_KEY_ACTION_CASCADE:
 
1947
                                                if (after_buf) {
 
1948
                                                        /* Do a cascaded update: */
 
1949
                                                        if (!xt_tab_load_record(ot, ot->ot_curr_rec_id, &after_info))
 
1950
                                                                goto failed_2;
 
1951
 
 
1952
                                                        if (!myxt_create_row_from_key(ot, ind, after_key, after_key_len, after_info.ib_db.db_data))
 
1953
                                                                goto failed_2;
 
1954
 
 
1955
                                                        if (!xt_tab_update_record(ot, NULL, after_info.ib_db.db_data)) {
 
1956
                                                                // Change to duplicate foreign key
 
1957
                                                                if (ot->ot_thread->t_exception.e_xt_err == XT_ERR_DUPLICATE_KEY)
 
1958
                                                                        xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_DUPLICATE_FKEY, tr_fkey->co_name);
 
1959
                                                                goto failed_2;
 
1960
                                                        }
 
1961
                                                }
 
1962
                                                else {
 
1963
                                                        /* Do a cascaded delete: */
 
1964
                                                        if (!xt_tab_delete_record(ot, NULL))
 
1965
                                                                goto failed_2;
 
1966
                                                }
 
1967
                                                break;
 
1968
                                        case XT_KEY_ACTION_SET_NULL:
 
1969
                                                if (!xt_tab_load_record(ot, ot->ot_curr_rec_id, &after_info))
 
1970
                                                        goto failed_2;
 
1971
 
 
1972
                                                myxt_set_null_row_from_key(ot, ind, after_info.ib_db.db_data);
 
1973
 
 
1974
                                                if (!xt_tab_update_record(ot, NULL, after_info.ib_db.db_data))
 
1975
                                                        goto failed_2;
 
1976
                                                break;
 
1977
                                        case XT_KEY_ACTION_SET_DEFAULT:
 
1978
 
 
1979
                                                if (!xt_tab_load_record(ot, ot->ot_curr_rec_id, &after_info))
 
1980
                                                        goto failed_2;
 
1981
 
 
1982
                                                myxt_set_default_row_from_key(ot, ind, after_info.ib_db.db_data);
 
1983
 
 
1984
                                                if (!xt_tab_update_record(ot, NULL, after_info.ib_db.db_data))
 
1985
                                                        goto failed_2;
 
1986
 
 
1987
                                                break;
 
1988
                                        case XT_KEY_ACTION_NO_ACTION:
 
1989
#ifdef XT_IMPLEMENT_NO_ACTION
 
1990
                                                XTRestrictItemRec       r;
 
1991
                                                
 
1992
                                                r.ri_tab_id = ref_ot->ot_table->tab_id;
 
1993
                                                r.ri_rec_id = ref_ot->ot_curr_rec_id;
 
1994
                                                if (!xt_bl_append(NULL, &thread->st_restrict_list, (void *) &r))
 
1995
                                                        goto failed_2;
 
1996
                                                break;
 
1997
#endif
 
1998
                                        default:
 
1999
                                                xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_ROW_IS_REFERENCED, tr_fkey->co_name);
 
2000
                                                goto failed_2;
 
2001
                                }
 
2002
                                /* Fall throught to next: */
 
2003
                        case FALSE:
 
2004
                                if (!xt_idx_next(ot, ind, &search_key))
 
2005
                                        goto failed_2;
 
2006
                                break;
 
2007
                }
 
2008
        }
 
2009
 
 
2010
        /* No matching children, all OK: */
 
2011
        if (ot->ot_ind_rhandle) {
 
2012
                xt_ind_release_handle(ot->ot_ind_rhandle, FALSE, thread);
 
2013
                ot->ot_ind_rhandle = NULL;
 
2014
        }
 
2015
        xt_db_return_table_to_pool_ns(ot);
 
2016
 
 
2017
        success:
 
2018
        xt_ib_free(NULL, &after_info);
 
2019
        if (after_key)
 
2020
                xt_free_ns(after_key);
 
2021
        return true;
 
2022
 
 
2023
        failed_2:
 
2024
        if (ot->ot_ind_rhandle) {
 
2025
                xt_ind_release_handle(ot->ot_ind_rhandle, FALSE, thread);
 
2026
                ot->ot_ind_rhandle = NULL;
 
2027
        }
 
2028
        xt_db_return_table_to_pool_ns(ot);
 
2029
 
 
2030
        failed:
 
2031
        xt_ib_free(NULL, &after_info);
 
2032
        if (after_key)
 
2033
                xt_free_ns(after_key);
 
2034
        return false;
 
2035
}
 
2036
 
 
2037
void XTDDTableRef::deleteAllRows(XTThreadPtr self)
 
2038
{
 
2039
        XTOpenTablePtr  ot;
 
2040
        xtBool                  eof;
 
2041
        xtWord1                 *buffer;
 
2042
 
 
2043
        if (!tr_fkey->getReferenceIndexPtr())
 
2044
                xt_throw(self);
 
2045
 
 
2046
        if (!tr_fkey->getIndexPtr())
 
2047
                xt_throw(self);
 
2048
 
 
2049
        if (!(ot = xt_db_open_table_using_tab(tr_fkey->co_table->dt_table, self)))
 
2050
                xt_throw(self);
 
2051
 
 
2052
        /* Check if there are any rows in the referencing table: */
 
2053
        if (!xt_tab_seq_init(ot))
 
2054
                goto failed;
 
2055
 
 
2056
        if (!(buffer = (xtWord1 *) xt_malloc(self, ot->ot_table->tab_dic.dic_mysql_buf_size)))
 
2057
                goto failed_1;
 
2058
 
 
2059
        if (!xt_tab_seq_next(ot, buffer, &eof))
 
2060
                goto failed_2;
 
2061
 
 
2062
        xt_free(self, buffer);
 
2063
 
 
2064
        xt_tab_seq_exit(ot);
 
2065
 
 
2066
        xt_db_return_table_to_pool_ns(ot);
 
2067
 
 
2068
        if (!eof)
 
2069
                xt_throw_ixterr(XT_CONTEXT, XT_ERR_ROW_IS_REFERENCED, tr_fkey->co_name);
 
2070
        return;
 
2071
 
 
2072
        failed_2:
 
2073
        xt_free(self, buffer);
 
2074
 
 
2075
        failed_1:
 
2076
        xt_tab_seq_exit(ot);
 
2077
 
 
2078
        failed:
 
2079
        xt_db_return_table_to_pool_ns(ot);
 
2080
        xt_throw(self);
 
2081
}
 
2082
 
 
2083
void  XTDDIndex::init(XTThreadPtr self, XTObject *obj)
 
2084
{
 
2085
        XTDDConstraint::init(self, obj);
 
2086
}
 
2087
 
 
2088
XTIndexPtr XTDDIndex::getIndexPtr()
 
2089
{
 
2090
        if (in_index >= co_table->dt_table->tab_dic.dic_key_count) {
 
2091
                XTDDIndex               *in;
 
2092
 
 
2093
                if (!(in = co_table->findIndex(this)))
 
2094
                        return NULL;
 
2095
                in_index = in->in_index;
 
2096
        }
 
2097
        return co_table->dt_table->tab_dic.dic_keys[in_index];
 
2098
}
 
2099
 
 
2100
void XTDDForeignKey::init(XTThreadPtr self, XTObject *obj)
 
2101
{
 
2102
        XTDDForeignKey *fk = (XTDDForeignKey *) obj;
 
2103
 
 
2104
        XTDDIndex::init(self, obj);
 
2105
        if (fk->fk_ref_tab_name)
 
2106
                fk_ref_tab_name = (XTPathStrPtr) xt_dup_string(self, fk->fk_ref_tab_name->ps_path);
 
2107
        fk_ref_cols.clone(self, &fk->fk_ref_cols);
 
2108
        fk_on_delete = fk->fk_on_delete;
 
2109
        fk_on_update = fk->fk_on_update;
 
2110
}
 
2111
 
 
2112
void XTDDForeignKey::finalize(XTThreadPtr self)
 
2113
{
 
2114
        XTDDTable *ref_tab;
 
2115
 
 
2116
        if (fk_ref_tab_name) {
 
2117
                xt_free(self, fk_ref_tab_name);
 
2118
                fk_ref_tab_name = NULL;
 
2119
        }
 
2120
 
 
2121
        if ((ref_tab = fk_ref_table)) {
 
2122
                fk_ref_table = NULL;
 
2123
                ref_tab->removeReference(self, this);
 
2124
                xt_heap_release(self, ref_tab->dt_table); /* We referenced the table, not the index! */
 
2125
        }
 
2126
 
 
2127
        fk_ref_index = UINT_MAX;
 
2128
 
 
2129
        fk_ref_cols.deleteAll(self);
 
2130
        XTDDConstraint::finalize(self);
 
2131
}
 
2132
 
 
2133
void XTDDForeignKey::loadString(XTThreadPtr self, XTStringBufferPtr sb)
 
2134
{
 
2135
        char schema_name[XT_IDENTIFIER_NAME_SIZE];
 
2136
        
 
2137
        XTDDConstraint::loadString(self, sb);
 
2138
        xt_sb_concat(self, sb, " REFERENCES `");
 
2139
        xt_2nd_last_name_of_path(XT_IDENTIFIER_NAME_SIZE, schema_name, fk_ref_tab_name->ps_path);
 
2140
        xt_sb_concat(self, sb, schema_name);
 
2141
        xt_sb_concat(self, sb, "`.`");
 
2142
        xt_sb_concat(self, sb, xt_last_name_of_path(fk_ref_tab_name->ps_path));
 
2143
        xt_sb_concat(self, sb, "` ");
 
2144
 
 
2145
        xt_sb_concat(self, sb, "(`");
 
2146
        xt_sb_concat(self, sb, fk_ref_cols.itemAt(0)->cr_col_name);
 
2147
        for (u_int i=1; i<fk_ref_cols.size(); i++) {
 
2148
                xt_sb_concat(self, sb, "`, `");
 
2149
                xt_sb_concat(self, sb, fk_ref_cols.itemAt(i)->cr_col_name);
 
2150
        }
 
2151
        xt_sb_concat(self, sb, "`)");
 
2152
        
 
2153
        if (fk_on_delete != XT_KEY_ACTION_RESTRICT) {
 
2154
                xt_sb_concat(self, sb, " ON DELETE ");
 
2155
                switch (fk_on_delete) {
 
2156
                        case XT_KEY_ACTION_CASCADE:             xt_sb_concat(self, sb, "CASCADE"); break;
 
2157
                        case XT_KEY_ACTION_SET_NULL:    xt_sb_concat(self, sb, "SET NULL"); break;
 
2158
                        case XT_KEY_ACTION_SET_DEFAULT: xt_sb_concat(self, sb, "SET DEFAULT"); break;
 
2159
                        case XT_KEY_ACTION_NO_ACTION:   xt_sb_concat(self, sb, "NO ACTION"); break;
 
2160
                }
 
2161
        }
 
2162
        if (fk_on_update != XT_KEY_ACTION_RESTRICT) {
 
2163
                xt_sb_concat(self, sb, " ON UPDATE ");
 
2164
                switch (fk_on_update) {
 
2165
                        case XT_KEY_ACTION_RESTRICT:    xt_sb_concat(self, sb, "RESTRICT"); break;
 
2166
                        case XT_KEY_ACTION_CASCADE:             xt_sb_concat(self, sb, "CASCADE"); break;
 
2167
                        case XT_KEY_ACTION_SET_NULL:    xt_sb_concat(self, sb, "SET NULL"); break;
 
2168
                        case XT_KEY_ACTION_SET_DEFAULT: xt_sb_concat(self, sb, "SET DEFAULT"); break;
 
2169
                        case XT_KEY_ACTION_NO_ACTION:   xt_sb_concat(self, sb, "NO ACTION"); break;
 
2170
                }
 
2171
        }
 
2172
}
 
2173
 
 
2174
void XTDDForeignKey::getReferenceList(char *buffer, size_t size)
 
2175
{
 
2176
        buffer[0] = '`';
 
2177
        xt_strcpy(size, buffer + 1, xt_last_name_of_path(fk_ref_tab_name->ps_path));
 
2178
        xt_strcat(size, buffer, "` (");
 
2179
        xt_strcat(size, buffer, fk_ref_cols.itemAt(0)->cr_col_name);
 
2180
        for (u_int i=1; i<fk_ref_cols.size(); i++) {
 
2181
                xt_strcat(size, buffer, ", ");
 
2182
                xt_strcat(size, buffer, fk_ref_cols.itemAt(i)->cr_col_name);
 
2183
        }
 
2184
        xt_strcat(size, buffer, ")");
 
2185
}
 
2186
 
 
2187
struct XTIndex *XTDDForeignKey::getReferenceIndexPtr()
 
2188
{
 
2189
        if (!fk_ref_table) {
 
2190
                xt_register_taberr(XT_REG_CONTEXT, XT_ERR_REF_TABLE_NOT_FOUND, fk_ref_tab_name);
 
2191
                return NULL;
 
2192
        }
 
2193
        if (fk_ref_index >= fk_ref_table->dt_table->tab_dic.dic_key_count) {
 
2194
                XTDDIndex *in;
 
2195
 
 
2196
                if (!(in = fk_ref_table->findReferenceIndex(this)))
 
2197
                        return NULL;
 
2198
                if (!checkReferencedTypes(fk_ref_table))
 
2199
                        return NULL;
 
2200
                fk_ref_index = in->in_index;
 
2201
        }
 
2202
 
 
2203
        return fk_ref_table->dt_table->tab_dic.dic_keys[fk_ref_index];
 
2204
}
 
2205
 
 
2206
bool XTDDForeignKey::sameReferenceColumns(XTDDConstraint *co)
 
2207
{
 
2208
        u_int i = 0;
 
2209
 
 
2210
        if (fk_ref_cols.size() != co->co_cols.size())
 
2211
                return false;
 
2212
        while (i<fk_ref_cols.size()) {
 
2213
                if (myxt_strcasecmp(fk_ref_cols.itemAt(i)->cr_col_name, co->co_cols.itemAt(i)->cr_col_name) != 0)
 
2214
                        return false;
 
2215
                i++;
 
2216
        }
 
2217
        return OK;
 
2218
}
 
2219
 
 
2220
bool XTDDForeignKey::samePrefixReferenceColumns(XTDDConstraint *co)
 
2221
{
 
2222
        u_int i = 0;
 
2223
 
 
2224
        if (fk_ref_cols.size() > co->co_cols.size())
 
2225
                return false;
 
2226
        while (i<fk_ref_cols.size()) {
 
2227
                if (myxt_strcasecmp(fk_ref_cols.itemAt(i)->cr_col_name, co->co_cols.itemAt(i)->cr_col_name) != 0)
 
2228
                        return false;
 
2229
                i++;
 
2230
        }
 
2231
        return OK;
 
2232
}
 
2233
 
 
2234
bool XTDDForeignKey::checkReferencedTypes(XTDDTable *dt)
 
2235
{
 
2236
        XTDDColumn *col, *ref_col;
 
2237
        XTDDEnumerableColumn *enum_col, *enum_ref_col;
 
2238
 
 
2239
        if (XT_IS_TEMP_TABLE(dt->dt_table->tab_dic.dic_tab_flags)) {
 
2240
                xt_register_xterr(XT_REG_CONTEXT, XT_ERR_FK_REF_TEMP_TABLE);
 
2241
                return false;
 
2242
        }
 
2243
 
 
2244
        for (u_int i=0; i<co_cols.size() && i<fk_ref_cols.size(); i++) {
 
2245
                col = co_table->findColumn(co_cols.itemAt(i)->cr_col_name);
 
2246
                ref_col = dt->findColumn(fk_ref_cols.itemAt(i)->cr_col_name);
 
2247
                if (!col || !ref_col)
 
2248
                        continue;
 
2249
 
 
2250
                enum_col = col->castToEnumerable();
 
2251
                enum_ref_col = ref_col->castToEnumerable();
 
2252
 
 
2253
                if (!enum_col && !enum_ref_col && (strcmp(col->dc_data_type, ref_col->dc_data_type) == 0))
 
2254
                        continue;
 
2255
 
 
2256
                /* Allow match varchar(30) == varchar(40): */
 
2257
                if (strncmp(col->dc_data_type, "varchar", 7) == 0 && strncmp(ref_col->dc_data_type, "varchar", 7) == 0) {
 
2258
                        char *t1, *t2;
 
2259
                        
 
2260
                        t1 = col->dc_data_type + 7;
 
2261
                        while (*t1 && (isdigit(*t1) || *t1 == '(' || *t1 == ')')) t1++;
 
2262
                        t2 = col->dc_data_type + 7;
 
2263
                        while (*t2 && (isdigit(*t2) || *t2 == '(' || *t2 == ')')) t2++;
 
2264
                        
 
2265
                        if (strcmp(t1, t2) == 0)
 
2266
                                continue;
 
2267
                }
 
2268
 
 
2269
                /*
 
2270
                 * MySQL stores ENUMs an integer indexes for string values. That's why
 
2271
                 * it is ok to have refrences between columns that are different ENUMs as long
 
2272
                 * as they contain equal number of members, so that for example a cascase update
 
2273
                 * will not cause an invaid value to be stored in the child table. 
 
2274
                 *
 
2275
                 * The above is also true for SETs.
 
2276
                 *
 
2277
                 */
 
2278
 
 
2279
                if (enum_col && enum_ref_col && 
 
2280
                        (enum_col->enum_size == enum_ref_col->enum_size) && 
 
2281
                        (enum_col->is_enum == enum_ref_col->is_enum))
 
2282
                        continue;
 
2283
 
 
2284
                xt_register_tabcolerr(XT_REG_CONTEXT, XT_ERR_REF_TYPE_WRONG, fk_ref_tab_name, ref_col->dc_name);
 
2285
                return false;
 
2286
        }
 
2287
        return true;
 
2288
}
 
2289
 
 
2290
void XTDDForeignKey::removeReference(XTThreadPtr self)
 
2291
{
 
2292
        XTDDTable *ref_tab;
 
2293
 
 
2294
        xt_recurrwlock_xlock(self, &co_table->dt_ref_lock);
 
2295
        pushr_(xt_recurrwlock_unxlock, &co_table->dt_ref_lock);
 
2296
 
 
2297
        if ((ref_tab = fk_ref_table)) {                 
 
2298
                fk_ref_table = NULL;
 
2299
                ref_tab->removeReference(self, this);
 
2300
                xt_heap_release(self, ref_tab->dt_table); /* We referenced the table, not the index! */
 
2301
        }
 
2302
 
 
2303
        fk_ref_index = UINT_MAX;
 
2304
 
 
2305
        freer_(); // xt_recurrwlock_unxlock(&co_table->dt_ref_lock);
 
2306
}
 
2307
 
 
2308
/*
 
2309
 * A row was inserted, check that a key exists in the referenced
 
2310
 * table.
 
2311
 */
 
2312
bool XTDDForeignKey::insertRow(xtWord1 *before_buf, xtWord1 *rec_buf, XTThreadPtr thread)
 
2313
{
 
2314
        XTIndexPtr                      loc_ind, ind;
 
2315
        xtBool                          no_null = TRUE;
 
2316
        XTOpenTablePtr          ot;
 
2317
        XTIdxSearchKeyRec       search_key;
 
2318
        xtXactID                        xn_id;
 
2319
        XTXactWaitRec           xw;
 
2320
 
 
2321
        /* This lock ensures that the foreign key references are not
 
2322
         * changed.
 
2323
         */
 
2324
        xt_recurrwlock_slock_ns(&co_table->dt_ref_lock);
 
2325
 
 
2326
        if (!(loc_ind = getIndexPtr()))
 
2327
                goto failed;
 
2328
 
 
2329
        if (!(ind = getReferenceIndexPtr()))
 
2330
                goto failed;
 
2331
 
 
2332
        search_key.sk_key_value.sv_flags = 0;
 
2333
        search_key.sk_key_value.sv_rec_id = 0;
 
2334
        search_key.sk_key_value.sv_row_id = 0;
 
2335
        search_key.sk_key_value.sv_key = search_key.sk_key_buf;
 
2336
        search_key.sk_key_value.sv_length = myxt_create_foreign_key_from_row(loc_ind, search_key.sk_key_buf, rec_buf, ind, &no_null);
 
2337
        search_key.sk_on_key = FALSE;
 
2338
 
 
2339
        if (!no_null)
 
2340
                goto success;
 
2341
 
 
2342
        if (before_buf) {
 
2343
                u_int   before_key_len;
 
2344
                xtWord1 before_key[XT_INDEX_MAX_KEY_SIZE];
 
2345
 
 
2346
                /* If there is a before buffer, this insert was an update, so check
 
2347
                 * if the key value has changed. If not, we need not do anything.
 
2348
                 */
 
2349
                before_key_len = myxt_create_foreign_key_from_row(loc_ind, before_key, before_buf, ind, NULL);
 
2350
                
 
2351
                /* Check whether the key value has changed, if not, we have nothing
 
2352
                 * to do here!
 
2353
                 */
 
2354
                if (search_key.sk_key_value.sv_length == before_key_len &&
 
2355
                        memcmp(search_key.sk_key_buf, before_key, before_key_len) == 0)
 
2356
                        goto success;
 
2357
        }
 
2358
 
 
2359
        /* Search for the key in the parent (referenced) table: */
 
2360
        if (!(ot = xt_db_open_table_using_tab(fk_ref_table->dt_table, thread)))
 
2361
                goto failed;
 
2362
 
 
2363
        retry:
 
2364
        if (!xt_idx_search(ot, ind, &search_key))
 
2365
                goto failed_2;
 
2366
                
 
2367
        while (ot->ot_curr_rec_id) {
 
2368
                if (!search_key.sk_on_key)
 
2369
                        break;
 
2370
 
 
2371
                switch (xt_tab_maybe_committed(ot, ot->ot_curr_rec_id, &xn_id, &ot->ot_curr_row_id, &ot->ot_curr_updated)) {
 
2372
                        case XT_MAYBE:
 
2373
                                /* We should not get a deadlock here because the thread
 
2374
                                 * that we are waiting for, should not doing
 
2375
                                 * data definition (i.e. should not be trying to
 
2376
                                 * get an exclusive lock on dt_ref_lock.
 
2377
                                 */
 
2378
                                xw.xw_xn_id = xn_id;
 
2379
                                if (!xt_xn_wait_for_xact(thread, &xw, NULL))
 
2380
                                        goto failed_2;
 
2381
                                goto retry;                     
 
2382
                        case XT_ERR:
 
2383
                                goto failed_2;
 
2384
                        case TRUE:
 
2385
                                /* We found a matching parent: */
 
2386
                                if (ot->ot_ind_rhandle) {
 
2387
                                        xt_ind_release_handle(ot->ot_ind_rhandle, FALSE, thread);
 
2388
                                        ot->ot_ind_rhandle = NULL;
 
2389
                                }
 
2390
                                xt_db_return_table_to_pool_ns(ot);
 
2391
                                goto success;
 
2392
                        case FALSE:
 
2393
                                if (!xt_idx_next(ot, ind, &search_key))
 
2394
                                        goto failed_2;
 
2395
                                break;
 
2396
                }
 
2397
        }
 
2398
 
 
2399
        xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_NO_REFERENCED_ROW, co_name);
 
2400
 
 
2401
        failed_2:
 
2402
        if (ot->ot_ind_rhandle) {
 
2403
                xt_ind_release_handle(ot->ot_ind_rhandle, FALSE, thread);
 
2404
                ot->ot_ind_rhandle = NULL;
 
2405
        }
 
2406
        xt_db_return_table_to_pool_ns(ot);
 
2407
 
 
2408
        failed:
 
2409
        xt_recurrwlock_unslock_ns(&co_table->dt_ref_lock);
 
2410
        return false;
 
2411
 
 
2412
        success:
 
2413
        xt_recurrwlock_unslock_ns(&co_table->dt_ref_lock);
 
2414
        return true;
 
2415
}
 
2416
 
 
2417
/*
 
2418
 * Convert XT_KEY_ACTION_* constants to strings
 
2419
 */
 
2420
const char *XTDDForeignKey::actionTypeToString(int action)
 
2421
{
 
2422
        switch (action)
 
2423
        {
 
2424
        case XT_KEY_ACTION_RESTRICT:
 
2425
                return "RESTRICT";
 
2426
        case XT_KEY_ACTION_CASCADE:
 
2427
                return "CASCADE";
 
2428
        case XT_KEY_ACTION_SET_NULL:
 
2429
                return "SET NULL";
 
2430
        case XT_KEY_ACTION_SET_DEFAULT:
 
2431
                return "";
 
2432
        case XT_KEY_ACTION_NO_ACTION:
 
2433
                return "NO ACTION";
 
2434
        }
 
2435
 
 
2436
        return "";
 
2437
}
 
2438
 
 
2439
void XTDDTable::init(XTThreadPtr self)
 
2440
{
 
2441
        xt_recurrwlock_init_with_autoname(self, &dt_ref_lock);
 
2442
        dt_trefs = NULL;
 
2443
}
 
2444
 
 
2445
void XTDDTable::init(XTThreadPtr self, XTObject *obj)
 
2446
{
 
2447
        XTDDTable *tab = (XTDDTable *) obj;
 
2448
        u_int           i;
 
2449
 
 
2450
        init(self);
 
2451
        XTObject::init(self, obj);
 
2452
        dt_cols.clone(self, &tab->dt_cols);     
 
2453
        dt_indexes.clone(self, &tab->dt_indexes);       
 
2454
        dt_fkeys.clone(self, &tab->dt_fkeys);   
 
2455
 
 
2456
        for (i=0; i<dt_indexes.size(); i++)
 
2457
                dt_indexes.itemAt(i)->co_table = this;
 
2458
        for (i=0; i<dt_fkeys.size(); i++)
 
2459
                dt_fkeys.itemAt(i)->co_table = this;
 
2460
}
 
2461
 
 
2462
void XTDDTable::finalize(XTThreadPtr self)
 
2463
{
 
2464
        XTDDTableRef *ptr;
 
2465
 
 
2466
        removeReferences(self);
 
2467
 
 
2468
        dt_cols.deleteAll(self);
 
2469
        dt_indexes.deleteAll(self);
 
2470
        dt_fkeys.deleteAll(self);
 
2471
 
 
2472
        while (dt_trefs) {
 
2473
                ptr = dt_trefs;
 
2474
                dt_trefs = dt_trefs->tr_next;
 
2475
                ptr->release(self);
 
2476
        }
 
2477
 
 
2478
        xt_recurrwlock_free(&dt_ref_lock);
 
2479
}
 
2480
 
 
2481
XTDDColumn *XTDDTable::findColumn(char *name)
 
2482
{
 
2483
        XTDDColumn *col;
 
2484
 
 
2485
        for (u_int i=0; i<dt_cols.size(); i++) {
 
2486
                col = dt_cols.itemAt(i);
 
2487
                if (myxt_strcasecmp(name, col->dc_name) == 0)
 
2488
                        return col;
 
2489
        }
 
2490
        return NULL;
 
2491
}
 
2492
 
 
2493
void XTDDTable::loadString(XTThreadPtr self, XTStringBufferPtr sb)
 
2494
{
 
2495
        u_int i;
 
2496
 
 
2497
        /* I do not specify a table name because that is known */
 
2498
        xt_sb_concat(self, sb, "CREATE TABLE (\n  ");
 
2499
 
 
2500
        /* We only need to save the foreign key definitions!!
 
2501
        for (i=0; i<dt_cols.size(); i++) {
 
2502
                if (i != 0)
 
2503
                        xt_sb_concat(self, sb, ",\n  ");
 
2504
                dt_cols.itemAt(i)->loadString(self, sb);
 
2505
        }
 
2506
 
 
2507
        for (i=0; i<dt_indexes.size(); i++) {
 
2508
                xt_sb_concat(self, sb, ",\n  ");
 
2509
                dt_indexes.itemAt(i)->loadString(self, sb);
 
2510
        }
 
2511
        */
 
2512
 
 
2513
        for (i=0; i<dt_fkeys.size(); i++) {
 
2514
                if (i != 0)
 
2515
                        xt_sb_concat(self, sb, ",\n  ");
 
2516
                dt_fkeys.itemAt(i)->loadString(self, sb);
 
2517
        }
 
2518
 
 
2519
        xt_sb_concat(self, sb, "\n)\n");
 
2520
}
 
2521
 
 
2522
void XTDDTable::loadForeignKeyString(XTThreadPtr self, XTStringBufferPtr sb)
 
2523
{
 
2524
        for (u_int i=0; i<dt_fkeys.size(); i++) {
 
2525
                xt_sb_concat(self, sb, ",\n  ");
 
2526
                dt_fkeys.itemAt(i)->loadString(self, sb);
 
2527
        }
 
2528
}
 
2529
 
 
2530
/* Change all references to the given column name to new name. */
 
2531
void XTDDTable::alterColumnName(XTThreadPtr self, char *from_name, char *to_name)
 
2532
{
 
2533
        u_int i;
 
2534
 
 
2535
        /* We only alter references in the foreign keys (we copied the
 
2536
         * other changes from MySQL).
 
2537
         */
 
2538
        for (i=0; i<dt_fkeys.size(); i++)
 
2539
                dt_fkeys.itemAt(i)->alterColumnName(self, from_name, to_name);
 
2540
}
 
2541
 
 
2542
void XTDDTable::attachReference(XTThreadPtr self, XTDDForeignKey *fk)
 
2543
{
 
2544
        XTDDTableRef    *tr;
 
2545
 
 
2546
        /* Remove the reference to this FK if one exists: */
 
2547
        removeReference(self, fk);
 
2548
 
 
2549
        if (!fk->checkReferencedTypes(this)) {
 
2550
                if (!self->st_ignore_fkeys)
 
2551
                        throw_();
 
2552
        }
 
2553
 
 
2554
        xt_recurrwlock_xlock(self, &dt_ref_lock);
 
2555
        pushr_(xt_recurrwlock_unxlock, &dt_ref_lock);
 
2556
 
 
2557
        if (!(tr = new XTDDTableRef()))
 
2558
                xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
 
2559
        tr->tr_fkey = fk;
 
2560
        tr->tr_next = dt_trefs;
 
2561
        dt_trefs = tr;
 
2562
 
 
2563
        /* Reference the database table of the foreign key, not the FK itself.
 
2564
         * Just referencing the key will not guarantee that the
 
2565
         * table remains valid because the FK does not reference the
 
2566
         * table.
 
2567
         */
 
2568
        xt_heap_reference(self, fk->co_table->dt_table);
 
2569
 
 
2570
        freer_(); // xt_recurrwlock_unxlock(&dt_ref_lock);
 
2571
}
 
2572
 
 
2573
/*
 
2574
 * Remove the reference to the given foreign key.
 
2575
 */
 
2576
void XTDDTable::removeReference(XTThreadPtr self, XTDDForeignKey *fk)
 
2577
{
 
2578
        XTDDTableRef    *tr, *prev_tr = NULL;
 
2579
 
 
2580
        xt_recurrwlock_xlock(self, &dt_ref_lock);
 
2581
        pushr_(xt_recurrwlock_unxlock, &dt_ref_lock);
 
2582
 
 
2583
        tr = dt_trefs;
 
2584
        while (tr) {
 
2585
                if (tr->tr_fkey == fk) {
 
2586
                        if (prev_tr)
 
2587
                                prev_tr->tr_next = tr->tr_next;
 
2588
                        else
 
2589
                                dt_trefs = tr->tr_next;
 
2590
                        break;
 
2591
                }
 
2592
                prev_tr = tr;
 
2593
                tr = tr->tr_next;
 
2594
        }
 
2595
        freer_(); // xt_recurrwlock_unxlock(&dt_ref_lock);
 
2596
        if (tr)
 
2597
                tr->release(self);
 
2598
}
 
2599
 
 
2600
void XTDDTable::checkForeignKeyReference(XTThreadPtr self, XTDDForeignKey *fk)
 
2601
{
 
2602
        XTDDColumnRef   *cr;
 
2603
 
 
2604
        for (u_int i=0; i<fk->fk_ref_cols.size(); i++) {
 
2605
                cr = fk->fk_ref_cols.itemAt(i);
 
2606
                if (!findColumn(cr->cr_col_name))
 
2607
                        xt_throw_tabcolerr(XT_CONTEXT, XT_ERR_COLUMN_NOT_FOUND, fk->fk_ref_tab_name, cr->cr_col_name);
 
2608
        }
 
2609
}
 
2610
 
 
2611
void XTDDTable::attachReference(XTThreadPtr self, XTDDTable *dt)
 
2612
{
 
2613
        XTDDForeignKey  *fk;
 
2614
 
 
2615
        for (u_int i=0; i<dt_fkeys.size(); i++) {
 
2616
                fk = dt_fkeys.itemAt(i);
 
2617
                if (xt_tab_compare_names(fk->fk_ref_tab_name->ps_path, dt->dt_table->tab_name->ps_path) == 0) {
 
2618
                        fk->removeReference(self);
 
2619
 
 
2620
                        dt->attachReference(self, fk);
 
2621
 
 
2622
                        xt_recurrwlock_xlock(self, &dt_ref_lock);
 
2623
                        pushr_(xt_recurrwlock_unxlock, &dt_ref_lock);
 
2624
                        /* Referenced the table, not the index!
 
2625
                         * We do this because we know that if the table is referenced, the
 
2626
                         * index will remain valid!
 
2627
                         * This is because the table references the index, and only
 
2628
                         * releases it when the table is released. The index does not
 
2629
                         * reference the table though!
 
2630
                         */
 
2631
                        xt_heap_reference(self, dt->dt_table);
 
2632
                        fk->fk_ref_table = dt;
 
2633
                        freer_(); // xt_recurrwlock_unxlock(&dt_ref_lock);
 
2634
                }
 
2635
        }
 
2636
}
 
2637
 
 
2638
/*
 
2639
 * This function assumes the database table list is locked!
 
2640
 */
 
2641
void XTDDTable::attachReferences(XTThreadPtr self, XTDatabaseHPtr db)
 
2642
{
 
2643
        XTDDForeignKey  *fk;
 
2644
        XTTableHPtr             tab;
 
2645
        XTDDTable               *dt;
 
2646
        XTHashEnumRec   tables;
 
2647
 
 
2648
        /* Search for table referenced by this table. */
 
2649
        for (u_int i=0; i<dt_fkeys.size(); i++) {
 
2650
                fk = dt_fkeys.itemAt(i);
 
2651
                fk->removeReference(self);
 
2652
 
 
2653
                // if self-reference
 
2654
                if (xt_tab_compare_names(fk->fk_ref_tab_name->ps_path, this->dt_table->tab_name->ps_path) == 0)
 
2655
                        fk->fk_ref_table = this;
 
2656
                else {
 
2657
                        /* get pointer to the referenced table, load it if needed
 
2658
                         * cyclic references are being handled, absent table is ignored
 
2659
                         */
 
2660
                        tab = xt_use_table_no_lock(self, db, fk->fk_ref_tab_name, /*TRUE*/FALSE, /*FALSE*/TRUE, NULL);
 
2661
 
 
2662
                        if (tab) {
 
2663
                                pushr_(xt_heap_release, tab);
 
2664
                                if ((dt = tab->tab_dic.dic_table)) {
 
2665
                                        // Add a reverse reference:
 
2666
                                        dt->attachReference(self, fk);
 
2667
                                        xt_heap_reference(self, dt->dt_table); /* Referenced the table, not the index! */
 
2668
                                        fk->fk_ref_table = dt;
 
2669
                                }
 
2670
                                freer_(); // xt_heap_release(tab)
 
2671
                        }
 
2672
                        else if (!self->st_ignore_fkeys) {
 
2673
                                xt_throw_taberr(XT_CONTEXT, XT_ERR_REF_TABLE_NOT_FOUND, fk->fk_ref_tab_name);
 
2674
                        }
 
2675
                }
 
2676
        }
 
2677
 
 
2678
        /* Search for tables that reference this table. */
 
2679
        xt_ht_enum(self, dt_table->tab_db->db_tables, &tables);
 
2680
        while ((tab = (XTTableHPtr) xt_ht_next(self, &tables))) {
 
2681
                if (tab == this->dt_table) /* no need to re-reference itself, also this fails with "native" pthreads */
 
2682
                        continue;
 
2683
                xt_heap_reference(self, tab);
 
2684
                pushr_(xt_heap_release, tab);
 
2685
                if ((dt = tab->tab_dic.dic_table))
 
2686
                        dt->attachReference(self, this);
 
2687
                freer_(); // xt_heap_release(tab)
 
2688
        }
 
2689
}
 
2690
 
 
2691
void XTDDTable::removeReferences(XTThreadPtr self)
 
2692
{
 
2693
        XTDDForeignKey  *fk;
 
2694
        XTDDTableRef    *tr;
 
2695
        XTDDTable               *tab;
 
2696
 
 
2697
        xt_recurrwlock_xlock(self, &dt_ref_lock);
 
2698
        pushr_(xt_recurrwlock_unxlock, &dt_ref_lock);
 
2699
 
 
2700
        for (u_int i=0; i<dt_fkeys.size(); i++) {
 
2701
                fk = dt_fkeys.itemAt(i);
 
2702
                if ((tab = fk->fk_ref_table)) {                 
 
2703
                        fk->fk_ref_table = NULL;
 
2704
                        fk->fk_ref_index = UINT_MAX;
 
2705
                        if (tab != this) {
 
2706
                                /* To avoid deadlock we do not hold more than
 
2707
                                 * one lock at a time!
 
2708
                                 */
 
2709
                                freer_(); // xt_recurrwlock_unxlock(&dt_ref_lock);
 
2710
        
 
2711
                                tab->removeReference(self, fk);
 
2712
                                xt_heap_release(self, tab->dt_table); /* We referenced the table, not the index! */
 
2713
        
 
2714
                                xt_recurrwlock_xlock(self, &dt_ref_lock);
 
2715
                                pushr_(xt_recurrwlock_unxlock, &dt_ref_lock);
 
2716
                        }
 
2717
                }
 
2718
        }
 
2719
 
 
2720
        while (dt_trefs) {
 
2721
                tr = dt_trefs;
 
2722
                dt_trefs = tr->tr_next;
 
2723
                freer_(); // xt_recurrwlock_unxlock(&dt_ref_lock);
 
2724
                tr->release(self);
 
2725
                xt_recurrwlock_xlock(self, &dt_ref_lock);
 
2726
                pushr_(xt_recurrwlock_unxlock, &dt_ref_lock);
 
2727
        }
 
2728
 
 
2729
        freer_(); // xt_recurrwlock_unxlock(&dt_ref_lock);
 
2730
}
 
2731
 
 
2732
void XTDDTable::checkForeignKeys(XTThreadPtr self, bool temp_table)
 
2733
{
 
2734
        XTDDForeignKey  *fk;
 
2735
 
 
2736
        if (temp_table && dt_fkeys.size()) {
 
2737
                /* Temporary tables cannot have foreign keys: */
 
2738
                xt_throw_xterr(XT_CONTEXT, XT_ERR_FK_ON_TEMP_TABLE);
 
2739
                
 
2740
        }
 
2741
 
 
2742
        /* Search for table referenced by this table. */
 
2743
        for (u_int i=0; i<dt_fkeys.size(); i++) {
 
2744
                fk = dt_fkeys.itemAt(i);
 
2745
 
 
2746
                if (fk->fk_on_delete == XT_KEY_ACTION_SET_NULL || fk->fk_on_update == XT_KEY_ACTION_SET_NULL) {
 
2747
                        /* Check that all the columns can be set to NULL! */
 
2748
                        XTDDColumn *col;
 
2749
 
 
2750
                        for (u_int j=0; j<fk->co_cols.size(); j++) {
 
2751
                                if ((col = findColumn(fk->co_cols.itemAt(j)->cr_col_name))) {
 
2752
                                        if (!col->dc_null_ok)
 
2753
                                                xt_throw_tabcolerr(XT_CONTEXT, XT_ERR_COLUMN_IS_NOT_NULL, fk->fk_ref_tab_name, col->dc_name);
 
2754
                                }
 
2755
                        }
 
2756
                }
 
2757
 
 
2758
                // TODO: dont close table immediately so it can be possibly reused in this loop
 
2759
                XTTable *ref_tab;
 
2760
 
 
2761
                pushsr_(ref_tab, xt_heap_release, xt_use_table(self, fk->fk_ref_tab_name, FALSE, TRUE));
 
2762
                if (ref_tab && !fk->checkReferencedTypes(ref_tab->tab_dic.dic_table))
 
2763
                        throw_();
 
2764
                freer_();
 
2765
 
 
2766
                /* Currently I allow foreign keys to be created on tables that do not yet exist!
 
2767
                pushsr_(tab, xt_heap_release, xt_use_table(self, fk->fk_ref_tab_name, FALSE FALSE));
 
2768
                if ((dt = tab->tab_dic.dic_table))
 
2769
                        dt->checkForeignKeyReference(self, fk);
 
2770
                freer_(); // xt_heap_release(tab)
 
2771
                */
 
2772
        }
 
2773
}
 
2774
 
 
2775
XTDDIndex *XTDDTable::findIndex(XTDDConstraint *co)
 
2776
{
 
2777
        XTDDIndex *ind = NULL;
 
2778
        XTDDIndex *cur_ind;
 
2779
        u_int index_size = UINT_MAX;
 
2780
 
 
2781
        for (u_int i=0; i<dt_indexes.size(); i++) {
 
2782
                cur_ind = dt_indexes.itemAt(i);
 
2783
                u_int sz = cur_ind->getIndexPtr()->mi_key_size;
 
2784
                if (sz < index_size && co->samePrefixColumns(cur_ind)) {
 
2785
                        ind = cur_ind;
 
2786
                        index_size = sz;
 
2787
                }
 
2788
        }
 
2789
 
 
2790
        if (ind) 
 
2791
                return ind;
 
2792
        
 
2793
        {
 
2794
                char buffer[XT_ERR_MSG_SIZE - 200];
 
2795
                co->getColumnList(buffer, XT_ERR_MSG_SIZE - 200);
 
2796
                xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_NO_MATCHING_INDEX, buffer);
 
2797
        }
 
2798
        return NULL;
 
2799
}
 
2800
 
 
2801
XTDDIndex *XTDDTable::findReferenceIndex(XTDDForeignKey *fk)
 
2802
{
 
2803
        XTDDIndex               *ind = NULL;
 
2804
        XTDDIndex               *cur_ind;
 
2805
        XTDDColumnRef   *cr;
 
2806
        u_int                   i;
 
2807
        u_int                   index_size = UINT_MAX;
 
2808
 
 
2809
        for (i=0; i<dt_indexes.size(); i++) {
 
2810
                cur_ind = dt_indexes.itemAt(i);
 
2811
                u_int sz = cur_ind->getIndexPtr()->mi_key_size;
 
2812
                if (sz < index_size && fk->samePrefixReferenceColumns(cur_ind)) {
 
2813
                        ind = cur_ind;
 
2814
                        index_size = sz;
 
2815
                }
 
2816
        }
 
2817
 
 
2818
        if (ind)
 
2819
                return ind;
 
2820
 
 
2821
        /* If the index does not exist, maybe the columns do not exist?! */
 
2822
        for (i=0; i<fk->fk_ref_cols.size(); i++) {
 
2823
                cr = fk->fk_ref_cols.itemAt(i);
 
2824
                if (!findColumn(cr->cr_col_name)) {
 
2825
                        xt_register_tabcolerr(XT_REG_CONTEXT, XT_ERR_COLUMN_NOT_FOUND, fk->fk_ref_tab_name, cr->cr_col_name);
 
2826
                        return NULL;
 
2827
                }
 
2828
        }
 
2829
        
 
2830
        {
 
2831
                char buffer[XT_ERR_MSG_SIZE - 200];
 
2832
 
 
2833
                fk->getReferenceList(buffer, XT_ERR_MSG_SIZE - 200);
 
2834
                xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_NO_MATCHING_INDEX, buffer);
 
2835
        }
 
2836
        return NULL;
 
2837
}
 
2838
 
 
2839
bool XTDDTable::insertRow(XTOpenTablePtr ot, xtWord1 *rec_ptr)
 
2840
{
 
2841
        bool                    ok = true;
 
2842
        XTInfoBufferRec rec_buf;
 
2843
 
 
2844
        if (ot->ot_thread->st_ignore_fkeys || ot->ot_thread->st_import_stat == XT_IMP_COPY_TABLE)
 
2845
                return true;
 
2846
        rec_buf.ib_free = FALSE;
 
2847
        if (!rec_ptr) {
 
2848
                if (!xt_tab_load_record(ot, ot->ot_curr_rec_id, &rec_buf))
 
2849
                        return false;
 
2850
                rec_ptr = rec_buf.ib_db.db_data;
 
2851
                
 
2852
        }
 
2853
        for (u_int i=0; i<dt_fkeys.size(); i++) {
 
2854
                if (!dt_fkeys.itemAt(i)->insertRow(NULL, rec_ptr, ot->ot_thread)) {
 
2855
                        ok = false;
 
2856
                        break;
 
2857
                }
 
2858
        }
 
2859
        xt_ib_free(NULL, &rec_buf);
 
2860
        return ok;
 
2861
}
 
2862
 
 
2863
bool XTDDTable::checkNoAction(XTOpenTablePtr ot, xtRecordID rec_id)
 
2864
{
 
2865
        XTDDTableRef    *tr;
 
2866
        bool                    ok = true;
 
2867
        XTInfoBufferRec rec_buf;
 
2868
        xtWord1                 *rec_ptr;
 
2869
 
 
2870
        if (ot->ot_thread->st_ignore_fkeys)
 
2871
                return true;
 
2872
 
 
2873
        rec_buf.ib_free = FALSE;
 
2874
        if (!xt_tab_load_record(ot, rec_id, &rec_buf))
 
2875
                return false;
 
2876
        rec_ptr = rec_buf.ib_db.db_data;
 
2877
 
 
2878
        xt_recurrwlock_slock_ns(&dt_ref_lock);
 
2879
        tr = dt_trefs;
 
2880
        while (tr) {
 
2881
                if (!tr->checkReference(rec_ptr, ot->ot_thread)) {
 
2882
                        ok = false;
 
2883
                        break;
 
2884
                }
 
2885
                tr = tr->tr_next;
 
2886
        }
 
2887
        xt_recurrwlock_unslock_ns(&dt_ref_lock);
 
2888
        xt_ib_free(NULL, &rec_buf);
 
2889
        return ok;
 
2890
}
 
2891
 
 
2892
bool XTDDTable::deleteRow(XTOpenTablePtr ot, xtWord1 *rec_ptr)
 
2893
{
 
2894
        XTDDTableRef    *tr;
 
2895
        bool                    ok = true;
 
2896
        XTInfoBufferRec rec_buf;
 
2897
 
 
2898
        if (ot->ot_thread->st_ignore_fkeys)
 
2899
                return true;
 
2900
 
 
2901
        rec_buf.ib_free = FALSE;
 
2902
        if (!rec_ptr) {
 
2903
                if (!xt_tab_load_record(ot, ot->ot_curr_rec_id, &rec_buf))
 
2904
                        return false;
 
2905
                rec_ptr = rec_buf.ib_db.db_data;
 
2906
                
 
2907
        }
 
2908
        xt_recurrwlock_slock_ns(&dt_ref_lock);
 
2909
        tr = dt_trefs;
 
2910
        while (tr) {
 
2911
                if (!tr->modifyRow(ot, rec_ptr, NULL, ot->ot_thread)) {
 
2912
                        ok = false;
 
2913
                        break;
 
2914
                }
 
2915
                tr = tr->tr_next;
 
2916
        }
 
2917
        xt_recurrwlock_unslock_ns(&dt_ref_lock);
 
2918
        xt_ib_free(NULL, &rec_buf);
 
2919
        return ok;
 
2920
}
 
2921
 
 
2922
void XTDDTable::deleteAllRows(XTThreadPtr self)
 
2923
{
 
2924
        XTDDTableRef    *tr;
 
2925
 
 
2926
        xt_recurrwlock_slock(self, &dt_ref_lock);
 
2927
        pushr_(xt_recurrwlock_unslock, &dt_ref_lock);
 
2928
 
 
2929
        tr = dt_trefs;
 
2930
        while (tr) {
 
2931
                tr->deleteAllRows(self);
 
2932
                tr = tr->tr_next;
 
2933
        }
 
2934
 
 
2935
        freer_(); // xt_recurrwlock_unslock(&dt_ref_lock);
 
2936
}
 
2937
 
 
2938
bool XTDDTable::updateRow(XTOpenTablePtr ot, xtWord1 *before, xtWord1 *after)
 
2939
{
 
2940
        XTDDTableRef    *tr;
 
2941
        bool                    ok;
 
2942
        XTInfoBufferRec before_buf;
 
2943
 
 
2944
        ASSERT_NS(after);
 
2945
 
 
2946
        if (ot->ot_thread->st_ignore_fkeys)
 
2947
                return true;
 
2948
 
 
2949
        /* If before is NULL then this is a cascaded
 
2950
         * update. In this case there is no need to check
 
2951
         * if the column has a parent!!
 
2952
         */
 
2953
        if (before) {
 
2954
                if (dt_fkeys.size() > 0) {
 
2955
                        for (u_int i=0; i<dt_fkeys.size(); i++) {
 
2956
                                if (!dt_fkeys.itemAt(i)->insertRow(before, after, ot->ot_thread))
 
2957
                                        return false;
 
2958
                        }
 
2959
                }
 
2960
        }
 
2961
 
 
2962
        ok = true;
 
2963
        before_buf.ib_free = FALSE;
 
2964
 
 
2965
        xt_recurrwlock_slock_ns(&dt_ref_lock);
 
2966
        if ((tr = dt_trefs)) {
 
2967
                if (!before) {
 
2968
                        if (!xt_tab_load_record(ot, ot->ot_curr_rec_id, &before_buf))
 
2969
                                return false;
 
2970
                        before = before_buf.ib_db.db_data;
 
2971
                }
 
2972
 
 
2973
                while (tr) {
 
2974
                        if (!tr->modifyRow(ot, before, after, ot->ot_thread)) {
 
2975
                                ok = false;
 
2976
                                break;
 
2977
                        }
 
2978
                        tr = tr->tr_next;
 
2979
                }
 
2980
        }
 
2981
        xt_recurrwlock_unslock_ns(&dt_ref_lock);
 
2982
        
 
2983
        xt_ib_free(NULL, &before_buf);
 
2984
        return ok;
 
2985
}
 
2986
 
 
2987
/*
 
2988
 * drop_db parameter is TRUE if we are dropping the schema of this table. In this case
 
2989
 * we return TRUE if the table has only refs to the tables from its own schema
 
2990
 */
 
2991
xtBool XTDDTable::checkCanDrop(xtBool drop_db)
 
2992
{
 
2993
        /* no refs or references only itself */
 
2994
        if ((dt_trefs == NULL) || ((dt_trefs->tr_next == NULL) && (dt_trefs->tr_fkey->co_table == this)))
 
2995
                return TRUE;
 
2996
 
 
2997
        if (!drop_db) 
 
2998
                return FALSE;
 
2999
        
 
3000
        const char *this_schema = xt_last_2_names_of_path(dt_table->tab_name->ps_path);
 
3001
        size_t this_schema_sz = xt_last_name_of_path(dt_table->tab_name->ps_path) - this_schema;
 
3002
        XTDDTableRef *tr = dt_trefs;
 
3003
 
 
3004
        while (tr) {
 
3005
                const char *tab_path = tr->tr_fkey->co_table->dt_table->tab_name->ps_path;
 
3006
                const char *tab_schema = xt_last_2_names_of_path(tab_path);
 
3007
                size_t tab_schema_sz = xt_last_name_of_path(tab_path) - tab_schema;
 
3008
 
 
3009
                if (this_schema_sz != tab_schema_sz || strncmp(this_schema, tab_schema, tab_schema_sz))
 
3010
                        return FALSE;
 
3011
                
 
3012
                tr = tr->tr_next;
 
3013
        }
 
3014
 
 
3015
        return TRUE;
 
3016
}