/VisualStudio/DocumentView/Documents/TableDocument.cs
C# | 3833 lines | 2280 code | 469 blank | 1084 comment | 498 complexity | 551511a65d50c195c0939af085b2b403 MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, MPL-2.0-no-copyleft-exception
Large files files are truncated, but you can click here to view the full file
- // Copyright (C) 2006-2007 MySQL AB
- //
- // This file is part of MySQL Tools for Visual Studio.
- // MySQL Tools for Visual Studio is free software; you can redistribute it
- // and/or modify it under the terms of the GNU Lesser General Public
- // License version 2.1 as published by the Free Software Foundation
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with this program; if not, write to the Free Software
- // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System;
-
- /*
- * This file contains implementation of data object for TABLE representation.
- */
- using System;
- using System.ComponentModel;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Text;
- using System.Data;
- using Microsoft.VisualStudio.Data;
- using Microsoft.VisualStudio.Shell;
- using Microsoft.VisualStudio.Shell.Interop;
- using Microsoft.VisualStudio;
- using MySql.Data.VisualStudio.Utils;
- using System.Windows.Forms.Design;
- using System.Data.SqlTypes;
- using MySql.Data.VisualStudio.Descriptors;
- using Table = MySql.Data.VisualStudio.Descriptors.TableDescriptor.Attributes;
- using Column = MySql.Data.VisualStudio.Descriptors.ColumnDescriptor.Attributes;
- using ForeignKey = MySql.Data.VisualStudio.Descriptors.ForeignKeyDescriptor.Attributes;
- using ForeignKeyColumn = MySql.Data.VisualStudio.Descriptors.ForeignKeyColumnDescriptor.Attributes;
- using Index = MySql.Data.VisualStudio.Descriptors.IndexDescriptor.Attributes;
- using IndexColumn = MySql.Data.VisualStudio.Descriptors.IndexColumnDescriptor.Attributes;
- using System.Globalization;
- using MySql.Data.VisualStudio.Properties;
- using System.Drawing.Design;
- using System.Windows.Forms;
-
- namespace MySql.Data.VisualStudio.DocumentView
- {
- /// <summary>
- /// This class implements document (data object) functionality and
- /// represent database table. It implements IVsPersistDocData interface and
- /// beaves like filelles data object for Visual Studio enviroment.
- /// </summary>
- [DocumentObject(TableDescriptor.TypeName, typeof(TableDocument))]
- public class TableDocument : BaseDocument, CollationConverter.ICharacterSetProvider
- {
- #region Initialization
- /// <summary>
- /// This constructor initialize private identifier variables.
- /// </summary>
- /// <param name="hierarchy">
- /// Data view hierarchy accessor used to interact with Server Explorer.
- /// Also used to extract connection.
- /// </param>
- /// <param name="id">
- /// Array with the object identifier.
- /// </param>
- /// <param name="isNew">
- /// Indicates if this instance represents new database object doesn’t fixed in
- /// database yet.
- /// </param>
- public TableDocument(ServerExplorerFacade hierarchy, bool isNew, object[] id)
- :base(hierarchy, isNew, id)
- {
- }
- #endregion
-
- #region BaseDocument overridings
- /// <summary>
- /// Dirty flag used to determine whenever data are changed or not. At this
- /// point table data considered as changed if Columns data table has changes.
- /// </summary>
- protected override bool IsDirty
- {
- get
- {
- // Asks base class
- if (base.IsDirty)
- return true;
-
- Debug.Assert(Columns != null, "Collumns are not read!");
- Debug.Assert(ForeignKeys != null, "Foreign keys are not read!");
- Debug.Assert(ForeignKeysColumns != null, "Foreign keys columns are not read!");
- Debug.Assert(Indexes != null, "Indexes are not read!");
- Debug.Assert(IndexColumns != null, "IndexColumns columns are not read!");
-
- // If we aren't read return true
- if (Columns == null || ForeignKeys == null || ForeignKeysColumns == null
- || Indexes == null || IndexColumns == null)
- return false;
-
- // Check column list and other tables
- if (DataInterpreter.HasChanged(Columns)
- || DataInterpreter.HasChanged(ForeignKeys)
- || DataInterpreter.HasChanged(ForeignKeysColumns)
- || DataInterpreter.HasChanged(Indexes)
- || DataInterpreter.HasChanged(IndexColumns))
- return true; ;
- return false;
- }
- }
-
- /// <summary>
- /// Returns query for pre-dropping foreign keys and indexes.
- /// </summary>
- /// <returns>Returns query for pre-dropping foreign keys and indexes.</returns>
- protected override string BuildPreDropQuery()
- {
- // Pre-drop foreign keys and indexes if needed
- if (NeedToDropForeignKeys())
- return BuildPreDropForeignKeys();
-
- return String.Empty;
- }
-
- /// <summary>
- /// Builds alter query for table.
- /// </summary>
- /// <returns>Alter query for table.</returns>
- protected override string BuildAlterQuery()
- {
- StringBuilder query = new StringBuilder();
-
- // Write header
- BuildAlterHeader(query);
-
- BuildAlterSpecifications(query);
-
- return query.ToString();
- }
-
- /// <summary>
- /// Builda create query for table.
- /// </summary>
- /// <returns>Create query for table.</returns>
- protected override string BuildCreateQuery()
- {
- StringBuilder query = new StringBuilder();
- BuildCreateHeader(query);
- query.Append(" (");
- BuildCreateDefinition(query);
- query.Append(" )");
- BuildTableOptions(query, null);
- return query.ToString();
- }
-
- /// <summary>
- /// Load database object from database.
- /// </summary>
- /// <param name="reloading">
- /// This flag indicates that object is reloading. Should be ignored in most cases.
- /// </param>
- /// <returns>Returns true if load succeeds and false otherwise.</returns>
- protected override bool LoadData(bool reloading)
- {
- // Read table atributes using base method
- if (!base.LoadData(reloading))
- return false;
-
- // Reset current columns table, if any.
- if (columnsTable != null)
- ResetColumnsTable();
-
- // Reset foreign keys table, if any
- if (foreignKeysTable != null)
- ResetForeignKeysTable();
-
- // Reset foreign keys columns table, if any
- if (foreignKeysColumnsTable != null)
- ResetForeignKeysColumnsTable();
-
- // Reset indexes table, if any
- if (indexesTable != null)
- ResetIndexesTable();
-
- // Reset indexes columns table, if any
- if (indexesTable != null)
- ResetIndexesColumnsTable();
-
- // Read columns for table
- columnsTable = ColumnDescriptor.Enumerate(Connection, ObjectIDForLoad);
- if (columnsTable == null)
- {
- Debug.Fail("Failed to read columns!");
- return false;
- }
- // Subscribe to new columns table events
- SubscribeToColumnsTableEvents();
-
- // Read foreign keys for table.
- foreignKeysTable = ForeignKeyDescriptor.Enumerate(Connection, ObjectIDForLoad);
- if (foreignKeysTable == null)
- {
- Debug.Fail("Failed to read foreign keys!");
- return false;
- }
- // Subscrube to events
- SubscribeToForeignKeysTableEvents();
-
- // Read foreign keys columns for table.
- foreignKeysColumnsTable = ForeignKeyColumnDescriptor.Enumerate(Connection, ObjectIDForLoad);
- if (foreignKeysColumnsTable == null)
- {
- Debug.Fail("Failed to read foreign keys columns!");
- return false;
- }
- // Subscrube to events
- SubscribeToForeignKeysColumnsTableEvents();
-
- // Read indexes for table.
- indexesTable = IndexDescriptor.Enumerate(Connection, ObjectIDForLoad);
- if (indexesTable == null)
- {
- Debug.Fail("Failed to read indexes!");
- return false;
- }
- // Subscrube to events
- SubscribeToIndexesTableEvents();
-
- // Read index columns for table.
- indexColumnsTable = IndexColumnDescriptor.Enumerate(Connection, ObjectIDForLoad);
- if (indexColumnsTable == null)
- {
- Debug.Fail("Failed to read index columns!");
- return false;
- }
- // Subscrube to events
- SubscribeToIndexesColumnsTableEvents();
-
- return true;
- }
-
- /// <summary>
- /// Accepts changes in column grid.
- /// </summary>
- protected override void AcceptChanges()
- {
- base.AcceptChanges();
-
- // Accepting columns changes
- columnsTable.AcceptChanges();
- }
-
- /// <summary>
- /// Fills aditional properties for new table.
- /// </summary>
- /// <param name="newRow">DataRow to fill with properties.</param>
- protected override void FillNewObjectAttributes(DataRow newRow)
- {
- base.FillNewObjectAttributes(newRow);
- newRow[Table.Engine] = Connection.DefaultEngine;
- newRow[Table.CharacterSet] = Connection.DefaultCharacterSet;
- newRow[Table.Collation] = Connection.DefaultCollation;
- }
-
- /// <summary>
- /// Resets data for the cloned table.
- /// </summary>
- protected override void ResetToNew()
- {
- // Call to base
- base.ResetToNew();
-
- // Validate columns and other data (must be loaded)
- if (Columns == null || ForeignKeys == null || ForeignKeysColumns == null
- || Indexes == null || IndexColumns == null)
- return;
-
- //Reset columns
- DataTable temp = MakeCopy(Columns);
- ResetTableName(temp, Column.Table);
- ResetColumnsTable();
- columnsTable = temp;
- SubscribeToColumnsTableEvents();
-
- //Reset foreign keys
- temp = MakeCopy(ForeignKeys);
- ResetTableName(temp, ForeignKey.Table);
- // For each foreign key we need new name (some times InnoDB falls if keys have same name
- // even in the different tables)
- foreach (DataRow key in temp.Rows)
- GenerateNewName(key);
- ResetForeignKeysTable();
- foreignKeysTable = temp;
- SubscribeToForeignKeysTableEvents();
-
- //Reset foreign key columns
- temp = MakeCopy(ForeignKeysColumns);
- ResetTableName(temp, ForeignKeyColumn.Table);
- ResetForeignKeysColumnsTable();
- foreignKeysColumnsTable = temp;
- SubscribeToForeignKeysColumnsTableEvents();
-
- //Reset indexes
- temp = MakeCopy(Indexes);
- ResetTableName(temp, Index.Table);
- ResetIndexesTable();
- indexesTable = temp;
- SubscribeToIndexesTableEvents();
-
- //Reset indexes columns
- temp = MakeCopy(IndexColumns);
- ResetTableName(temp, IndexColumn.Table);
- ResetIndexesColumnsTable();
- indexColumnsTable = temp;
- SubscribeToIndexesColumnsTableEvents();
- }
-
- /// <summary>
- /// Processes failures on save. May remove pre-dropped foreign keys and indexes
- /// from tables.
- /// </summary>
- protected override void SaveFailed()
- {
- base.SaveFailed();
-
- // If query was with two parts and at first part we droped foreign keys
- // we must check all keys and indexes for existens
- if (!NeedToDropForeignKeys())
- {
- // Call to base
- CallBaseSaveFailed();
- return;
- }
-
- // Flag to indicate that we droped somthing
- bool dropped = false;
-
- // Enumerate current foreign keys
- DataTable currentKeys = ForeignKeyDescriptor.Enumerate(Connection, OldObjectID);
- Debug.Assert(currentKeys != null && currentKeys.Rows != null, "Failed to re-enumerate foreign keys");
- if (currentKeys != null && currentKeys.Rows != null)
- {
- foreach (DataRow key in ForeignKeys.Select())
- {
- switch (key.RowState)
- {
- // Skip added and deleted keys
- case DataRowState.Added:
- case DataRowState.Deleted:
- break;
- // Check if this key was changed and pre-droped
- default:
- if (HasForeignKeyChanged(key))
- {
- DataRow currentKey = currentKeys.Rows.Find(new object[] {
- Schema,
- OldName,
- DataInterpreter.GetString(key, ForeignKey.Name) });
- // If no current key founded, mark it as new
- if (currentKey == null)
- {
- dropped = true;
- key.AcceptChanges();
- key.SetAdded();
- }
- }
- break;
- }
- }
- }
-
- // Enumerate current indexes
- DataTable currentIndexes = IndexDescriptor.Enumerate(Connection, OldObjectID);
- Debug.Assert(currentIndexes != null && currentIndexes.Rows != null, "Failed to re-enumerate indexes");
- if (currentIndexes != null && currentIndexes.Rows != null)
- {
- foreach (DataRow index in Indexes.Select())
- {
- switch (index.RowState)
- {
- // Skip added and deleted indexes
- case DataRowState.Added:
- case DataRowState.Deleted:
- break;
- // Check if this index was changed and pre-droped
- default:
- DataRow currentIndex = currentIndexes.Rows.Find(new object[] {
- Schema,
- OldName,
- DataInterpreter.GetString(index, Index.Name) });
- // If no current index founded, mark it as new
- if (currentIndex == null)
- {
- dropped = true;
- index.AcceptChanges();
- index.SetAdded();
- }
- break;
-
- }
- }
- }
-
- // Call to base
- CallBaseSaveFailed();
-
- // Warn user if key or index was pre-droped
- if (dropped)
- UIHelper.ShowWarning(Resources.Warning_KeyWasPredropped);
- }
- #endregion
-
- #region Index changes handling
- /// <summary>
- /// Unsubscribe from old indexes table event
- /// </summary>
- private void ResetIndexesTable()
- {
- indexesTable.TableNewRow -= new DataTableNewRowEventHandler(OnNewIndex);
- indexesTable.RowChanged -= new DataRowChangeEventHandler(OnIndexRowChanged);
- indexesTable.RowDeleted -= new DataRowChangeEventHandler(OnIndexRowDeleted);
- indexesTable.Dispose();
- indexesTable = null;
- }
-
- /// <summary>
- /// Subscribe to new index table events
- /// </summary>
- private void SubscribeToIndexesTableEvents()
- {
- indexesTable.TableNewRow += new DataTableNewRowEventHandler(OnNewIndex);
- indexesTable.RowChanged += new DataRowChangeEventHandler(OnIndexRowChanged);
- indexesTable.RowDeleted += new DataRowChangeEventHandler(OnIndexRowDeleted);
- }
-
- /// <summary>
- /// Handles chnages for the index rows.
- /// </summary>
- /// <param name="sender">Event sender, unused.</param>
- /// <param name="e">Detailed information about event</param>>
- void OnIndexRowChanged(object sender, DataRowChangeEventArgs e)
- {
- Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
-
- // Extract changed index
- DataRow index = e.Row;
- if (index == null)
- return;
-
- // Chose right method for action
- switch (e.Action)
- {
- case DataRowAction.Change:
- HandleIndexChanges(index);
- break;
- default: break;
- }
- }
-
- /// <summary>
- /// Handles adding new index.
- /// </summary>
- /// <param name="sender">Event sender, unused.</param>
- /// <param name="e">Detailed information about event.</param>
- void OnNewIndex(object sender, DataTableNewRowEventArgs e)
- {
- Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
-
- // Extract added index
- DataRow newIndex = e.Row;
- if (newIndex == null)
- return;
-
- // Initialize new column attributes
- newIndex[Index.Schema] = Schema;
- newIndex[Index.Table] = OldName; // Old name is used to keep table name for all indexes the same.
- newIndex[Index.Name] = BuildNewIndexName();
- newIndex[Index.IndexKind] = IndexDescriptor.INDEX;
- newIndex[Index.IndexType] = IndexDescriptor.BTREE;
- }
-
- /// <summary>
- /// If index changed, ensures that all index columns will have right index name.
- /// </summary>
- /// <param name="index">DataRow with index data.</param>
- private void HandleIndexChanges(DataRow index)
- {
- string newName = DataInterpreter.GetStringNotNull(index, Index.Name);
-
- // Iterate through index columns
- string indexName;
- foreach (DataRow column in IndexColumns.Rows)
- {
- // Need to skip deleted columns if any
- if (column.RowState == DataRowState.Deleted)
- continue;
-
- // Extract index name
- indexName = DataInterpreter.GetStringNotNull(column, IndexColumn.Index);
-
- // Check if any index with this name (search for index with given schema, table and name)
- DataRow candidate = FindIndex(indexName);
-
- // If index was not found, need to change index name for index column.
- if (candidate == null)
- column[IndexColumn.Index] = newName;
- }
- }
-
- /// <summary>
- /// Handles indeex deletion and delete all index columns.
- /// </summary>
- /// <param name="sender">Event sender, unused.</param>
- /// <param name="e">Detailed information about event</param>>
- private void OnIndexRowDeleted(object sender, DataRowChangeEventArgs e)
- {
- string indexName;
- foreach (DataRow column in IndexColumns.Select())
- {
- // Need to skip deleted columns if any
- if (column.RowState == DataRowState.Deleted)
- continue;
-
- // Extract index name
- indexName = DataInterpreter.GetStringNotNull(column, IndexColumn.Index);
-
- // Check if any index with this name (search for index with given schema, table and name)
- DataRow candidate = FindIndex(indexName);
-
- // If index was not found, need to delete index column.
- if (candidate == null)
- column.Delete();
- }
- }
-
- /// <summary>
- /// Builds name for the new new index in format Index_N.
- /// </summary>
- /// <returns>Returns name for the new new index in format Index_N.</returns>
- private string BuildNewIndexName()
- {
- // Initialize search data
- int count = 0; string result; DataRow[] existsIndexes = null;
-
- // Generate new index name
- do
- {
- result = String.Format(CultureInfo.CurrentCulture, Resources.New_Index_Template, ++count);
- existsIndexes = DataInterpreter.Select(Indexes, Index.Name, result);
- }
- while (existsIndexes == null || existsIndexes.Length > 0);
-
- // Return results
- return result;
- }
- #endregion
-
- #region Indexes columns changes handling
- /// <summary>
- /// Unsubscribe from old indexes columns table event
- /// </summary>
- private void ResetIndexesColumnsTable()
- {
- indexColumnsTable.TableNewRow -= new DataTableNewRowEventHandler(OnNewIndexColumn);
- indexColumnsTable.RowChanged -= new DataRowChangeEventHandler(OnIndexColumnChanged);
- indexColumnsTable.RowDeleted -= new DataRowChangeEventHandler(OnIndexColumnChanged);
- indexColumnsTable.Dispose();
- indexColumnsTable = null;
- }
-
- /// <summary>
- /// Subscribe to new indexes columns table events
- /// </summary>
- private void SubscribeToIndexesColumnsTableEvents()
- {
- indexColumnsTable.TableNewRow += new DataTableNewRowEventHandler(OnNewIndexColumn);
- indexColumnsTable.RowChanged += new DataRowChangeEventHandler(OnIndexColumnChanged);
- indexColumnsTable.RowDeleted += new DataRowChangeEventHandler(OnIndexColumnChanged);
- }
-
- /// <summary>
- /// Handles adding new indes collumn.
- /// </summary>
- /// <param name="sender">Event sender, unused.</param>
- /// <param name="e">Detailed information about event.</param>
- void OnNewIndexColumn(object sender, DataTableNewRowEventArgs e)
- {
- Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
-
- // Extract added index column
- DataRow newColumn = e.Row;
- if (newColumn == null)
- return;
-
- // Initialize new column attributes
- newColumn[IndexColumn.Schema] = Schema;
- newColumn[IndexColumn.Table] = OldName; // Old name is used to keep table name for all columns the same.
-
- // Set index name to empty string
- newColumn[IndexColumn.Index] = String.Empty;
-
- // Search for any not deleted column and set name for index column
- DataRow dataColumn = DataInterpreter.GetNotDeletedRow(Columns);
- newColumn[IndexColumn.Name] = dataColumn != null
- ? DataInterpreter.GetStringNotNull(dataColumn, Column.Name)
- : String.Empty;
-
- }
-
- /// <summary>
- /// Handles index column changes and deletions. Adjust table columns primary key status.
- /// </summary>
- /// <param name="sender">Event sender, unused.</param>
- /// <param name="e">Detailed information about event.</param>
- void OnIndexColumnChanged(object sender, DataRowChangeEventArgs e)
- {
- Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
-
- // Extract added column
- DataRow column = e.Row;
- if (column == null)
- return;
-
- // Chose right method for action
- switch (e.Action)
- {
- case DataRowAction.Add:
- case DataRowAction.Change:
- HandleIndexColumnChanges(column);
- break;
- case DataRowAction.Delete:
- HandleIndexColumnDeleted();
- break;
- default: break;
- }
- }
-
- /// <summary>
- /// Then primary key index column is deleted, ensures that table column will be unmarked
- /// as primary key.
- /// </summary>
- private void HandleIndexColumnDeleted()
- {
- // Check each primary key column
- foreach (DataRow column in DataInterpreter.Select(Columns, Column.IsPrimaryKey, DataInterpreter.True))
- {
- // Skip deleted columns
- if (column.RowState == DataRowState.Deleted)
- continue;
-
- // Ensure that index column stil exists
- DataRow indexColumn = FindIndexColumn(IndexDescriptor.PRIMARY, DataInterpreter.GetStringNotNull(column, Column.Name));
- if(indexColumn == null)
- DataInterpreter.SetValueIfChanged(column, Column.IsPrimaryKey, DataInterpreter.False);
- }
- }
-
- /// <summary>
- /// Handles index column changes and mark/unmark primary key columns.
- /// </summary>
- /// <param name="column">DataRow with index column data.</param>
- private void HandleIndexColumnChanges(DataRow column)
- {
- // Extract index name
- string indexName = DataInterpreter.GetStringNotNull(column, IndexColumn.Index);
- // Extract related table column
- DataRow tableColumn = FindColumn(DataInterpreter.GetStringNotNull(column, IndexColumn.Name));
- if (tableColumn == null)
- return;
-
- // If index is primary key, mark column as primary key
- if (DataInterpreter.CompareInvariant(indexName, IndexDescriptor.PRIMARY))
- DataInterpreter.SetValueIfChanged(tableColumn, Column.IsPrimaryKey, DataInterpreter.True);
- }
- #endregion
-
- #region Foreign keys changes handling
- /// <summary>
- /// Unsubscribe from old foreign keys columns table event
- /// </summary>
- private void ResetForeignKeysTable()
- {
- foreignKeysTable.TableNewRow -= new DataTableNewRowEventHandler(OnNewForeignKey);
- foreignKeysTable.RowChanged -= new DataRowChangeEventHandler(OnForeignKeyRowChanged);
- foreignKeysTable.RowDeleted -= new DataRowChangeEventHandler(OnForeignKeyRowDeleted);
- foreignKeysTable.Dispose();
- foreignKeysTable = null;
- }
-
- /// <summary>
- /// Subscribe to new foreign keys columns table events
- /// </summary>
- private void SubscribeToForeignKeysTableEvents()
- {
- foreignKeysTable.TableNewRow += new DataTableNewRowEventHandler(OnNewForeignKey);
- foreignKeysTable.RowChanged += new DataRowChangeEventHandler(OnForeignKeyRowChanged);
- foreignKeysTable.RowDeleted += new DataRowChangeEventHandler(OnForeignKeyRowDeleted);
- }
-
- /// <summary>
- /// Handles chnages for the column rows.
- /// </summary>
- /// <param name="sender">Event sender, unused.</param>
- /// <param name="e">Detailed information about event</param>>
- void OnForeignKeyRowChanged(object sender, DataRowChangeEventArgs e)
- {
- Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
-
- // Extract changed key
- DataRow key = e.Row;
- if (key == null)
- return;
-
- // Chose right method for action
- switch (e.Action)
- {
- case DataRowAction.Change:
- HandleKeyChanges(key);
- break;
- default: break;
- }
- }
-
- /// <summary>
- /// Handles adding new key.
- /// </summary>
- /// <param name="sender">Event sender, unused.</param>
- /// <param name="e">Detailed information about event.</param>
- void OnNewForeignKey(object sender, DataTableNewRowEventArgs e)
- {
- Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
-
- // Extract added foreign key column
- DataRow newKey = e.Row;
- if (newKey == null)
- return;
-
- // Initialize new column attributes
- newKey[ForeignKey.Schema] = Schema;
- newKey[ForeignKey.Table] = OldName; // Old name is used to keep table name for all keys the same.
- newKey[ForeignKey.Name] = BuildNewKeyName();
- newKey[ForeignKey.OnDelete] = ForeignKeyDescriptor.RESTRICT;
- newKey[ForeignKey.OnUpdate] = ForeignKeyDescriptor.RESTRICT;
- }
-
- /// <summary>
- /// If foreign key changed, ensures that all foreign key columns will have right
- /// key name.
- /// </summary>
- /// <param name="key">DataRow with foreign key data.</param>
- private void HandleKeyChanges(DataRow key)
- {
- string newName = DataInterpreter.GetStringNotNull(key, ForeignKey.Name);
-
- // Iterate through foreign keys columns
- string fkName;
- foreach (DataRow column in ForeignKeysColumns.Rows)
- {
- // Need to skip deleted columns if any
- if (column.RowState == DataRowState.Deleted)
- continue;
-
- // Extract foreign key name
- fkName = DataInterpreter.GetStringNotNull(column, ForeignKeyColumn.ForeignKeyName);
-
- // Check if any foreign key with this name (search for key with given schema, table and name)
- DataRow candidate = FindForeignKey(fkName);
-
- // If key was not found, need to change key name for foreign key column.
- if (candidate == null)
- column[ForeignKeyColumn.ForeignKeyName] = newName;
- }
- }
-
- /// <summary>
- /// Handles foreign key deletion and delete all foreign key columns.
- /// </summary>
- /// <param name="sender">Event sender, unused.</param>
- /// <param name="e">Detailed information about event</param>>
- private void OnForeignKeyRowDeleted(object sender, DataRowChangeEventArgs e)
- {
- string fkName;
- foreach (DataRow column in ForeignKeysColumns.Select())
- {
- // Need to skip deleted columns if any
- if (column.RowState == DataRowState.Deleted)
- continue;
-
- // Extract foreign key name
- fkName = DataInterpreter.GetStringNotNull(column, ForeignKeyColumn.ForeignKeyName);
-
- // Check if any foreign key with this name (search for key with given schema, table and name)
- DataRow candidate = FindForeignKey(fkName);
-
- // If key was not found, need to delete foreign key column.
- if (candidate == null)
- column.Delete();
- }
- }
-
- /// <summary>
- /// Builds name for the new foreign key in format FK_{table name}_N.
- /// </summary>
- /// <returns>Returns name for the new foreign key in format FK_{table name}_N.</returns>
- private string BuildNewKeyName()
- {
- // Initialize searche data
- int count = 0; string result; DataRow[] existsKeys = null;
-
- // Generate new column name
- do
- {
- result = String.Format(CultureInfo.CurrentCulture, Resources.New_ForeignKey_Template, Name, ++count);
- existsKeys = DataInterpreter.Select(ForeignKeys, ForeignKey.Name, result);
- }
- while (existsKeys == null || existsKeys.Length > 0);
-
- // Return results
- return result;
- }
- #endregion
-
- #region Foreign keys column changes handling
- /// <summary>
- /// Unsubscribe from old foreign keys columns table event
- /// </summary>
- private void ResetForeignKeysColumnsTable()
- {
- foreignKeysColumnsTable.TableNewRow -= new DataTableNewRowEventHandler(OnNewForeignKeyColumn);
- foreignKeysColumnsTable.Dispose();
- foreignKeysColumnsTable = null;
- }
-
- /// <summary>
- /// Subscribe to new foreign keys columns table events
- /// </summary>
- private void SubscribeToForeignKeysColumnsTableEvents()
- {
- foreignKeysColumnsTable.TableNewRow += new DataTableNewRowEventHandler(OnNewForeignKeyColumn);
- }
-
- /// <summary>
- /// Handles adding new foreign key collumn.
- /// </summary>
- /// <param name="sender">Event sender, unused.</param>
- /// <param name="e">Detailed information about event.</param>
- void OnNewForeignKeyColumn(object sender, DataTableNewRowEventArgs e)
- {
- Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
-
- // Extract added foreign key column
- DataRow newColumn = e.Row;
- if (newColumn == null)
- return;
-
- // Initialize new column attributes
- newColumn[ForeignKeyColumn.Schema] = Schema;
- newColumn[ForeignKeyColumn.Table] = OldName; // Old name is used to keep table name for all columns the same.
-
- // Set foreign key name to empty string
- newColumn[ForeignKeyColumn.ForeignKeyName] = String.Empty;
-
- // Search for any not deleted column and set name for foreign key column
- DataRow dataColumn = DataInterpreter.GetNotDeletedRow(Columns);
- newColumn[ForeignKeyColumn.Name] = dataColumn != null
- ? DataInterpreter.GetStringNotNull(dataColumn, Column.Name)
- : String.Empty;
- }
- #endregion
-
- #region Columns changes handling
- /// <summary>
- /// Unsubscribe from old columns table event
- /// </summary>
- private void ResetColumnsTable()
- {
- columnsTable.TableNewRow -= new DataTableNewRowEventHandler(OnNewColumnRow);
- columnsTable.RowChanged -= new DataRowChangeEventHandler(OnColumnRowChanged);
- columnsTable.RowDeleted -= new DataRowChangeEventHandler(OnColumnDeleted);
- columnsTable.Dispose();
- columnsTable = null;
-
- // Clear column defaults dictionary
- columnDefaults.Clear();
- }
-
- /// <summary>
- /// Subscribe to new columns table events
- /// </summary>
- private void SubscribeToColumnsTableEvents()
- {
- columnsTable.TableNewRow += new DataTableNewRowEventHandler(OnNewColumnRow);
- columnsTable.RowChanged += new DataRowChangeEventHandler(OnColumnRowChanged);
- columnsTable.RowDeleted += new DataRowChangeEventHandler(OnColumnDeleted);
-
- // Add default value for each column into defaluts dictionary
- foreach (DataRow column in columnsTable.Rows)
- if (column != null && column.HasVersion(DataRowVersion.Current))
- columnDefaults[DataInterpreter.GetStringNotNull(column, Column.Name)]
- = DataInterpreter.GetString(column, Column.Default);
- }
-
- /// <summary>
- /// Handles chnages for the column rows.
- /// </summary>
- /// <param name="sender">Event sender, unused.</param>
- /// <param name="e">Detailed information about event</param>>
- void OnColumnRowChanged(object sender, DataRowChangeEventArgs e)
- {
- Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
-
- // Extract added column
- DataRow column = e.Row;
- if (column == null)
- return;
-
- // Chose right method for action
- switch (e.Action)
- {
- case DataRowAction.Add:
- CompleteNewCollumn(column);
- HandleColumnChanges(column);
- break;
- case DataRowAction.Change:
- HandleColumnChanges(column);
- break;
- default: break;
- }
- }
-
- /// <summary>
- /// Handles adding new collumn.
- /// </summary>
- /// <param name="sender">Event sender, unused.</param>
- /// <param name="e">Detailed information about event.</param>
- private void OnNewColumnRow(object sender, DataTableNewRowEventArgs e)
- {
- Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
-
- // Extract added column
- DataRow newColumn = e.Row;
- if (newColumn == null)
- return;
-
- // Initialize new column attributes
- newColumn[Column.Schema] = Schema;
- newColumn[Column.Table] = OldName; // Old name is used to keep table name for all columns the same.
- newColumn[Column.Name] = BuildNewColumnName();
- }
-
- /// <summary>
- /// Handles chenges in the column attributes.
- /// </summary>
- /// <param name="column">Data row with column attributes.</param>
- private void HandleColumnChanges(DataRow column)
- {
- string newName = DataInterpreter.GetStringNotNull(column, Column.Name);
-
- // Iterate through foreign keys columns
- string fkName;
- foreach (DataRow fkColumn in ForeignKeysColumns.Rows)
- {
- // Need to skip deleted columns if any
- if (fkColumn.RowState == DataRowState.Deleted)
- continue;
-
- fkName = DataInterpreter.GetStringNotNull(fkColumn, ForeignKeyColumn.Name);
-
- // Check if any data column with proper name (search for columns with given schema, table and name)
- DataRow candidate = FindColumn(fkName);
-
- // If column was not found, need to change column name for foreign key column.
- if (candidate == null)
- fkColumn[ForeignKeyColumn.Name] = newName;
-
- }
-
- // Iterate through indexes columns
- string indexName;
- foreach (DataRow indexColumn in IndexColumns.Rows)
- {
- // Need to skip deleted columns if any
- if (indexColumn.RowState == DataRowState.Deleted)
- continue;
-
- indexName = DataInterpreter.GetStringNotNull(indexColumn, IndexColumn.Name);
-
- // Check if any data column with proper name (search for columns with given schema, table and name)
- DataRow candidate = FindColumn(indexName);
-
- // If column was not found, need to change column name for index column.
- if (candidate == null)
- indexColumn[IndexColumn.Name] = newName;
- }
-
- // If column is primary key, should be index column in index PRIMARY
- if (DataInterpreter.GetSqlBool(column, Column.IsPrimaryKey).IsTrue)
- IncludeInPrimaryKey(newName);
- // If column is not primary key, exclude it from primary key index
- if (DataInterpreter.GetSqlBool(column, Column.IsPrimaryKey).IsFalse)
- ExcludeFromPrimaryKey(newName);
-
- // If default value is null, reset nullable flag (only for not auto increment columns)
- if (!DataInterpreter.IsNotNull(column, Column.Default)
- && DataInterpreter.GetSqlBool(column, Column.IsAutoIncrement).IsFalse)
- {
- object oldDefault = null;
- // If previous default value is stored and it is not null, reset allow nulls flag
- if (columnDefaults.TryGetValue(DataInterpreter.GetStringNotNull(column, Column.Name), out oldDefault)
- && oldDefault != null)
- DataInterpreter.SetValueIfChanged(column, Column.Nullable, DataInterpreter.True);
- }
- // If not null flag set, reset default value to empty string (only for not auto increment columns)
- if (DataInterpreter.GetSqlBool(column, Column.Nullable).IsFalse
- && !DataInterpreter.IsNotNull(column, Column.Default)
- && DataInterpreter.GetSqlBool(column, Column.IsAutoIncrement).IsFalse)
- SetDefault(column);
-
- // Stores old default
- columnDefaults[DataInterpreter.GetStringNotNull(column, Column.Name)]
- = DataInterpreter.GetString(column, Column.Default);
- }
-
- /// <summary>
- /// Includes given column into primary key.
- /// </summary>
- /// <param name="newName">Name of column to include in primary key.</param>
- private void IncludeInPrimaryKey(string newName)
- {
- // Create primary key index if not created yet
- if (FindIndex(IndexDescriptor.PRIMARY) == null)
- {
- DataRow primaryKey = Indexes.NewRow();
- primaryKey[Index.Name] = IndexDescriptor.PRIMARY;
- primaryKey[Index.IndexKind] = IndexDescriptor.PRIMARY;
- Indexes.Rows.Add(primaryKey);
- }
-
- // Extract exists primary key columns
- DataRow[] columns = DataInterpreter.Select(
- IndexColumns,
- IndexColumn.Index,
- IndexDescriptor.PRIMARY);
-
- // Create primary key column if not exists
- if (FindIndexColumn(IndexDescriptor.PRIMARY, newName) == null)
- {
- DataRow pkColumn = IndexColumns.NewRow();
- pkColumn[IndexColumn.Name] = newName;
- pkColumn[IndexColumn.Index] = IndexDescriptor.PRIMARY;
- // Index ordinals are one-based
- pkColumn[IndexColumn.Ordinal] = GetMaximumOrdinal(columns);
- IndexColumns.Rows.Add(pkColumn);
- }
- }
-
- /// <summary>
- /// Excludes given column from primary key.
- /// </summary>
- /// <param name="newName">Name of column to exclude from primary key.</param>
- private void ExcludeFromPrimaryKey(string newName)
- {
- // Delete related primary key column
- DataRow pkColumn = FindIndexColumn(IndexDescriptor.PRIMARY, newName);
- if (pkColumn != null)
- pkColumn.Delete();
-
- // If no more primary key columns, delete primary key index
- DataRow[] pkColumns = DataInterpreter.Select(IndexColumns, IndexColumn.Index, IndexDescriptor.PRIMARY);
- if (pkColumns == null || pkColumns.Length <= 0)
- {
- // Delete primary key, if any
- DataRow primaryKey = FindIndex(IndexDescriptor.PRIMARY);
- if (primaryKey != null)
- primaryKey.Delete();
- }
- }
-
- /// <summary>
- /// Returns maximum ordinal value for the given index columns.
- /// </summary>
- /// <param name="columns">Array with index columns to process.</param>
- /// <returns>Returns maximum ordinal value for the given index columns.</returns>
- private static Int64 GetMaximumOrdinal(DataRow[] columns)
- {
- if (columns == null)
- return 1;
- Int64 result = columns.Length + 1;
- foreach (DataRow column in columns)
- if (DataInterpreter.GetInt(column, IndexColumn.Ordinal) >= result)
- result = (Int64)DataInterpreter.GetInt(column, IndexColumn.Ordinal) + 1;
- return result;
- }
-
- /// <summary>
- /// Completes definition of the command.
- /// </summary>
- /// <param name="newColumn">Command data row to complete.</param>
- private void CompleteNewCollumn(DataRow newColumn)
- {
- // Extract column name, if any
- string columnName = DataInterpreter.GetString(newColumn, Column.Name);
-
- // If column name is given and type is not, try to find out default options
- if (!String.IsNullOrEmpty(columnName)
- && !DataInterpreter.IsNotEmptyString(newColumn, Column.MySqlType))
- {
- // If column ends with ID, let it be integer, otherwise, let it be varchar
- if (columnName.EndsWith(Resources.ID_Column_Name, StringComparison.CurrentCulture))
- {
- newColumn[Column.MySqlType] = TableDescriptor.DefaultIntType;
- newColumn[Column.Unsigned] = DataInterpreter.True;
- }
- else
- newColumn[Column.MySqlType] = TableDescriptor.DefaultCharType;
-
- // If column is ID and table have not primary key yet, let column be primary key
- if (columnName.Equals(Resources.ID_Column_Name, StringComparison.CurrentCulture)
- && !HasPrimaryKey)
- {
- newColumn[Column.IsAutoIncrement] = DataInterpreter.True;
- newColumn[Column.IsPrimaryKey] = DataInterpreter.True;
- }
- else
- {
- // Set default value if columns is not marked as autoincrement
- SetDefault(newColumn);
- }
-
- // Let column be not nullabale
- newColumn[Column.Nullable] = DataInterpreter.False;
- }
-
- columnDefaults[DataInterpreter.GetStringNotNull(newColumn, Column.Name)]
- = DataInterpreter.GetString(newColumn, Column.Default);
- }
-
- /// <summary>
- /// Handles column deletion and deletes related foreign key columns.
- /// </summary>
- /// <param name="sender">Event sender, unused.</param>
- /// <param name="e">Detailed information about event.</param>
- private void OnColumnDeleted(object sender, DataRowChangeEventArgs e)
- {
- // Iterate through foreign keys columns
- string fkName;
- foreach (DataRow fkColumn in ForeignKeysColumns.Select())
- {
- // Need to skip deleted columns if any
- if (fkColumn.RowState == DataRowState.Deleted)
- continue;
-
- fkName = DataInterpreter.GetStringNotNull(fkColumn, ForeignKeyColumn.Name);
-
- // Check if any data column with proper name (search for columns with given schema, table and name)
- DataRow candidate = FindColumn(fkName);
-
- // If column was not found, need to delete this foreign key column.
- if (candidate == null)
- fkColumn.Delete();
-
- }
-
- // Iterate through indexes columns
- string indexName;
- foreach (DataRow indexColumn in IndexColumns.Select())
- {
- // Need to skip deleted columns if any
- if (indexColumn.RowState == DataRowState.Deleted)
- continue;
-
- indexName = DataInterpreter.GetStringNotNull(indexColumn, IndexColumn.Name);
-
- // Check if any data column with proper name (search for columns with given schema, table and name)
- DataRow candidate = FindColumn(indexName);
-
- // If column was not found, need to delete this index column.
- if (candidate == null)
- indexColumn.Delete()…
Large files files are truncated, but you can click here to view the full file