~drizzle-trunk/drizzle/development

934.2.20 by Jay Pipes
Move Foreign_key implementation into its own implementation file and out of session.cc
1
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
 *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3
 *
1999.6.1 by kalebral at gmail
update Copyright strings to a more common format to help with creating the master debian copyright file
4
 *  Copyright (C) 2008 Sun Microsystems, Inc.
934.2.20 by Jay Pipes
Move Foreign_key implementation into its own implementation file and out of session.cc
5
 *
6
 *  This program is free software; you can redistribute it and/or modify
7
 *  it under the terms of the GNU General Public License as published by
8
 *  the Free Software Foundation; version 2 of the License.
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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
 */
19
2173.2.1 by Monty Taylor
Fixes incorrect usage of include
20
#include <config.h>
1241.9.1 by Monty Taylor
Removed global.h. Fixed all the headers.
21
22
#include <string>
23
2173.2.1 by Monty Taylor
Fixes incorrect usage of include
24
#include <drizzled/foreign_key.h>
25
#include <drizzled/error.h>
26
#include <drizzled/create_field.h>
27
#include <drizzled/internal/my_sys.h>
28
#include <drizzled/table_ident.h>
934.2.20 by Jay Pipes
Move Foreign_key implementation into its own implementation file and out of session.cc
29
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
30
namespace drizzled
31
{
1253.1.3 by Monty Taylor
MEM_ROOT == memory::Root
32
2254 by Brian Aker
Shift CHARSET_INFO to charset_info_st
33
extern const charset_info_st *system_charset_info;
1241.9.1 by Monty Taylor
Removed global.h. Fixed all the headers.
34
1638.10.78 by Stewart Smith
separate out adding the foreign key to the table protobuf message from mysql_prepare_create_table and call a function to do it directly from the parser where we set up the foreign key structures
35
void add_foreign_key_to_table_message(
36
    message::Table *table_message,
37
    const char* fkey_name,
38
    List<Key_part_spec> &cols,
39
    Table_ident *table,
40
    List<Key_part_spec> &ref_cols,
41
    message::Table::ForeignKeyConstraint::ForeignKeyOption delete_opt_arg,
42
    message::Table::ForeignKeyConstraint::ForeignKeyOption update_opt_arg,
43
    message::Table::ForeignKeyConstraint::ForeignKeyMatchOption match_opt_arg)
44
{
45
  message::Table::ForeignKeyConstraint *pfkey= table_message->add_fk_constraint();
46
  if (fkey_name)
47
    pfkey->set_name(fkey_name);
1638.10.80 by Stewart Smith
fix storing and manipulating foreign keys in the proto around ALTER TABLE, CREATE TABLE and ALTER TABLE ADD/DROP FOREIGN KEY. We also (mostly) emulate the naming of innodb foreign keys in the upper layer.
48
  else if (table_message->has_name())
49
  {
50
    std::string name(table_message->name());
51
    char number[20];
52
53
    name.append("_ibfk_");
54
    snprintf(number, sizeof(number), "%d", table_message->fk_constraint_size());
55
    name.append(number);
56
57
    pfkey->set_name(name);
58
  }
1638.10.78 by Stewart Smith
separate out adding the foreign key to the table protobuf message from mysql_prepare_create_table and call a function to do it directly from the parser where we set up the foreign key structures
59
60
  pfkey->set_match(match_opt_arg);
61
  pfkey->set_update_option(update_opt_arg);
62
  pfkey->set_delete_option(delete_opt_arg);
63
64
  pfkey->set_references_table_name(table->table.str);
65
66
  Key_part_spec *keypart;
2183.2.10 by Olaf van der Spek
Use List::begin()
67
  List<Key_part_spec>::iterator col_it(cols.begin());
1638.10.78 by Stewart Smith
separate out adding the foreign key to the table protobuf message from mysql_prepare_create_table and call a function to do it directly from the parser where we set up the foreign key structures
68
  while ((keypart= col_it++))
69
  {
70
    pfkey->add_column_names(keypart->field_name.str);
71
  }
72
2183.2.10 by Olaf van der Spek
Use List::begin()
73
  List<Key_part_spec>::iterator ref_it(ref_cols.begin());
1638.10.78 by Stewart Smith
separate out adding the foreign key to the table protobuf message from mysql_prepare_create_table and call a function to do it directly from the parser where we set up the foreign key structures
74
  while ((keypart= ref_it++))
75
  {
76
    pfkey->add_references_columns(keypart->field_name.str);
77
  }
78
79
}
80
2201.1.2 by Olaf van der Spek
Fix EOL
81
/**
82
  Make a deep copy of each list element.
83
84
  @note A template function and not a template method of class List
85
  is employed because of explicit template instantiation:
86
  in server code there are explicit instantiations of List<T> and
87
  an explicit instantiation of a template requires that any method
88
  of the instantiated class used in the template can be resolved.
89
  Evidently not all template arguments have clone() method with
90
  the right signature.
91
92
  @return You must query the error state in Session for out-of-memory
93
  situation after calling this function.
94
*/
95
96
template <typename T>
97
void list_copy_and_replace_each_value(List<T> &list, memory::Root *mem_root)
98
{
99
  /* Make a deep copy of each element */
100
  typename List<T>::iterator it(list.begin());
2318.6.63 by Olaf van der Spek
Refactor
101
  while (T* el= it++)
2201.1.2 by Olaf van der Spek
Fix EOL
102
    it.replace(el->clone(mem_root));
103
}
2183.2.24 by Olaf van der Spek
Remove inline keyword
104
1253.1.3 by Monty Taylor
MEM_ROOT == memory::Root
105
Foreign_key::Foreign_key(const Foreign_key &rhs, memory::Root *mem_root)
934.2.20 by Jay Pipes
Move Foreign_key implementation into its own implementation file and out of session.cc
106
  :Key(rhs),
107
  ref_table(rhs.ref_table),
108
  ref_columns(rhs.ref_columns),
109
  delete_opt(rhs.delete_opt),
110
  update_opt(rhs.update_opt),
111
  match_opt(rhs.match_opt)
112
{
113
  list_copy_and_replace_each_value(ref_columns, mem_root);
114
}
115
116
/*
117
  Test if a foreign key (= generated key) is a prefix of the given key
118
  (ignoring key name, key type and order of columns)
119
120
  NOTES:
121
    This is only used to test if an index for a FOREIGN KEY exists
122
123
  IMPLEMENTATION
124
    We only compare field names
125
126
  RETURN
127
    0	Generated key is a prefix of other key
128
    1	Not equal
129
*/
130
bool foreign_key_prefix(Key *a, Key *b)
131
{
132
  /* Ensure that 'a' is the generated key */
133
  if (a->generated)
134
  {
2183.2.17 by Olaf van der Spek
Use List::size()
135
    if (b->generated && a->columns.size() > b->columns.size())
934.2.20 by Jay Pipes
Move Foreign_key implementation into its own implementation file and out of session.cc
136
      std::swap(a, b);                       // Put shorter key in 'a'
137
  }
138
  else
139
  {
140
    if (!b->generated)
141
      return true;                              // No foreign key
142
    std::swap(a, b);                       // Put generated key in 'a'
143
  }
144
145
  /* Test if 'a' is a prefix of 'b' */
2183.2.17 by Olaf van der Spek
Use List::size()
146
  if (a->columns.size() > b->columns.size())
934.2.20 by Jay Pipes
Move Foreign_key implementation into its own implementation file and out of session.cc
147
    return true;                                // Can't be prefix
148
2183.2.10 by Olaf van der Spek
Use List::begin()
149
  List<Key_part_spec>::iterator col_it1(a->columns.begin());
150
  List<Key_part_spec>::iterator col_it2(b->columns.begin());
934.2.20 by Jay Pipes
Move Foreign_key implementation into its own implementation file and out of session.cc
151
  const Key_part_spec *col1, *col2;
152
153
#ifdef ENABLE_WHEN_INNODB_CAN_HANDLE_SWAPED_FOREIGN_KEY_COLUMNS
154
  while ((col1= col_it1++))
155
  {
156
    bool found= 0;
2179.1.6 by Olaf van der Spek
x
157
    col_it2=b->columns.begin();
934.2.20 by Jay Pipes
Move Foreign_key implementation into its own implementation file and out of session.cc
158
    while ((col2= col_it2++))
159
    {
160
      if (*col1 == *col2)
161
      {
162
        found= true;
163
	break;
164
      }
165
    }
166
    if (!found)
167
      return true;                              // Error
168
  }
169
  return false;                                 // Is prefix
170
#else
171
  while ((col1= col_it1++))
172
  {
173
    col2= col_it2++;
174
    if (!(*col1 == *col2))
175
      return true;
176
  }
177
  return false;                                 // Is prefix
178
#endif
179
}
180
181
/*
182
  Check if the foreign key options are compatible with columns
183
  on which the FK is created.
184
185
  RETURN
186
    0   Key valid
187
    1   Key invalid
188
*/
1052.2.3 by Nathan Williams
No actual code changes. Changed Create_field to CreateField to be consistent with coding standards.
189
bool Foreign_key::validate(List<CreateField> &table_fields)
934.2.20 by Jay Pipes
Move Foreign_key implementation into its own implementation file and out of session.cc
190
{
1052.2.3 by Nathan Williams
No actual code changes. Changed Create_field to CreateField to be consistent with coding standards.
191
  CreateField  *sql_field;
934.2.20 by Jay Pipes
Move Foreign_key implementation into its own implementation file and out of session.cc
192
  Key_part_spec *column;
2183.2.10 by Olaf van der Spek
Use List::begin()
193
  List<Key_part_spec>::iterator cols(columns.begin());
194
  List<CreateField>::iterator it(table_fields.begin());
934.2.20 by Jay Pipes
Move Foreign_key implementation into its own implementation file and out of session.cc
195
  while ((column= cols++))
196
  {
2183.2.1 by Olaf van der Spek
x
197
    it= table_fields.begin();
934.2.20 by Jay Pipes
Move Foreign_key implementation into its own implementation file and out of session.cc
198
    while ((sql_field= it++) &&
199
           my_strcasecmp(system_charset_info,
200
                         column->field_name.str,
201
                         sql_field->field_name)) {}
202
    if (!sql_field)
203
    {
204
      my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str);
205
      return true;
206
    }
207
  }
208
  return false;
209
}
210
1280.1.10 by Monty Taylor
Put everything in drizzled into drizzled namespace.
211
} /* namespace drizzled */