Drizzled Public API Documentation

foreign_key.cc

00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2008 Sun Microsystems, Inc.
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; version 2 of the License.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  */
00019 
00020 #include <config.h>
00021 
00022 #include <string>
00023 
00024 #include <drizzled/foreign_key.h>
00025 #include <drizzled/error.h>
00026 #include <drizzled/create_field.h>
00027 #include <drizzled/internal/my_sys.h>
00028 #include <drizzled/table_ident.h>
00029 
00030 namespace drizzled
00031 {
00032 
00033 extern const CHARSET_INFO *system_charset_info;
00034 
00035 void add_foreign_key_to_table_message(
00036     message::Table *table_message,
00037     const char* fkey_name,
00038     List<Key_part_spec> &cols,
00039     Table_ident *table,
00040     List<Key_part_spec> &ref_cols,
00041     message::Table::ForeignKeyConstraint::ForeignKeyOption delete_opt_arg,
00042     message::Table::ForeignKeyConstraint::ForeignKeyOption update_opt_arg,
00043     message::Table::ForeignKeyConstraint::ForeignKeyMatchOption match_opt_arg)
00044 {
00045   message::Table::ForeignKeyConstraint *pfkey= table_message->add_fk_constraint();
00046   if (fkey_name)
00047     pfkey->set_name(fkey_name);
00048   else if (table_message->has_name())
00049   {
00050     std::string name(table_message->name());
00051     char number[20];
00052 
00053     name.append("_ibfk_");
00054     snprintf(number, sizeof(number), "%d", table_message->fk_constraint_size());
00055     name.append(number);
00056 
00057     pfkey->set_name(name);
00058   }
00059 
00060   pfkey->set_match(match_opt_arg);
00061   pfkey->set_update_option(update_opt_arg);
00062   pfkey->set_delete_option(delete_opt_arg);
00063 
00064   pfkey->set_references_table_name(table->table.str);
00065 
00066   Key_part_spec *keypart;
00067   List<Key_part_spec>::iterator col_it(cols.begin());
00068   while ((keypart= col_it++))
00069   {
00070     pfkey->add_column_names(keypart->field_name.str);
00071   }
00072 
00073   List<Key_part_spec>::iterator ref_it(ref_cols.begin());
00074   while ((keypart= ref_it++))
00075   {
00076     pfkey->add_references_columns(keypart->field_name.str);
00077   }
00078 
00079 }
00080 
00096 template <typename T>
00097 void list_copy_and_replace_each_value(List<T> &list, memory::Root *mem_root)
00098 {
00099   /* Make a deep copy of each element */
00100   typename List<T>::iterator it(list.begin());
00101   T *el;
00102   while ((el= it++))
00103     it.replace(el->clone(mem_root));
00104 }
00105 
00106 Foreign_key::Foreign_key(const Foreign_key &rhs, memory::Root *mem_root)
00107   :Key(rhs),
00108   ref_table(rhs.ref_table),
00109   ref_columns(rhs.ref_columns),
00110   delete_opt(rhs.delete_opt),
00111   update_opt(rhs.update_opt),
00112   match_opt(rhs.match_opt)
00113 {
00114   list_copy_and_replace_each_value(ref_columns, mem_root);
00115 }
00116 
00117 /*
00118   Test if a foreign key (= generated key) is a prefix of the given key
00119   (ignoring key name, key type and order of columns)
00120 
00121   NOTES:
00122     This is only used to test if an index for a FOREIGN KEY exists
00123 
00124   IMPLEMENTATION
00125     We only compare field names
00126 
00127   RETURN
00128     0 Generated key is a prefix of other key
00129     1 Not equal
00130 */
00131 bool foreign_key_prefix(Key *a, Key *b)
00132 {
00133   /* Ensure that 'a' is the generated key */
00134   if (a->generated)
00135   {
00136     if (b->generated && a->columns.size() > b->columns.size())
00137       std::swap(a, b);                       // Put shorter key in 'a'
00138   }
00139   else
00140   {
00141     if (!b->generated)
00142       return true;                              // No foreign key
00143     std::swap(a, b);                       // Put generated key in 'a'
00144   }
00145 
00146   /* Test if 'a' is a prefix of 'b' */
00147   if (a->columns.size() > b->columns.size())
00148     return true;                                // Can't be prefix
00149 
00150   List<Key_part_spec>::iterator col_it1(a->columns.begin());
00151   List<Key_part_spec>::iterator col_it2(b->columns.begin());
00152   const Key_part_spec *col1, *col2;
00153 
00154 #ifdef ENABLE_WHEN_INNODB_CAN_HANDLE_SWAPED_FOREIGN_KEY_COLUMNS
00155   while ((col1= col_it1++))
00156   {
00157     bool found= 0;
00158     col_it2=b->columns.begin();
00159     while ((col2= col_it2++))
00160     {
00161       if (*col1 == *col2)
00162       {
00163         found= true;
00164   break;
00165       }
00166     }
00167     if (!found)
00168       return true;                              // Error
00169   }
00170   return false;                                 // Is prefix
00171 #else
00172   while ((col1= col_it1++))
00173   {
00174     col2= col_it2++;
00175     if (!(*col1 == *col2))
00176       return true;
00177   }
00178   return false;                                 // Is prefix
00179 #endif
00180 }
00181 
00182 /*
00183   Check if the foreign key options are compatible with columns
00184   on which the FK is created.
00185 
00186   RETURN
00187     0   Key valid
00188     1   Key invalid
00189 */
00190 bool Foreign_key::validate(List<CreateField> &table_fields)
00191 {
00192   CreateField  *sql_field;
00193   Key_part_spec *column;
00194   List<Key_part_spec>::iterator cols(columns.begin());
00195   List<CreateField>::iterator it(table_fields.begin());
00196   while ((column= cols++))
00197   {
00198     it= table_fields.begin();
00199     while ((sql_field= it++) &&
00200            my_strcasecmp(system_charset_info,
00201                          column->field_name.str,
00202                          sql_field->field_name)) {}
00203     if (!sql_field)
00204     {
00205       my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str);
00206       return true;
00207     }
00208   }
00209   return false;
00210 }
00211 
00212 } /* namespace drizzled */