1
/* Copyright (c) 2005 PrimeBase Technologies GmbH
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.
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.
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
19
* 2006-05-16 Paul McCullagh
23
* Implementation of the PBXT internal data dictionary.
27
#include "xt_config.h"
38
//#include <drizzled/common_includes.h>
40
#include "mysql_priv.h"
44
#include "pthread_xt.h"
45
#include "datadic_xt.h"
47
#include "database_xt.h"
50
#include "strutil_xt.h"
52
#include "hashtab_xt.h"
55
* -----------------------------------------------------------------------
60
#define XT_TK_IDENTIFIER 1
61
#define XT_TK_NUMBER 2
62
#define XT_TK_STRING 3
63
#define XT_TK_PUNCTUATION 4
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
72
#define XT_TK_CHECK 11
73
#define XT_TK_FOREIGN 12
74
#define XT_TK_COLUMN 13
75
#define XT_TK_REFERENCES 14
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
89
void initCString(u_int type, char *start, char *end);
90
inline char charAt(u_int i) {
93
return toupper(tk_text[i]);
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();
105
size_t getString(char *string, size_t len);
106
void getTokenText(char *string, size_t len);
107
XTToken *clone(XTThreadPtr self);
110
void XTToken::initCString(u_int type, char *start, char *end)
114
tk_length = (size_t) end - (size_t) start;
117
bool XTToken::isKeyWord(c_char *keyword)
120
size_t len = tk_length;
122
while (len && *keyword) {
123
if (toupper(*keyword) != toupper(*str))
129
return !len && !*keyword;
132
bool XTToken::isReservedWord()
134
return tk_type >= XT_TK_RESERVER_WORDS;
137
bool XTToken::isReservedWord(u_int word)
139
return tk_type == word;
142
void XTToken::identifyReservedWord()
144
if (tk_type == XT_TK_IDENTIFIER) {
147
if (isKeyWord("AUTO_INCREMENT"))
148
tk_type = XT_TK_AUTO_INCREMENT;
153
if (isKeyWord("CHECK"))
154
tk_type = XT_TK_CHECK;
157
if (isKeyWord("COLUMN"))
158
tk_type = XT_TK_COLUMN;
159
else if (isKeyWord("COLLATE"))
160
tk_type = XT_TK_COLLATE;
163
if (isKeyWord("COMMENT"))
164
tk_type = XT_TK_COMMENT;
169
if (isKeyWord("DEFAULT"))
170
tk_type = XT_TK_DEFAULT;
175
if (isKeyWord("FOREIGN"))
176
tk_type = XT_TK_FOREIGN;
179
if (isKeyWord("FULLTEXT"))
180
tk_type = XT_TK_FULLTEXT;
185
if (isKeyWord("INDEX"))
186
tk_type = XT_TK_INDEX;
189
if (isKeyWord("KEY"))
195
if (isKeyWord("NOT"))
199
if (isKeyWord("NULL"))
200
tk_type = XT_TK_NULL;
205
if (isKeyWord("PRIMARY"))
206
tk_type = XT_TK_PRIMARY;
209
if (isKeyWord("REFERENCES"))
210
tk_type = XT_TK_REFERENCES;
213
if (isKeyWord("SPATIAL"))
214
tk_type = XT_TK_SPATIAL;
217
if (isKeyWord("UNIQUE"))
218
tk_type = XT_TK_UNIQUE;
224
bool XTToken::isEOF()
226
return tk_type == XT_TK_EOF;
229
bool XTToken::isIdentifier()
231
return tk_type == XT_TK_IDENTIFIER;
234
bool XTToken::isNumber()
236
return tk_type == XT_TK_NUMBER;
239
/* Return actual, or required string length. */
240
size_t XTToken::getString(char *dtext, size_t dsize)
242
char *buffer = dtext;
248
if ((slen = (int) tk_length) == 0) {
261
if (*stext == '\\') {
299
else if (*stext == quote) {
322
if ((int) dlen > slen)
324
memcpy(dtext, tk_text, dlen);
333
/* Return the token as a string with ... in it if it is too long
335
void XTToken::getTokenText(char *string, size_t size)
337
if (tk_length == 0 || !tk_text) {
338
xt_strcpy(size, string, "EOF");
343
if (tk_length <= size) {
344
memcpy(string, tk_text, tk_length);
345
string[tk_length] = 0;
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;
356
XTToken *XTToken::clone(XTThreadPtr self)
360
if (!(tk = new XTToken()))
361
xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
362
tk->initCString(tk_type, tk_text, tk_text + tk_length);
366
void XTToken::expectKeyWord(XTThreadPtr self, c_char *keyword)
370
if (isKeyWord(keyword))
372
getTokenText(buffer, 100);
373
xt_throw_i2xterr(XT_CONTEXT, XT_ERR_A_EXPECTED_NOT_B, keyword, buffer);
376
void XTToken::expectIdentifier(XTThreadPtr self)
382
getTokenText(buffer, 100);
383
xt_throw_i2xterr(XT_CONTEXT, XT_ERR_A_EXPECTED_NOT_B, "Identifier", buffer);
386
void XTToken::expectNumber(XTThreadPtr self)
392
getTokenText(buffer, 100);
393
xt_throw_i2xterr(XT_CONTEXT, XT_ERR_A_EXPECTED_NOT_B, "Value", buffer);
396
struct charset_info_st;
399
MX_CHARSET_INFO *tkn_charset;
402
XTToken *tkn_current;
407
XTTokenizer(bool convert, char *cstring) {
408
tkn_charset = myxt_getcharset(convert);
409
tkn_cstring = cstring;
410
tkn_curr_pos = cstring;
412
tkn_in_comment = FALSE;
415
virtual ~XTTokenizer(void) {
420
inline bool isSingleChar(int ch)
422
return ch != '$' && ch != '_' && myxt_ispunct(tkn_charset, ch);
425
inline bool isIdentifierChar(int ch)
427
return ch && !isSingleChar(ch) && !myxt_isspace(tkn_charset, ch);
430
inline bool isNumberChar(int ch, int next_ch)
432
return myxt_isdigit(tkn_charset, ch) || ((ch == '-' || ch == '+') && myxt_isdigit(tkn_charset, next_ch));
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);
440
XTToken *XTTokenizer::newToken(XTThreadPtr self, u_int type, char *start, char *end)
443
if (!(tkn_current = new XTToken()))
444
xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
446
tkn_current->initCString(type, start, end);
447
if (type == XT_TK_IDENTIFIER)
448
tkn_current->identifyReservedWord();
452
XTToken *XTTokenizer::nextToken(XTThreadPtr self)
455
u_int token_type = XT_TK_PUNCTUATION;
462
while (*tkn_curr_pos && myxt_isspace(tkn_charset, *tkn_curr_pos)) tkn_curr_pos++;
464
token_start = tkn_curr_pos;
465
switch (*tkn_curr_pos) {
467
return newToken(self, XT_TK_EOF, NULL, NULL);
468
// Comment: # ... EOL
471
while (*tkn_curr_pos && *tkn_curr_pos != '\n' && *tkn_curr_pos != '\r') tkn_curr_pos++;
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++;
479
if (myxt_isdigit(tkn_charset, tkn_curr_pos[1]))
484
if (myxt_isdigit(tkn_charset, tkn_curr_pos[1]))
490
if (*tkn_curr_pos == '*') {
491
// Comment: /* ... */
492
// Look for: /*!99999 ... */ version conditional statements
494
if (*tkn_curr_pos == '!') {
496
if (isdigit(*tkn_curr_pos)) {
497
while (isdigit(*tkn_curr_pos))
499
tkn_in_comment = true;
504
while (*tkn_curr_pos && !(*tkn_curr_pos == '*' && *(tkn_curr_pos+1) == '/')) tkn_curr_pos++;
505
if (*tkn_curr_pos == '*' && *(tkn_curr_pos+1) == '/')
511
token_type = XT_TK_STRING;
515
token_type = XT_TK_IDENTIFIER;
517
quote = *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)
526
/* TODO: Unless sql_mode == 'NO_BACKSLASH_ESCAPES'!!! */
527
if (*tkn_curr_pos == '\\') {
528
if (*(tkn_curr_pos+1) == quote) {
529
if (quote == '"' || quote == '\'')
536
if (*tkn_curr_pos == quote)
542
if (tkn_in_comment) {
543
if (tkn_curr_pos[1] == '/') {
544
tkn_in_comment = false;
549
/* No break required! */
551
if (isNumberChar(tkn_curr_pos[0], tkn_curr_pos[1]))
554
if (isSingleChar(*tkn_curr_pos)) {
555
token_type = XT_TK_PUNCTUATION;
556
// The rest are singles...
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))
570
token_type = XT_TK_NUMBER;
572
if (*tkn_curr_pos == '-' || *tkn_curr_pos == '+') {
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++;
581
if (*tkn_curr_pos == '.') {
584
while (*tkn_curr_pos && myxt_isdigit(tkn_charset, *tkn_curr_pos)) tkn_curr_pos++;
587
if (*tkn_curr_pos == 'e' || *tkn_curr_pos == 'E') {
590
if (isNumberChar(tkn_curr_pos[0], tkn_curr_pos[1])) {
593
if (*tkn_curr_pos == '-' || *tkn_curr_pos == '+')
595
while (*tkn_curr_pos && myxt_isdigit(tkn_charset, *tkn_curr_pos))
598
else if (!must_be_num)
599
token_type = XT_TK_IDENTIFIER;
602
if (must_be_num || !isIdentifierChar(*tkn_curr_pos))
605
/* Crazy, but true. An identifier can start by looking like a number! */
609
return newToken(self, token_type, token_start, tkn_curr_pos);
612
XTToken *XTTokenizer::nextToken(XTThreadPtr self, c_char *keyword, XTToken *tk)
614
tk->expectKeyWord(self, keyword);
615
return nextToken(self);
619
* -----------------------------------------------------------------------
624
We must parse the following syntax. Note that the constraints
625
may be embedded in a CREATE TABLE/ALTER TABLE statement.
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}]
633
class XTParseTable : public XTObject {
635
void raiseError(XTThreadPtr self, XTToken *tk, int err);
638
XTTokenizer *pt_tokenizer;
640
XTStringBufferRec pt_sbuffer;
642
void syntaxError(XTThreadPtr self, XTToken *tk);
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);
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.
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);
673
memset(&pt_sbuffer, 0, sizeof(XTStringBufferRec));
676
virtual void finalize(XTThreadPtr XT_UNUSED(self)) {
679
xt_sb_set_size(NULL, &pt_sbuffer, 0);
682
// Hooks to receive output from the parser:
683
virtual void setTableName(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(name), bool XT_UNUSED(alterTable)) {
685
virtual void addColumn(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(col_name), char *XT_UNUSED(old_col_name)) {
687
virtual void setDataType(XTThreadPtr self, char *cstring) {
689
xt_free(self, cstring);
691
virtual void setNull(XTThreadPtr XT_UNUSED(self), bool XT_UNUSED(nullOK)) {
693
virtual void setAutoInc(XTThreadPtr XT_UNUSED(self), bool XT_UNUSED(autoInc)) {
696
/* Add a contraint. If lastColumn is TRUE then add the contraint
697
* to the last column. If not, expect addListedColumn() to be called.
699
virtual void addConstraint(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(name), u_int XT_UNUSED(type), bool XT_UNUSED(lastColumn)) {
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.
705
virtual void moveColumn(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(col_name)) {
708
virtual void dropColumn(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(col_name)) {
711
virtual void dropConstraint(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(name), u_int XT_UNUSED(type)) {
714
virtual void setIndexName(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(name)) {
716
virtual void addListedColumn(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(index_col_name)) {
718
virtual void setReferencedTable(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(ref_schema), char *XT_UNUSED(ref_table)) {
720
virtual void addReferencedColumn(XTThreadPtr XT_UNUSED(self), char *XT_UNUSED(index_col_name)) {
722
virtual void setActions(XTThreadPtr XT_UNUSED(self), int XT_UNUSED(on_delete), int XT_UNUSED(on_update)) {
725
virtual void parseTable(XTThreadPtr self, bool convert, char *sql);
728
void XTParseTable::raiseError(XTThreadPtr self, XTToken *tk, int err)
732
tk->getTokenText(buffer, 100);
733
xt_throw_ixterr(XT_CONTEXT, err, buffer);
736
void XTParseTable::syntaxError(XTThreadPtr self, XTToken *tk)
738
raiseError(self, tk, XT_ERR_SYNTAX);
741
void XTParseTable::parseIdentifier(XTThreadPtr self, char *name)
743
pt_current->expectIdentifier(self);
745
if (pt_current->getString(name, XT_IDENTIFIER_NAME_SIZE) >= XT_IDENTIFIER_NAME_SIZE)
746
raiseError(self, pt_current, XT_ERR_ID_TOO_LONG);
748
pt_current = pt_tokenizer->nextToken(self);
751
int XTParseTable::parseKeyAction(XTThreadPtr self)
755
tk = pt_tokenizer->nextToken(self);
757
if (tk->isKeyWord("RESTRICT"))
758
return XT_KEY_ACTION_RESTRICT;
760
if (tk->isKeyWord("CASCADE"))
761
return XT_KEY_ACTION_CASCADE;
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;
771
if (tk->isKeyWord("NO")) {
772
tk = pt_tokenizer->nextToken(self);
773
tk->expectKeyWord(self, "ACTION");
774
return XT_KEY_ACTION_NO_ACTION;
777
syntaxError(self, tk);
781
void XTParseTable::parseTable(XTThreadPtr self, bool convert, char *sql)
785
pt_tokenizer = new XTTokenizer(convert, sql);
787
xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
788
pt_current = pt_tokenizer->nextToken(self);
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);
795
parseCreateIndex(self);
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);
807
else if (pt_current->isKeyWord("OPTIMIZE") || pt_current->isKeyWord("REPAIR")) {
808
/* OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
810
* GOTCHA: This cannot work if more than one table is specified,
811
* because then I cannot find the source table?!
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);
820
syntaxError(self, pt_current);
823
void XTParseTable::parseCreateTable(XTThreadPtr self)
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);
834
/* Table name is optional (when loading from dictionary)! */
835
if (!pt_current->isKeyWord("("))
836
parseTableName(self, false);
838
setTableName(self, NULL, false);
840
/* We do not support CREATE ... SELECT! */
841
if (pt_current->isKeyWord("(")) {
842
pt_current = pt_tokenizer->nextToken(self);
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"))
849
/* Allow empty table definition for temporary table. */
850
while (!pt_current->isEOF() && !pt_current->isKeyWord(")")) {
851
parseAddTableItem(self);
852
if (!pt_current->isKeyWord(","))
854
pt_current = pt_tokenizer->nextToken(self);
856
pt_current = pt_tokenizer->nextToken(self, ")", pt_current);
860
void XTParseTable::parseAddTableItem(XTThreadPtr self)
862
char name[XT_IDENTIFIER_NAME_SIZE];
865
if (pt_current->isKeyWord("CONSTRAINT")) {
866
pt_current = pt_tokenizer->nextToken(self);
867
if (pt_current->isIdentifier())
868
parseQualifiedName(self, NULL, name);
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);
875
addConstraint(self, name, XT_DD_KEY_PRIMARY, false);
876
optionalIndexType(self);
878
/* GATCHA: Wierd?! This syntax is used in a test:
879
* alter table t1 add primary key aaa(tt);
881
if (!pt_current->isKeyWord("("))
882
pt_current = pt_tokenizer->nextToken(self);
883
columnList(self, true);
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;
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);
898
if (pt_current->isReservedWord(XT_TK_INDEX) || pt_current->isReservedWord(XT_TK_KEY))
899
pt_current = pt_tokenizer->nextToken(self);
901
addConstraint(self, name, is_unique ? XT_DD_INDEX_UNIQUE : XT_DD_INDEX, false);
902
optionalIndexName(self);
903
optionalIndexType(self);
904
columnList(self, true);
906
else if (pt_current->isReservedWord(XT_TK_CHECK)) {
907
pt_current = pt_tokenizer->nextToken(self);
908
parseExpression(self, false);
910
else if (pt_current->isReservedWord(XT_TK_FOREIGN)) {
913
pt_current = pt_tokenizer->nextToken(self);
914
pt_current = pt_tokenizer->nextToken(self, "KEY", pt_current);
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,
922
parseReferenceDefinition(self, req_cols);
924
else if (pt_current->isKeyWord("(")) {
925
pt_current = pt_tokenizer->nextToken(self);
927
parseColumnDefinition(self, NULL);
928
if (!pt_current->isKeyWord(","))
930
pt_current = pt_tokenizer->nextToken(self);
932
pt_current = pt_tokenizer->nextToken(self, ")", pt_current);
935
if (pt_current->isReservedWord(XT_TK_COLUMN))
936
pt_current = pt_tokenizer->nextToken(self);
937
parseColumnDefinition(self, NULL);
938
parseMoveColumn(self);
940
/* GOTCHA: Support: create table t1 (a int not null, key `a` (a) key_block_size=1024)
941
* and any other undocumented syntax?!
943
parseExpression(self, true);
946
void XTParseTable::parseExpression(XTThreadPtr self, bool allow_reserved)
948
while (!pt_current->isEOF() && !pt_current->isKeyWord(",") &&
949
!pt_current->isKeyWord(")") && (allow_reserved || !pt_current->isReservedWord())) {
950
if (pt_current->isKeyWord("("))
953
pt_current = pt_tokenizer->nextToken(self);
957
void XTParseTable::parseBrackets(XTThreadPtr self)
960
pt_current = pt_tokenizer->nextToken(self, "(", pt_current);
962
if (pt_current->isEOF())
964
if (pt_current->isKeyWord("("))
966
if (pt_current->isKeyWord(")"))
968
pt_current = pt_tokenizer->nextToken(self);
972
void XTParseTable::parseMoveColumn(XTThreadPtr self)
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);
979
else if (pt_current->isKeyWord("AFTER")) {
980
char name[XT_IDENTIFIER_NAME_SIZE];
982
pt_current = pt_tokenizer->nextToken(self);
983
parseQualifiedName(self, NULL, name);
984
moveColumn(self, name);
988
void XTParseTable::parseQualifiedName(XTThreadPtr self, char *parent_name, char *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;
995
* COMMENT is elsewhere used as reserved word?!
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(".")) {
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);
1011
void XTParseTable::parseTableName(XTThreadPtr self, bool alterTable)
1013
char name[XT_IDENTIFIER_NAME_SIZE];
1015
parseQualifiedName(self, NULL, name);
1016
setTableName(self, name, alterTable);
1019
void XTParseTable::parseColumnDefinition(XTThreadPtr self, char *old_col_name)
1021
char col_name[XT_IDENTIFIER_NAME_SIZE];
1023
// column_definition
1024
parseQualifiedName(self, NULL, col_name);
1025
addColumn(self, col_name, old_col_name);
1026
parseDataType(self);
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);
1034
else if (pt_current->isReservedWord(XT_TK_NULL)) {
1035
pt_current = pt_tokenizer->nextToken(self);
1036
setNull(self, true);
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);
1043
else if (pt_current->isReservedWord(XT_TK_AUTO_INCREMENT)) {
1044
pt_current = pt_tokenizer->nextToken(self);
1045
setAutoInc(self, true);
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);
1053
else if (pt_current->isReservedWord(XT_TK_KEY)) {
1054
pt_current = pt_tokenizer->nextToken(self);
1055
addConstraint(self, NULL, XT_DD_INDEX, true);
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);
1062
else if (pt_current->isReservedWord(XT_TK_COMMENT)) {
1063
pt_current = pt_tokenizer->nextToken(self);
1064
pt_current = pt_tokenizer->nextToken(self);
1066
else if (pt_current->isReservedWord(XT_TK_REFERENCES)) {
1067
addConstraint(self, NULL, XT_DD_KEY_FOREIGN, true);
1068
parseReferenceDefinition(self, 1);
1070
else if (pt_current->isReservedWord(XT_TK_CHECK)) {
1071
pt_current = pt_tokenizer->nextToken(self);
1072
parseExpression(self, false);
1074
/* GOTCHA: Not in the documentation:
1075
* CREATE TABLE t1 (c varchar(255) NOT NULL COLLATE utf8_general_ci, INDEX (c))
1077
else if (pt_current->isReservedWord(XT_TK_COLLATE)) {
1078
pt_current = pt_tokenizer->nextToken(self);
1079
pt_current = pt_tokenizer->nextToken(self);
1086
void XTParseTable::parseDataType(XTThreadPtr self)
1088
/* Not actually implemented because MySQL allows undocumented
1090
* create table t1 (c national character varying(10))
1092
parseExpression(self, false);
1093
setDataType(self, NULL);
1096
void XTParseTable::optionalIndexName(XTThreadPtr self)
1099
if (!pt_current->isKeyWord("USING") && !pt_current->isKeyWord("(")) {
1100
char name[XT_IDENTIFIER_NAME_SIZE];
1102
parseIdentifier(self, name);
1103
setIndexName(self, name);
1107
void XTParseTable::optionalIndexType(XTThreadPtr self)
1109
// USING {BTREE | HASH}
1110
if (pt_current->isKeyWord("USING")) {
1111
pt_current = pt_tokenizer->nextToken(self);
1112
pt_current = pt_tokenizer->nextToken(self);
1116
u_int XTParseTable::columnList(XTThreadPtr self, bool index_cols)
1118
char name[XT_IDENTIFIER_NAME_SIZE];
1121
pt_current->expectKeyWord(self, "(");
1123
pt_current = pt_tokenizer->nextToken(self);
1124
parseQualifiedName(self, NULL, name);
1125
addListedColumn(self, name);
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);
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);
1138
} while (pt_current->isKeyWord(","));
1139
pt_current = pt_tokenizer->nextToken(self, ")", pt_current);
1143
void XTParseTable::parseReferenceDefinition(XTThreadPtr self, u_int req_cols)
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];
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);
1156
// [ (index_col_name,...) ]
1157
if (pt_current->isKeyWord("(")) {
1158
pt_current->expectKeyWord(self, "(");
1160
pt_current = pt_tokenizer->nextToken(self);
1161
parseQualifiedName(self, NULL, name);
1162
addReferencedColumn(self, name);
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);
1172
addReferencedColumn(self, NULL);
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);
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);
1189
syntaxError(self, pt_current);
1190
pt_current = pt_tokenizer->nextToken(self);
1193
setActions(self, on_delete, on_update);
1196
void XTParseTable::parseAlterTable(XTThreadPtr self)
1198
char name[XT_IDENTIFIER_NAME_SIZE];
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);
1206
if (pt_current->isKeyWord("ADD")) {
1207
pt_current = pt_tokenizer->nextToken(self);
1208
parseAddTableItem(self);
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);
1221
else if (pt_current->isKeyWord("DROP")) {
1222
pt_current = pt_tokenizer->nextToken(self);
1223
pt_current = pt_tokenizer->nextToken(self, "DEFAULT", pt_current);
1226
else if (pt_current->isKeyWord("CHANGE")) {
1227
char old_col_name[XT_IDENTIFIER_NAME_SIZE];
1229
pt_current = pt_tokenizer->nextToken(self);
1230
if (pt_current->isReservedWord(XT_TK_COLUMN))
1231
pt_current = pt_tokenizer->nextToken(self);
1233
parseQualifiedName(self, NULL, old_col_name);
1234
parseColumnDefinition(self, old_col_name);
1235
parseMoveColumn(self);
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);
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);
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);
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);
1263
if (pt_current->isReservedWord(XT_TK_COLUMN))
1264
pt_current = pt_tokenizer->nextToken(self);
1265
parseQualifiedName(self, NULL, name);
1266
dropColumn(self, name);
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);
1276
/* Just ignore the syntax until the next , */
1277
parseExpression(self, true);
1278
if (!pt_current->isKeyWord(","))
1280
pt_current = pt_tokenizer->nextToken(self);
1284
void XTParseTable::parseCreateIndex(XTThreadPtr self)
1286
char name[XT_IDENTIFIER_NAME_SIZE];
1287
bool is_unique = false;
1289
if (pt_current->isReservedWord(XT_TK_UNIQUE)) {
1290
pt_current = pt_tokenizer->nextToken(self);
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);
1307
void XTParseTable::parseDropIndex(XTThreadPtr self)
1309
char name[XT_IDENTIFIER_NAME_SIZE];
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);
1320
* -----------------------------------------------------------------------
1321
* Create/Alter table table
1324
class XTCreateTable : public XTParseTable {
1327
MX_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;
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));
1345
virtual void finalize(XTThreadPtr self) {
1347
ct_curr_table->release(self);
1348
XTParseTable::finalize(self);
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);
1360
virtual void parseTable(XTThreadPtr self, bool convert, char *sql);
1363
static void ri_free_create_table(XTThreadPtr self, XTCreateTable *ct)
1369
XTDDTable *xt_ri_create_table(XTThreadPtr self, bool convert, XTPathStrPtr tab_path, char *sql, XTDDTable *start_tab, XTDictionaryPtr source_dic)
1374
if (!(ct = new XTCreateTable(convert, tab_path))) {
1376
start_tab->release(self);
1377
xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
1380
ct->ct_curr_table = start_tab;
1382
pushr_(ri_free_create_table, ct);
1384
ct->parseTable(self, convert, sql);
1386
/* Return the table ... */
1387
dd_tab = ct->ct_curr_table;
1388
ct->ct_curr_table = NULL;
1391
*source_dic = ct->ct_curr_dic;
1397
void XTCreateTable::parseTable(XTThreadPtr self, bool convert, char *sql)
1401
ct_contraint_no = 0;
1402
XTParseTable::parseTable(self, convert, sql);
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);
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);
1420
void XTCreateTable::setTableName(XTThreadPtr self, char *name, bool alterTable)
1422
char path[PATH_MAX];
1427
xt_strcpy(PATH_MAX, path, ct_tab_path->ps_path);
1428
xt_remove_last_name_of_path(path);
1431
char buffer[XT_IDENTIFIER_NAME_SIZE];
1434
myxt_static_convert_identifier(self, ct_charset, name, buffer, XT_IDENTIFIER_NAME_SIZE);
1436
myxt_static_convert_table_name(self, buffer, &path[len], PATH_MAX - len);
1439
xt_strcat(PATH_MAX, path, name);
1444
/* Find the table... */
1445
pushsr_(tab, xt_heap_release, xt_use_table(self, (XTPathStrPtr) path, FALSE, TRUE));
1447
/* Clone the foreign key definitions: */
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;
1456
/* Copy the dictionary! */
1457
ct_curr_dic = tab->tab_dic;
1460
freer_(); // xt_heap_release(tab)
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.
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!
1474
void XTCreateTable::addColumn(XTThreadPtr self, char *new_name, char *old_name)
1476
char new_col_name[XT_IDENTIFIER_NAME_SIZE];
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);
1481
char old_col_name[XT_IDENTIFIER_NAME_SIZE];
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);
1488
void XTCreateTable::addConstraint(XTThreadPtr self, char *name, u_int type, bool lastColumn)
1490
/* We are only interested in foreign keys! */
1491
if (type == XT_DD_KEY_FOREIGN) {
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;
1500
ct_curr_constraint->co_name = myxt_convert_identifier(self, ct_charset, name);
1502
// Generate a default constraint name:
1504
sprintf(buffer, "FOREIGN_%d", ct_contraint_no);
1505
ct_curr_constraint->co_name = xt_dup_string(self, buffer);
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);
1513
if (!(cref = new XTDDColumnRef())) {
1514
xt_free(self, col_name);
1515
xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
1517
cref->cr_col_name = col_name;
1518
ct_curr_constraint->co_cols.append(self, cref);
1522
/* Other constraints/indexes do not interest us: */
1523
ct_curr_constraint = NULL;
1526
void XTCreateTable::dropConstraint(XTThreadPtr self, char *name, u_int type)
1528
if (type == XT_DD_KEY_FOREIGN && name) {
1530
XTDDForeignKey *fkey;
1531
char con_name[XT_IDENTIFIER_NAME_SIZE];
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);
1544
void XTCreateTable::addListedColumn(XTThreadPtr self, char *index_col_name)
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);
1550
if (!(cref = new XTDDColumnRef())) {
1551
xt_free(self, name);
1552
xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
1554
cref->cr_col_name = name;
1555
ct_curr_constraint->co_cols.append(self, cref);
1559
void XTCreateTable::setReferencedTable(XTThreadPtr self, char *ref_schema, char *ref_table)
1561
XTDDForeignKey *fk = (XTDDForeignKey *) ct_curr_constraint;
1562
char path[PATH_MAX];
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);
1571
xt_strcpy(PATH_MAX, path, ct_tab_path->ps_path);
1572
xt_remove_last_name_of_path(path);
1574
char buffer[XT_IDENTIFIER_NAME_SIZE];
1577
myxt_static_convert_identifier(self, ct_charset, ref_table, buffer, XT_IDENTIFIER_NAME_SIZE);
1579
myxt_static_convert_table_name(self, buffer, &path[len], PATH_MAX - len);
1582
xt_strcat(PATH_MAX, path, ref_table);
1585
fk->fk_ref_tab_name = (XTPathStrPtr) xt_dup_string(self, path);
1588
/* If the referenced column is NULL, this means
1589
* duplicate the local column list!
1591
void XTCreateTable::addReferencedColumn(XTThreadPtr self, char *index_col_name)
1593
XTDDForeignKey *fk = (XTDDForeignKey *) ct_curr_constraint;
1594
XTDDColumnRef *cref;
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);
1603
cref->cr_col_name = name;
1604
fk->fk_ref_cols.append(self, cref);
1607
fk->fk_ref_cols.clone(self, &fk->co_cols);
1610
void XTCreateTable::setActions(XTThreadPtr XT_UNUSED(self), int on_delete, int on_update)
1612
XTDDForeignKey *fk = (XTDDForeignKey *) ct_curr_constraint;
1614
fk->fk_on_delete = on_delete;
1615
fk->fk_on_update = on_update;
1619
* -----------------------------------------------------------------------
1620
* Dictionary methods
1623
void XTDDColumn::init(XTThreadPtr self, XTObject *obj) {
1624
XTDDColumn *col = (XTDDColumn *) obj;
1626
XTObject::init(self, obj);
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;
1635
void XTDDColumn::finalize(XTThreadPtr self)
1638
xt_free(self, dc_name);
1640
xt_free(self, dc_data_type);
1643
void XTDDColumn::loadString(XTThreadPtr self, XTStringBufferPtr sb)
1645
xt_sb_concat(self, sb, "`");
1646
xt_sb_concat(self, sb, dc_name);
1647
xt_sb_concat(self, sb, "` ");
1649
xt_sb_concat(self, sb, dc_data_type);
1651
xt_sb_concat(self, sb, " NULL");
1653
xt_sb_concat(self, sb, " NOT NULL");
1655
xt_sb_concat(self, sb, " AUTO_INCREMENT");
1659
void XTDDColumnRef::init(XTThreadPtr self, XTObject *obj)
1661
XTDDColumnRef *cr = (XTDDColumnRef *) obj;
1663
XTObject::init(self, obj);
1664
cr_col_name = xt_dup_string(self, cr->cr_col_name);
1667
void XTDDColumnRef::finalize(XTThreadPtr self)
1669
XTObject::finalize(self);
1671
xt_free(self, cr_col_name);
1676
void XTDDConstraint::init(XTThreadPtr self, XTObject *obj)
1678
XTDDConstraint *co = (XTDDConstraint *) obj;
1680
XTObject::init(self, obj);
1681
co_type = co->co_type;
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);
1689
void XTDDConstraint::loadString(XTThreadPtr self, XTStringBufferPtr sb)
1692
xt_sb_concat(self, sb, "CONSTRAINT `");
1693
xt_sb_concat(self, sb, co_name);
1694
xt_sb_concat(self, sb, "` ");
1698
xt_sb_concat(self, sb, "INDEX ");
1700
case XT_DD_INDEX_UNIQUE:
1701
xt_sb_concat(self, sb, "UNIQUE INDEX ");
1703
case XT_DD_KEY_PRIMARY:
1704
xt_sb_concat(self, sb, "PRIMARY KEY ");
1706
case XT_DD_KEY_FOREIGN:
1707
xt_sb_concat(self, sb, "FOREIGN KEY ");
1711
xt_sb_concat(self, sb, "`");
1712
xt_sb_concat(self, sb, co_ind_name);
1713
xt_sb_concat(self, sb, "` ");
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);
1721
xt_sb_concat(self, sb, "`)");
1724
void XTDDConstraint::alterColumnName(XTThreadPtr self, char *from_name, char *to_name)
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);
1733
xt_free(self, col->cr_col_name);
1734
col->cr_col_name = name;
1740
void XTDDConstraint::getColumnList(char *buffer, size_t size)
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, "` (`");
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);
1754
xt_strcat(size, buffer, "`)");
1757
bool XTDDConstraint::sameColumns(XTDDConstraint *co)
1761
if (co_cols.size() != co->co_cols.size())
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)
1771
bool XTDDConstraint::samePrefixColumns(XTDDConstraint *co)
1775
if (co_cols.size() > co->co_cols.size())
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)
1785
bool XTDDConstraint::attachColumns()
1789
for (u_int i=0; i<co_cols.size(); i++) {
1790
if (!(col = co_table->findColumn(co_cols.itemAt(i)->cr_col_name)))
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;
1799
void XTDDTableRef::finalize(XTThreadPtr self)
1803
if ((fk = tr_fkey)) {
1805
fk->removeReference(self);
1806
xt_heap_release(self, fk->co_table->dt_table); /* We referenced the database table, not the foreign key */
1808
XTObject::finalize(self);
1811
bool XTDDTableRef::checkReference(xtWord1 *before_buf, XTThreadPtr thread)
1813
XTIndexPtr loc_ind, ind;
1814
xtBool no_null = TRUE;
1816
XTIdxSearchKeyRec search_key;
1821
if (!(loc_ind = tr_fkey->getReferenceIndexPtr()))
1824
if (!(ind = tr_fkey->getIndexPtr()))
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;
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)))
1842
if (!xt_idx_search(ot, ind, &search_key))
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)) {
1848
xw.xw_xn_id = xn_id;
1849
if (!xt_xn_wait_for_xact(thread, &xw, NULL))
1855
/* We found a matching child: */
1856
xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_ROW_IS_REFERENCED, tr_fkey->co_name);
1859
if (!xt_idx_next(ot, ind, &search_key))
1865
/* No matching children, all OK: */
1869
if (ot->ot_ind_rhandle) {
1870
xt_ind_release_handle(ot->ot_ind_rhandle, FALSE, thread);
1871
ot->ot_ind_rhandle = NULL;
1873
xt_db_return_table_to_pool_ns(ot);
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.
1881
bool XTDDTableRef::modifyRow(XTOpenTablePtr XT_UNUSED(ref_ot), xtWord1 *before_buf, xtWord1 *after_buf, XTThreadPtr thread)
1883
XTIndexPtr loc_ind, ind;
1884
xtBool no_null = TRUE;
1886
XTIdxSearchKeyRec search_key;
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;
1894
after_info.ib_free = FALSE;
1896
if (!(loc_ind = tr_fkey->getReferenceIndexPtr()))
1899
if (!(ind = tr_fkey->getIndexPtr()))
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;
1913
if (!(after_key = (xtWord1 *) xt_malloc_ns(XT_INDEX_MAX_KEY_SIZE)))
1915
after_key_len = myxt_create_foreign_key_from_row(loc_ind, after_key, after_buf, ind, NULL);
1917
/* Check whether the key value has changed, if not, we have nothing
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)
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)))
1931
if (!xt_idx_search(ot, ind, &search_key))
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)) {
1937
xw.xw_xn_id = xn_id;
1938
if (!xt_xn_wait_for_xact(thread, &xw, NULL))
1944
/* We found a matching child: */
1946
case XT_KEY_ACTION_CASCADE:
1948
/* Do a cascaded update: */
1949
if (!xt_tab_load_record(ot, ot->ot_curr_rec_id, &after_info))
1952
if (!myxt_create_row_from_key(ot, ind, after_key, after_key_len, after_info.ib_db.db_data))
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);
1963
/* Do a cascaded delete: */
1964
if (!xt_tab_delete_record(ot, NULL))
1968
case XT_KEY_ACTION_SET_NULL:
1969
if (!xt_tab_load_record(ot, ot->ot_curr_rec_id, &after_info))
1972
myxt_set_null_row_from_key(ot, ind, after_info.ib_db.db_data);
1974
if (!xt_tab_update_record(ot, NULL, after_info.ib_db.db_data))
1977
case XT_KEY_ACTION_SET_DEFAULT:
1979
if (!xt_tab_load_record(ot, ot->ot_curr_rec_id, &after_info))
1982
myxt_set_default_row_from_key(ot, ind, after_info.ib_db.db_data);
1984
if (!xt_tab_update_record(ot, NULL, after_info.ib_db.db_data))
1988
case XT_KEY_ACTION_NO_ACTION:
1989
#ifdef XT_IMPLEMENT_NO_ACTION
1990
XTRestrictItemRec r;
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))
1999
xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_ROW_IS_REFERENCED, tr_fkey->co_name);
2002
/* Fall throught to next: */
2004
if (!xt_idx_next(ot, ind, &search_key))
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;
2015
xt_db_return_table_to_pool_ns(ot);
2018
xt_ib_free(NULL, &after_info);
2020
xt_free_ns(after_key);
2024
if (ot->ot_ind_rhandle) {
2025
xt_ind_release_handle(ot->ot_ind_rhandle, FALSE, thread);
2026
ot->ot_ind_rhandle = NULL;
2028
xt_db_return_table_to_pool_ns(ot);
2031
xt_ib_free(NULL, &after_info);
2033
xt_free_ns(after_key);
2037
void XTDDTableRef::deleteAllRows(XTThreadPtr self)
2043
if (!tr_fkey->getReferenceIndexPtr())
2046
if (!tr_fkey->getIndexPtr())
2049
if (!(ot = xt_db_open_table_using_tab(tr_fkey->co_table->dt_table, self)))
2052
/* {FREE-ROWS-BAD} */
2054
row_count = ((xtInt8) ot->ot_table->tab_row_eof_id) - 1;
2055
row_count -= (xtInt8) ot->ot_table->tab_row_fnum;
2057
/* Check if there are any rows in the referencing table: */
2058
if (!xt_tab_seq_init(ot))
2061
if (!(buffer = (xtWord1 *) xt_malloc(self, ot->ot_table->tab_dic.dic_mysql_buf_size)))
2064
if (!xt_tab_seq_next(ot, buffer, &eof))
2067
xt_free(self, buffer);
2069
xt_tab_seq_exit(ot);
2071
xt_db_return_table_to_pool_ns(ot);
2074
xt_throw_ixterr(XT_CONTEXT, XT_ERR_ROW_IS_REFERENCED, tr_fkey->co_name);
2078
xt_free(self, buffer);
2081
xt_tab_seq_exit(ot);
2084
xt_db_return_table_to_pool_ns(ot);
2088
void XTDDIndex::init(XTThreadPtr self, XTObject *obj)
2090
XTDDConstraint::init(self, obj);
2093
XTIndexPtr XTDDIndex::getIndexPtr()
2095
if (in_index >= co_table->dt_table->tab_dic.dic_key_count) {
2098
if (!(in = co_table->findIndex(this)))
2100
in_index = in->in_index;
2102
return co_table->dt_table->tab_dic.dic_keys[in_index];
2105
void XTDDForeignKey::init(XTThreadPtr self, XTObject *obj)
2107
XTDDForeignKey *fk = (XTDDForeignKey *) obj;
2109
XTDDIndex::init(self, obj);
2110
if (fk->fk_ref_tab_name)
2111
fk_ref_tab_name = (XTPathStrPtr) xt_dup_string(self, fk->fk_ref_tab_name->ps_path);
2112
fk_ref_cols.clone(self, &fk->fk_ref_cols);
2113
fk_on_delete = fk->fk_on_delete;
2114
fk_on_update = fk->fk_on_update;
2117
void XTDDForeignKey::finalize(XTThreadPtr self)
2121
if (fk_ref_tab_name) {
2122
xt_free(self, fk_ref_tab_name);
2123
fk_ref_tab_name = NULL;
2126
if ((ref_tab = fk_ref_table)) {
2127
fk_ref_table = NULL;
2128
ref_tab->removeReference(self, this);
2129
xt_heap_release(self, ref_tab->dt_table); /* We referenced the table, not the index! */
2132
fk_ref_index = UINT_MAX;
2134
fk_ref_cols.deleteAll(self);
2135
XTDDConstraint::finalize(self);
2138
void XTDDForeignKey::loadString(XTThreadPtr self, XTStringBufferPtr sb)
2140
char schema_name[XT_IDENTIFIER_NAME_SIZE];
2142
XTDDConstraint::loadString(self, sb);
2143
xt_sb_concat(self, sb, " REFERENCES `");
2144
xt_2nd_last_name_of_path(XT_IDENTIFIER_NAME_SIZE, schema_name, fk_ref_tab_name->ps_path);
2145
xt_sb_concat(self, sb, schema_name);
2146
xt_sb_concat(self, sb, "`.`");
2147
xt_sb_concat(self, sb, xt_last_name_of_path(fk_ref_tab_name->ps_path));
2148
xt_sb_concat(self, sb, "` ");
2150
xt_sb_concat(self, sb, "(`");
2151
xt_sb_concat(self, sb, fk_ref_cols.itemAt(0)->cr_col_name);
2152
for (u_int i=1; i<fk_ref_cols.size(); i++) {
2153
xt_sb_concat(self, sb, "`, `");
2154
xt_sb_concat(self, sb, fk_ref_cols.itemAt(i)->cr_col_name);
2156
xt_sb_concat(self, sb, "`)");
2158
if (fk_on_delete != XT_KEY_ACTION_RESTRICT) {
2159
xt_sb_concat(self, sb, " ON DELETE ");
2160
switch (fk_on_delete) {
2161
case XT_KEY_ACTION_CASCADE: xt_sb_concat(self, sb, "CASCADE"); break;
2162
case XT_KEY_ACTION_SET_NULL: xt_sb_concat(self, sb, "SET NULL"); break;
2163
case XT_KEY_ACTION_SET_DEFAULT: xt_sb_concat(self, sb, "SET DEFAULT"); break;
2164
case XT_KEY_ACTION_NO_ACTION: xt_sb_concat(self, sb, "NO ACTION"); break;
2167
if (fk_on_update != XT_KEY_ACTION_RESTRICT) {
2168
xt_sb_concat(self, sb, " ON UPDATE ");
2169
switch (fk_on_update) {
2170
case XT_KEY_ACTION_RESTRICT: xt_sb_concat(self, sb, "RESTRICT"); break;
2171
case XT_KEY_ACTION_CASCADE: xt_sb_concat(self, sb, "CASCADE"); break;
2172
case XT_KEY_ACTION_SET_NULL: xt_sb_concat(self, sb, "SET NULL"); break;
2173
case XT_KEY_ACTION_SET_DEFAULT: xt_sb_concat(self, sb, "SET DEFAULT"); break;
2174
case XT_KEY_ACTION_NO_ACTION: xt_sb_concat(self, sb, "NO ACTION"); break;
2179
void XTDDForeignKey::getReferenceList(char *buffer, size_t size)
2182
xt_strcpy(size, buffer + 1, xt_last_name_of_path(fk_ref_tab_name->ps_path));
2183
xt_strcat(size, buffer, "` (");
2184
xt_strcat(size, buffer, fk_ref_cols.itemAt(0)->cr_col_name);
2185
for (u_int i=1; i<fk_ref_cols.size(); i++) {
2186
xt_strcat(size, buffer, ", ");
2187
xt_strcat(size, buffer, fk_ref_cols.itemAt(i)->cr_col_name);
2189
xt_strcat(size, buffer, ")");
2192
struct XTIndex *XTDDForeignKey::getReferenceIndexPtr()
2194
if (!fk_ref_table) {
2195
xt_register_taberr(XT_REG_CONTEXT, XT_ERR_REF_TABLE_NOT_FOUND, fk_ref_tab_name);
2198
if (fk_ref_index >= fk_ref_table->dt_table->tab_dic.dic_key_count) {
2201
if (!(in = fk_ref_table->findReferenceIndex(this)))
2203
if (!checkReferencedTypes(fk_ref_table))
2205
fk_ref_index = in->in_index;
2208
return fk_ref_table->dt_table->tab_dic.dic_keys[fk_ref_index];
2211
bool XTDDForeignKey::sameReferenceColumns(XTDDConstraint *co)
2215
if (fk_ref_cols.size() != co->co_cols.size())
2217
while (i<fk_ref_cols.size()) {
2218
if (myxt_strcasecmp(fk_ref_cols.itemAt(i)->cr_col_name, co->co_cols.itemAt(i)->cr_col_name) != 0)
2225
bool XTDDForeignKey::samePrefixReferenceColumns(XTDDConstraint *co)
2229
if (fk_ref_cols.size() > co->co_cols.size())
2231
while (i<fk_ref_cols.size()) {
2232
if (myxt_strcasecmp(fk_ref_cols.itemAt(i)->cr_col_name, co->co_cols.itemAt(i)->cr_col_name) != 0)
2239
bool XTDDForeignKey::checkReferencedTypes(XTDDTable *dt)
2241
XTDDColumn *col, *ref_col;
2242
XTDDEnumerableColumn *enum_col, *enum_ref_col;
2244
if (XT_IS_TEMP_TABLE(dt->dt_table->tab_dic.dic_tab_flags)) {
2245
xt_register_xterr(XT_REG_CONTEXT, XT_ERR_FK_REF_TEMP_TABLE);
2249
for (u_int i=0; i<co_cols.size() && i<fk_ref_cols.size(); i++) {
2250
col = co_table->findColumn(co_cols.itemAt(i)->cr_col_name);
2251
ref_col = dt->findColumn(fk_ref_cols.itemAt(i)->cr_col_name);
2252
if (!col || !ref_col)
2255
enum_col = col->castToEnumerable();
2256
enum_ref_col = ref_col->castToEnumerable();
2258
if (!enum_col && !enum_ref_col && (strcmp(col->dc_data_type, ref_col->dc_data_type) == 0))
2261
/* Allow match varchar(30) == varchar(40): */
2262
if (strncmp(col->dc_data_type, "varchar", 7) == 0 && strncmp(ref_col->dc_data_type, "varchar", 7) == 0) {
2265
t1 = col->dc_data_type + 7;
2266
while (*t1 && (isdigit(*t1) || *t1 == '(' || *t1 == ')')) t1++;
2267
t2 = col->dc_data_type + 7;
2268
while (*t2 && (isdigit(*t2) || *t2 == '(' || *t2 == ')')) t2++;
2270
if (strcmp(t1, t2) == 0)
2275
* MySQL stores ENUMs an integer indexes for string values. That's why
2276
* it is ok to have refrences between columns that are different ENUMs as long
2277
* as they contain equal number of members, so that for example a cascase update
2278
* will not cause an invaid value to be stored in the child table.
2280
* The above is also true for SETs.
2284
if (enum_col && enum_ref_col &&
2285
(enum_col->enum_size == enum_ref_col->enum_size) &&
2286
(enum_col->is_enum == enum_ref_col->is_enum))
2289
xt_register_tabcolerr(XT_REG_CONTEXT, XT_ERR_REF_TYPE_WRONG, fk_ref_tab_name, ref_col->dc_name);
2295
void XTDDForeignKey::removeReference(XTThreadPtr self)
2299
xt_xlock_rwlock(self, &co_table->dt_ref_lock);
2300
pushr_(xt_unlock_rwlock, &co_table->dt_ref_lock);
2302
if ((ref_tab = fk_ref_table)) {
2303
fk_ref_table = NULL;
2304
ref_tab->removeReference(self, this);
2305
xt_heap_release(self, ref_tab->dt_table); /* We referenced the table, not the index! */
2308
fk_ref_index = UINT_MAX;
2310
freer_(); // xt_unlock_rwlock(&co_table->dt_ref_lock);
2314
* A row was inserted, check that a key exists in the referenced
2317
bool XTDDForeignKey::insertRow(xtWord1 *before_buf, xtWord1 *rec_buf, XTThreadPtr thread)
2319
XTIndexPtr loc_ind, ind;
2320
xtBool no_null = TRUE;
2322
XTIdxSearchKeyRec search_key;
2326
/* This lock ensures that the foreign key references are not
2329
xt_slock_rwlock_ns(&co_table->dt_ref_lock);
2331
if (!(loc_ind = getIndexPtr()))
2334
if (!(ind = getReferenceIndexPtr()))
2337
search_key.sk_key_value.sv_flags = 0;
2338
search_key.sk_key_value.sv_rec_id = 0;
2339
search_key.sk_key_value.sv_row_id = 0;
2340
search_key.sk_key_value.sv_key = search_key.sk_key_buf;
2341
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);
2342
search_key.sk_on_key = FALSE;
2348
u_int before_key_len;
2349
xtWord1 before_key[XT_INDEX_MAX_KEY_SIZE];
2351
/* If there is a before buffer, this insert was an update, so check
2352
* if the key value has changed. If not, we need not do anything.
2354
before_key_len = myxt_create_foreign_key_from_row(loc_ind, before_key, before_buf, ind, NULL);
2356
/* Check whether the key value has changed, if not, we have nothing
2359
if (search_key.sk_key_value.sv_length == before_key_len &&
2360
memcmp(search_key.sk_key_buf, before_key, before_key_len) == 0)
2364
/* Search for the key in the parent (referenced) table: */
2365
if (!(ot = xt_db_open_table_using_tab(fk_ref_table->dt_table, thread)))
2369
if (!xt_idx_search(ot, ind, &search_key))
2372
while (ot->ot_curr_rec_id) {
2373
if (!search_key.sk_on_key)
2376
switch (xt_tab_maybe_committed(ot, ot->ot_curr_rec_id, &xn_id, &ot->ot_curr_row_id, &ot->ot_curr_updated)) {
2378
/* We should not get a deadlock here because the thread
2379
* that we are waiting for, should not doing
2380
* data definition (i.e. should not be trying to
2381
* get an exclusive lock on dt_ref_lock.
2383
xw.xw_xn_id = xn_id;
2384
if (!xt_xn_wait_for_xact(thread, &xw, NULL))
2390
/* We found a matching parent: */
2391
if (ot->ot_ind_rhandle) {
2392
xt_ind_release_handle(ot->ot_ind_rhandle, FALSE, thread);
2393
ot->ot_ind_rhandle = NULL;
2395
xt_db_return_table_to_pool_ns(ot);
2398
if (!xt_idx_next(ot, ind, &search_key))
2404
xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_NO_REFERENCED_ROW, co_name);
2407
if (ot->ot_ind_rhandle) {
2408
xt_ind_release_handle(ot->ot_ind_rhandle, FALSE, thread);
2409
ot->ot_ind_rhandle = NULL;
2411
xt_db_return_table_to_pool_ns(ot);
2414
xt_unlock_rwlock_ns(&co_table->dt_ref_lock);
2418
xt_unlock_rwlock_ns(&co_table->dt_ref_lock);
2423
* Convert XT_KEY_ACTION_* constants to strings
2425
const char *XTDDForeignKey::actionTypeToString(int action)
2429
case XT_KEY_ACTION_RESTRICT:
2431
case XT_KEY_ACTION_CASCADE:
2433
case XT_KEY_ACTION_SET_NULL:
2435
case XT_KEY_ACTION_SET_DEFAULT:
2437
case XT_KEY_ACTION_NO_ACTION:
2444
void XTDDTable::init(XTThreadPtr self)
2446
xt_init_rwlock_with_autoname(self, &dt_ref_lock);
2450
void XTDDTable::init(XTThreadPtr self, XTObject *obj)
2452
XTDDTable *tab = (XTDDTable *) obj;
2456
XTObject::init(self, obj);
2457
dt_cols.clone(self, &tab->dt_cols);
2458
dt_indexes.clone(self, &tab->dt_indexes);
2459
dt_fkeys.clone(self, &tab->dt_fkeys);
2461
for (i=0; i<dt_indexes.size(); i++)
2462
dt_indexes.itemAt(i)->co_table = this;
2463
for (i=0; i<dt_fkeys.size(); i++)
2464
dt_fkeys.itemAt(i)->co_table = this;
2467
void XTDDTable::finalize(XTThreadPtr self)
2471
removeReferences(self);
2473
dt_cols.deleteAll(self);
2474
dt_indexes.deleteAll(self);
2475
dt_fkeys.deleteAll(self);
2479
dt_trefs = dt_trefs->tr_next;
2483
xt_free_rwlock(&dt_ref_lock);
2486
XTDDColumn *XTDDTable::findColumn(char *name)
2490
for (u_int i=0; i<dt_cols.size(); i++) {
2491
col = dt_cols.itemAt(i);
2492
if (myxt_strcasecmp(name, col->dc_name) == 0)
2498
void XTDDTable::loadString(XTThreadPtr self, XTStringBufferPtr sb)
2502
/* I do not specify a table name because that is known */
2503
xt_sb_concat(self, sb, "CREATE TABLE (\n ");
2505
/* We only need to save the foreign key definitions!!
2506
for (i=0; i<dt_cols.size(); i++) {
2508
xt_sb_concat(self, sb, ",\n ");
2509
dt_cols.itemAt(i)->loadString(self, sb);
2512
for (i=0; i<dt_indexes.size(); i++) {
2513
xt_sb_concat(self, sb, ",\n ");
2514
dt_indexes.itemAt(i)->loadString(self, sb);
2518
for (i=0; i<dt_fkeys.size(); i++) {
2520
xt_sb_concat(self, sb, ",\n ");
2521
dt_fkeys.itemAt(i)->loadString(self, sb);
2524
xt_sb_concat(self, sb, "\n)\n");
2527
void XTDDTable::loadForeignKeyString(XTThreadPtr self, XTStringBufferPtr sb)
2529
for (u_int i=0; i<dt_fkeys.size(); i++) {
2530
xt_sb_concat(self, sb, ",\n ");
2531
dt_fkeys.itemAt(i)->loadString(self, sb);
2535
/* Change all references to the given column name to new name. */
2536
void XTDDTable::alterColumnName(XTThreadPtr self, char *from_name, char *to_name)
2540
/* We only alter references in the foreign keys (we copied the
2541
* other changes from MySQL).
2543
for (i=0; i<dt_fkeys.size(); i++)
2544
dt_fkeys.itemAt(i)->alterColumnName(self, from_name, to_name);
2547
void XTDDTable::attachReference(XTThreadPtr self, XTDDForeignKey *fk)
2551
/* Remove the reference to this FK if one exists: */
2552
removeReference(self, fk);
2554
if (!fk->checkReferencedTypes(this)) {
2555
if (!self->st_ignore_fkeys)
2559
xt_xlock_rwlock(self, &dt_ref_lock);
2560
pushr_(xt_unlock_rwlock, &dt_ref_lock);
2562
if (!(tr = new XTDDTableRef()))
2563
xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
2565
tr->tr_next = dt_trefs;
2568
/* Reference the database table of the foreign key, not the FK itself.
2569
* Just referencing the key will not guarantee that the
2570
* table remains valid because the FK does not reference the
2573
xt_heap_reference(self, fk->co_table->dt_table);
2575
freer_(); // xt_unlock_rwlock(&dt_ref_lock);
2579
* Remove the reference to the given foreign key.
2581
void XTDDTable::removeReference(XTThreadPtr self, XTDDForeignKey *fk)
2583
XTDDTableRef *tr, *prev_tr = NULL;
2585
xt_xlock_rwlock(self, &dt_ref_lock);
2586
pushr_(xt_unlock_rwlock, &dt_ref_lock);
2590
if (tr->tr_fkey == fk) {
2592
prev_tr->tr_next = tr->tr_next;
2594
dt_trefs = tr->tr_next;
2600
freer_(); // xt_unlock_rwlock(&dt_ref_lock);
2605
void XTDDTable::checkForeignKeyReference(XTThreadPtr self, XTDDForeignKey *fk)
2609
for (u_int i=0; i<fk->fk_ref_cols.size(); i++) {
2610
cr = fk->fk_ref_cols.itemAt(i);
2611
if (!findColumn(cr->cr_col_name))
2612
xt_throw_tabcolerr(XT_CONTEXT, XT_ERR_COLUMN_NOT_FOUND, fk->fk_ref_tab_name, cr->cr_col_name);
2616
void XTDDTable::attachReference(XTThreadPtr self, XTDDTable *dt)
2620
for (u_int i=0; i<dt_fkeys.size(); i++) {
2621
fk = dt_fkeys.itemAt(i);
2622
if (xt_tab_compare_names(fk->fk_ref_tab_name->ps_path, dt->dt_table->tab_name->ps_path) == 0) {
2623
fk->removeReference(self);
2625
dt->attachReference(self, fk);
2627
xt_xlock_rwlock(self, &dt_ref_lock);
2628
pushr_(xt_unlock_rwlock, &dt_ref_lock);
2629
/* Referenced the table, not the index!
2630
* We do this because we know that if the table is referenced, the
2631
* index will remain valid!
2632
* This is because the table references the index, and only
2633
* releases it when the table is released. The index does not
2634
* reference the table though!
2636
xt_heap_reference(self, dt->dt_table);
2637
fk->fk_ref_table = dt;
2638
freer_(); // xt_unlock_rwlock(&dt_ref_lock);
2644
* This function assumes the database table list is locked!
2646
void XTDDTable::attachReferences(XTThreadPtr self, XTDatabaseHPtr db)
2651
XTHashEnumRec tables;
2653
/* Search for table referenced by this table. */
2654
for (u_int i=0; i<dt_fkeys.size(); i++) {
2655
fk = dt_fkeys.itemAt(i);
2656
fk->removeReference(self);
2658
// if self-reference
2659
if (xt_tab_compare_names(fk->fk_ref_tab_name->ps_path, this->dt_table->tab_name->ps_path) == 0)
2660
fk->fk_ref_table = this;
2662
/* get pointer to the referenced table, load it if needed
2663
* cyclic references are being handled, absent table is ignored
2665
tab = xt_use_table_no_lock(self, db, fk->fk_ref_tab_name, /*TRUE*/FALSE, /*FALSE*/TRUE, NULL);
2668
pushr_(xt_heap_release, tab);
2669
if ((dt = tab->tab_dic.dic_table)) {
2670
// Add a reverse reference:
2671
dt->attachReference(self, fk);
2672
xt_heap_reference(self, dt->dt_table); /* Referenced the table, not the index! */
2673
fk->fk_ref_table = dt;
2675
freer_(); // xt_heap_release(tab)
2677
else if (!self->st_ignore_fkeys) {
2678
xt_throw_taberr(XT_CONTEXT, XT_ERR_REF_TABLE_NOT_FOUND, fk->fk_ref_tab_name);
2683
/* Search for tables that reference this table. */
2684
xt_ht_enum(self, dt_table->tab_db->db_tables, &tables);
2685
while ((tab = (XTTableHPtr) xt_ht_next(self, &tables))) {
2686
if (tab == this->dt_table) /* no need to re-reference itself, also this fails with "native" pthreads */
2688
xt_heap_reference(self, tab);
2689
pushr_(xt_heap_release, tab);
2690
if ((dt = tab->tab_dic.dic_table))
2691
dt->attachReference(self, this);
2692
freer_(); // xt_heap_release(tab)
2696
void XTDDTable::removeReferences(XTThreadPtr self)
2702
xt_xlock_rwlock(self, &dt_ref_lock);
2703
pushr_(xt_unlock_rwlock, &dt_ref_lock);
2705
for (u_int i=0; i<dt_fkeys.size(); i++) {
2706
fk = dt_fkeys.itemAt(i);
2707
if ((tab = fk->fk_ref_table)) {
2708
fk->fk_ref_table = NULL;
2709
fk->fk_ref_index = UINT_MAX;
2711
/* To avoid deadlock we do not hold more than
2712
* one lock at a time!
2714
freer_(); // xt_unlock_rwlock(&dt_ref_lock);
2716
tab->removeReference(self, fk);
2717
xt_heap_release(self, tab->dt_table); /* We referenced the table, not the index! */
2719
xt_xlock_rwlock(self, &dt_ref_lock);
2720
pushr_(xt_unlock_rwlock, &dt_ref_lock);
2727
dt_trefs = tr->tr_next;
2728
freer_(); // xt_unlock_rwlock(&dt_ref_lock);
2730
xt_xlock_rwlock(self, &dt_ref_lock);
2731
pushr_(xt_unlock_rwlock, &dt_ref_lock);
2734
freer_(); // xt_unlock_rwlock(&dt_ref_lock);
2737
void XTDDTable::checkForeignKeys(XTThreadPtr self, bool temp_table)
2741
if (temp_table && dt_fkeys.size()) {
2742
/* Temporary tables cannot have foreign keys: */
2743
xt_throw_xterr(XT_CONTEXT, XT_ERR_FK_ON_TEMP_TABLE);
2747
/* Search for table referenced by this table. */
2748
for (u_int i=0; i<dt_fkeys.size(); i++) {
2749
fk = dt_fkeys.itemAt(i);
2751
if (fk->fk_on_delete == XT_KEY_ACTION_SET_NULL || fk->fk_on_update == XT_KEY_ACTION_SET_NULL) {
2752
/* Check that all the columns can be set to NULL! */
2755
for (u_int j=0; j<fk->co_cols.size(); j++) {
2756
if ((col = findColumn(fk->co_cols.itemAt(j)->cr_col_name))) {
2757
if (!col->dc_null_ok)
2758
xt_throw_tabcolerr(XT_CONTEXT, XT_ERR_COLUMN_IS_NOT_NULL, fk->fk_ref_tab_name, col->dc_name);
2763
// TODO: dont close table immediately so it can be possibly reused in this loop
2766
pushsr_(ref_tab, xt_heap_release, xt_use_table(self, fk->fk_ref_tab_name, FALSE, TRUE));
2767
if (ref_tab && !fk->checkReferencedTypes(ref_tab->tab_dic.dic_table))
2771
/* Currently I allow foreign keys to be created on tables that do not yet exist!
2772
pushsr_(tab, xt_heap_release, xt_use_table(self, fk->fk_ref_tab_name, FALSE FALSE));
2773
if ((dt = tab->tab_dic.dic_table))
2774
dt->checkForeignKeyReference(self, fk);
2775
freer_(); // xt_heap_release(tab)
2780
XTDDIndex *XTDDTable::findIndex(XTDDConstraint *co)
2782
XTDDIndex *ind = NULL;
2784
u_int index_size = UINT_MAX;
2786
for (u_int i=0; i<dt_indexes.size(); i++) {
2787
cur_ind = dt_indexes.itemAt(i);
2788
u_int sz = cur_ind->getIndexPtr()->mi_key_size;
2789
if (sz < index_size && co->samePrefixColumns(cur_ind)) {
2799
char buffer[XT_ERR_MSG_SIZE - 200];
2800
co->getColumnList(buffer, XT_ERR_MSG_SIZE - 200);
2801
xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_NO_MATCHING_INDEX, buffer);
2806
XTDDIndex *XTDDTable::findReferenceIndex(XTDDForeignKey *fk)
2808
XTDDIndex *ind = NULL;
2812
u_int index_size = UINT_MAX;
2814
for (i=0; i<dt_indexes.size(); i++) {
2815
cur_ind = dt_indexes.itemAt(i);
2816
u_int sz = cur_ind->getIndexPtr()->mi_key_size;
2817
if (sz < index_size && fk->samePrefixReferenceColumns(cur_ind)) {
2826
/* If the index does not exist, maybe the columns do not exist?! */
2827
for (i=0; i<fk->fk_ref_cols.size(); i++) {
2828
cr = fk->fk_ref_cols.itemAt(i);
2829
if (!findColumn(cr->cr_col_name)) {
2830
xt_register_tabcolerr(XT_REG_CONTEXT, XT_ERR_COLUMN_NOT_FOUND, fk->fk_ref_tab_name, cr->cr_col_name);
2836
char buffer[XT_ERR_MSG_SIZE - 200];
2838
fk->getReferenceList(buffer, XT_ERR_MSG_SIZE - 200);
2839
xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_NO_MATCHING_INDEX, buffer);
2844
bool XTDDTable::insertRow(XTOpenTablePtr ot, xtWord1 *rec_ptr)
2847
XTInfoBufferRec rec_buf;
2849
if (ot->ot_thread->st_ignore_fkeys || ot->ot_thread->st_import_stat == XT_IMP_COPY_TABLE)
2851
rec_buf.ib_free = FALSE;
2853
if (!xt_tab_load_record(ot, ot->ot_curr_rec_id, &rec_buf))
2855
rec_ptr = rec_buf.ib_db.db_data;
2858
for (u_int i=0; i<dt_fkeys.size(); i++) {
2859
if (!dt_fkeys.itemAt(i)->insertRow(NULL, rec_ptr, ot->ot_thread)) {
2864
xt_ib_free(NULL, &rec_buf);
2868
bool XTDDTable::checkNoAction(XTOpenTablePtr ot, xtRecordID rec_id)
2872
XTInfoBufferRec rec_buf;
2875
if (ot->ot_thread->st_ignore_fkeys)
2878
rec_buf.ib_free = FALSE;
2879
if (!xt_tab_load_record(ot, rec_id, &rec_buf))
2881
rec_ptr = rec_buf.ib_db.db_data;
2883
xt_slock_rwlock_ns(&dt_ref_lock);
2886
if (!tr->checkReference(rec_ptr, ot->ot_thread)) {
2892
xt_unlock_rwlock_ns(&dt_ref_lock);
2893
xt_ib_free(NULL, &rec_buf);
2897
bool XTDDTable::deleteRow(XTOpenTablePtr ot, xtWord1 *rec_ptr)
2901
XTInfoBufferRec rec_buf;
2903
if (ot->ot_thread->st_ignore_fkeys)
2906
rec_buf.ib_free = FALSE;
2908
if (!xt_tab_load_record(ot, ot->ot_curr_rec_id, &rec_buf))
2910
rec_ptr = rec_buf.ib_db.db_data;
2913
xt_slock_rwlock_ns(&dt_ref_lock);
2916
if (!tr->modifyRow(ot, rec_ptr, NULL, ot->ot_thread)) {
2922
xt_unlock_rwlock_ns(&dt_ref_lock);
2923
xt_ib_free(NULL, &rec_buf);
2927
void XTDDTable::deleteAllRows(XTThreadPtr self)
2931
xt_slock_rwlock(self, &dt_ref_lock);
2932
pushr_(xt_unlock_rwlock, &dt_ref_lock);
2936
tr->deleteAllRows(self);
2940
freer_(); // xt_unlock_rwlock(&dt_ref_lock);
2943
bool XTDDTable::updateRow(XTOpenTablePtr ot, xtWord1 *before, xtWord1 *after)
2947
XTInfoBufferRec before_buf;
2951
if (ot->ot_thread->st_ignore_fkeys)
2954
/* If before is NULL then this is a cascaded
2955
* update. In this case there is no need to check
2956
* if the column has a parent!!
2959
if (dt_fkeys.size() > 0) {
2960
for (u_int i=0; i<dt_fkeys.size(); i++) {
2961
if (!dt_fkeys.itemAt(i)->insertRow(before, after, ot->ot_thread))
2968
before_buf.ib_free = FALSE;
2970
xt_slock_rwlock_ns(&dt_ref_lock);
2971
if ((tr = dt_trefs)) {
2973
if (!xt_tab_load_record(ot, ot->ot_curr_rec_id, &before_buf))
2975
before = before_buf.ib_db.db_data;
2979
if (!tr->modifyRow(ot, before, after, ot->ot_thread)) {
2986
xt_unlock_rwlock_ns(&dt_ref_lock);
2988
xt_ib_free(NULL, &before_buf);
2993
* drop_db parameter is TRUE if we are dropping the schema of this table. In this case
2994
* we return TRUE if the table has only refs to the tables from its own schema
2996
xtBool XTDDTable::checkCanDrop(xtBool drop_db)
2998
/* no refs or references only itself */
2999
if ((dt_trefs == NULL) || ((dt_trefs->tr_next == NULL) && (dt_trefs->tr_fkey->co_table == this)))
3005
const char *this_schema = xt_last_2_names_of_path(dt_table->tab_name->ps_path);
3006
size_t this_schema_sz = xt_last_name_of_path(dt_table->tab_name->ps_path) - this_schema;
3007
XTDDTableRef *tr = dt_trefs;
3010
const char *tab_path = tr->tr_fkey->co_table->dt_table->tab_name->ps_path;
3011
const char *tab_schema = xt_last_2_names_of_path(tab_path);
3012
size_t tab_schema_sz = xt_last_name_of_path(tab_path) - tab_schema;
3014
if (this_schema_sz != tab_schema_sz || strncmp(this_schema, tab_schema, tab_schema_sz))