PageRenderTime 77ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 1ms

/VisualStudio/DocumentView/Documents/TableDocument.cs

https://github.com/rykr/connector-net
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

  1. // Copyright (C) 2006-2007 MySQL AB
  2. //
  3. // This file is part of MySQL Tools for Visual Studio.
  4. // MySQL Tools for Visual Studio is free software; you can redistribute it
  5. // and/or modify it under the terms of the GNU Lesser General Public
  6. // License version 2.1 as published by the Free Software Foundation
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU Lesser General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU Lesser General Public License
  14. // along with this program; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System;
  16. /*
  17. * This file contains implementation of data object for TABLE representation.
  18. */
  19. using System;
  20. using System.ComponentModel;
  21. using System.Collections.Generic;
  22. using System.Diagnostics;
  23. using System.Text;
  24. using System.Data;
  25. using Microsoft.VisualStudio.Data;
  26. using Microsoft.VisualStudio.Shell;
  27. using Microsoft.VisualStudio.Shell.Interop;
  28. using Microsoft.VisualStudio;
  29. using MySql.Data.VisualStudio.Utils;
  30. using System.Windows.Forms.Design;
  31. using System.Data.SqlTypes;
  32. using MySql.Data.VisualStudio.Descriptors;
  33. using Table = MySql.Data.VisualStudio.Descriptors.TableDescriptor.Attributes;
  34. using Column = MySql.Data.VisualStudio.Descriptors.ColumnDescriptor.Attributes;
  35. using ForeignKey = MySql.Data.VisualStudio.Descriptors.ForeignKeyDescriptor.Attributes;
  36. using ForeignKeyColumn = MySql.Data.VisualStudio.Descriptors.ForeignKeyColumnDescriptor.Attributes;
  37. using Index = MySql.Data.VisualStudio.Descriptors.IndexDescriptor.Attributes;
  38. using IndexColumn = MySql.Data.VisualStudio.Descriptors.IndexColumnDescriptor.Attributes;
  39. using System.Globalization;
  40. using MySql.Data.VisualStudio.Properties;
  41. using System.Drawing.Design;
  42. using System.Windows.Forms;
  43. namespace MySql.Data.VisualStudio.DocumentView
  44. {
  45. /// <summary>
  46. /// This class implements document (data object) functionality and
  47. /// represent database table. It implements IVsPersistDocData interface and
  48. /// beaves like filelles data object for Visual Studio enviroment.
  49. /// </summary>
  50. [DocumentObject(TableDescriptor.TypeName, typeof(TableDocument))]
  51. public class TableDocument : BaseDocument, CollationConverter.ICharacterSetProvider
  52. {
  53. #region Initialization
  54. /// <summary>
  55. /// This constructor initialize private identifier variables.
  56. /// </summary>
  57. /// <param name="hierarchy">
  58. /// Data view hierarchy accessor used to interact with Server Explorer.
  59. /// Also used to extract connection.
  60. /// </param>
  61. /// <param name="id">
  62. /// Array with the object identifier.
  63. /// </param>
  64. /// <param name="isNew">
  65. /// Indicates if this instance represents new database object doesn’t fixed in
  66. /// database yet.
  67. /// </param>
  68. public TableDocument(ServerExplorerFacade hierarchy, bool isNew, object[] id)
  69. :base(hierarchy, isNew, id)
  70. {
  71. }
  72. #endregion
  73. #region BaseDocument overridings
  74. /// <summary>
  75. /// Dirty flag used to determine whenever data are changed or not. At this
  76. /// point table data considered as changed if Columns data table has changes.
  77. /// </summary>
  78. protected override bool IsDirty
  79. {
  80. get
  81. {
  82. // Asks base class
  83. if (base.IsDirty)
  84. return true;
  85. Debug.Assert(Columns != null, "Collumns are not read!");
  86. Debug.Assert(ForeignKeys != null, "Foreign keys are not read!");
  87. Debug.Assert(ForeignKeysColumns != null, "Foreign keys columns are not read!");
  88. Debug.Assert(Indexes != null, "Indexes are not read!");
  89. Debug.Assert(IndexColumns != null, "IndexColumns columns are not read!");
  90. // If we aren't read return true
  91. if (Columns == null || ForeignKeys == null || ForeignKeysColumns == null
  92. || Indexes == null || IndexColumns == null)
  93. return false;
  94. // Check column list and other tables
  95. if (DataInterpreter.HasChanged(Columns)
  96. || DataInterpreter.HasChanged(ForeignKeys)
  97. || DataInterpreter.HasChanged(ForeignKeysColumns)
  98. || DataInterpreter.HasChanged(Indexes)
  99. || DataInterpreter.HasChanged(IndexColumns))
  100. return true; ;
  101. return false;
  102. }
  103. }
  104. /// <summary>
  105. /// Returns query for pre-dropping foreign keys and indexes.
  106. /// </summary>
  107. /// <returns>Returns query for pre-dropping foreign keys and indexes.</returns>
  108. protected override string BuildPreDropQuery()
  109. {
  110. // Pre-drop foreign keys and indexes if needed
  111. if (NeedToDropForeignKeys())
  112. return BuildPreDropForeignKeys();
  113. return String.Empty;
  114. }
  115. /// <summary>
  116. /// Builds alter query for table.
  117. /// </summary>
  118. /// <returns>Alter query for table.</returns>
  119. protected override string BuildAlterQuery()
  120. {
  121. StringBuilder query = new StringBuilder();
  122. // Write header
  123. BuildAlterHeader(query);
  124. BuildAlterSpecifications(query);
  125. return query.ToString();
  126. }
  127. /// <summary>
  128. /// Builda create query for table.
  129. /// </summary>
  130. /// <returns>Create query for table.</returns>
  131. protected override string BuildCreateQuery()
  132. {
  133. StringBuilder query = new StringBuilder();
  134. BuildCreateHeader(query);
  135. query.Append(" (");
  136. BuildCreateDefinition(query);
  137. query.Append(" )");
  138. BuildTableOptions(query, null);
  139. return query.ToString();
  140. }
  141. /// <summary>
  142. /// Load database object from database.
  143. /// </summary>
  144. /// <param name="reloading">
  145. /// This flag indicates that object is reloading. Should be ignored in most cases.
  146. /// </param>
  147. /// <returns>Returns true if load succeeds and false otherwise.</returns>
  148. protected override bool LoadData(bool reloading)
  149. {
  150. // Read table atributes using base method
  151. if (!base.LoadData(reloading))
  152. return false;
  153. // Reset current columns table, if any.
  154. if (columnsTable != null)
  155. ResetColumnsTable();
  156. // Reset foreign keys table, if any
  157. if (foreignKeysTable != null)
  158. ResetForeignKeysTable();
  159. // Reset foreign keys columns table, if any
  160. if (foreignKeysColumnsTable != null)
  161. ResetForeignKeysColumnsTable();
  162. // Reset indexes table, if any
  163. if (indexesTable != null)
  164. ResetIndexesTable();
  165. // Reset indexes columns table, if any
  166. if (indexesTable != null)
  167. ResetIndexesColumnsTable();
  168. // Read columns for table
  169. columnsTable = ColumnDescriptor.Enumerate(Connection, ObjectIDForLoad);
  170. if (columnsTable == null)
  171. {
  172. Debug.Fail("Failed to read columns!");
  173. return false;
  174. }
  175. // Subscribe to new columns table events
  176. SubscribeToColumnsTableEvents();
  177. // Read foreign keys for table.
  178. foreignKeysTable = ForeignKeyDescriptor.Enumerate(Connection, ObjectIDForLoad);
  179. if (foreignKeysTable == null)
  180. {
  181. Debug.Fail("Failed to read foreign keys!");
  182. return false;
  183. }
  184. // Subscrube to events
  185. SubscribeToForeignKeysTableEvents();
  186. // Read foreign keys columns for table.
  187. foreignKeysColumnsTable = ForeignKeyColumnDescriptor.Enumerate(Connection, ObjectIDForLoad);
  188. if (foreignKeysColumnsTable == null)
  189. {
  190. Debug.Fail("Failed to read foreign keys columns!");
  191. return false;
  192. }
  193. // Subscrube to events
  194. SubscribeToForeignKeysColumnsTableEvents();
  195. // Read indexes for table.
  196. indexesTable = IndexDescriptor.Enumerate(Connection, ObjectIDForLoad);
  197. if (indexesTable == null)
  198. {
  199. Debug.Fail("Failed to read indexes!");
  200. return false;
  201. }
  202. // Subscrube to events
  203. SubscribeToIndexesTableEvents();
  204. // Read index columns for table.
  205. indexColumnsTable = IndexColumnDescriptor.Enumerate(Connection, ObjectIDForLoad);
  206. if (indexColumnsTable == null)
  207. {
  208. Debug.Fail("Failed to read index columns!");
  209. return false;
  210. }
  211. // Subscrube to events
  212. SubscribeToIndexesColumnsTableEvents();
  213. return true;
  214. }
  215. /// <summary>
  216. /// Accepts changes in column grid.
  217. /// </summary>
  218. protected override void AcceptChanges()
  219. {
  220. base.AcceptChanges();
  221. // Accepting columns changes
  222. columnsTable.AcceptChanges();
  223. }
  224. /// <summary>
  225. /// Fills aditional properties for new table.
  226. /// </summary>
  227. /// <param name="newRow">DataRow to fill with properties.</param>
  228. protected override void FillNewObjectAttributes(DataRow newRow)
  229. {
  230. base.FillNewObjectAttributes(newRow);
  231. newRow[Table.Engine] = Connection.DefaultEngine;
  232. newRow[Table.CharacterSet] = Connection.DefaultCharacterSet;
  233. newRow[Table.Collation] = Connection.DefaultCollation;
  234. }
  235. /// <summary>
  236. /// Resets data for the cloned table.
  237. /// </summary>
  238. protected override void ResetToNew()
  239. {
  240. // Call to base
  241. base.ResetToNew();
  242. // Validate columns and other data (must be loaded)
  243. if (Columns == null || ForeignKeys == null || ForeignKeysColumns == null
  244. || Indexes == null || IndexColumns == null)
  245. return;
  246. //Reset columns
  247. DataTable temp = MakeCopy(Columns);
  248. ResetTableName(temp, Column.Table);
  249. ResetColumnsTable();
  250. columnsTable = temp;
  251. SubscribeToColumnsTableEvents();
  252. //Reset foreign keys
  253. temp = MakeCopy(ForeignKeys);
  254. ResetTableName(temp, ForeignKey.Table);
  255. // For each foreign key we need new name (some times InnoDB falls if keys have same name
  256. // even in the different tables)
  257. foreach (DataRow key in temp.Rows)
  258. GenerateNewName(key);
  259. ResetForeignKeysTable();
  260. foreignKeysTable = temp;
  261. SubscribeToForeignKeysTableEvents();
  262. //Reset foreign key columns
  263. temp = MakeCopy(ForeignKeysColumns);
  264. ResetTableName(temp, ForeignKeyColumn.Table);
  265. ResetForeignKeysColumnsTable();
  266. foreignKeysColumnsTable = temp;
  267. SubscribeToForeignKeysColumnsTableEvents();
  268. //Reset indexes
  269. temp = MakeCopy(Indexes);
  270. ResetTableName(temp, Index.Table);
  271. ResetIndexesTable();
  272. indexesTable = temp;
  273. SubscribeToIndexesTableEvents();
  274. //Reset indexes columns
  275. temp = MakeCopy(IndexColumns);
  276. ResetTableName(temp, IndexColumn.Table);
  277. ResetIndexesColumnsTable();
  278. indexColumnsTable = temp;
  279. SubscribeToIndexesColumnsTableEvents();
  280. }
  281. /// <summary>
  282. /// Processes failures on save. May remove pre-dropped foreign keys and indexes
  283. /// from tables.
  284. /// </summary>
  285. protected override void SaveFailed()
  286. {
  287. base.SaveFailed();
  288. // If query was with two parts and at first part we droped foreign keys
  289. // we must check all keys and indexes for existens
  290. if (!NeedToDropForeignKeys())
  291. {
  292. // Call to base
  293. CallBaseSaveFailed();
  294. return;
  295. }
  296. // Flag to indicate that we droped somthing
  297. bool dropped = false;
  298. // Enumerate current foreign keys
  299. DataTable currentKeys = ForeignKeyDescriptor.Enumerate(Connection, OldObjectID);
  300. Debug.Assert(currentKeys != null && currentKeys.Rows != null, "Failed to re-enumerate foreign keys");
  301. if (currentKeys != null && currentKeys.Rows != null)
  302. {
  303. foreach (DataRow key in ForeignKeys.Select())
  304. {
  305. switch (key.RowState)
  306. {
  307. // Skip added and deleted keys
  308. case DataRowState.Added:
  309. case DataRowState.Deleted:
  310. break;
  311. // Check if this key was changed and pre-droped
  312. default:
  313. if (HasForeignKeyChanged(key))
  314. {
  315. DataRow currentKey = currentKeys.Rows.Find(new object[] {
  316. Schema,
  317. OldName,
  318. DataInterpreter.GetString(key, ForeignKey.Name) });
  319. // If no current key founded, mark it as new
  320. if (currentKey == null)
  321. {
  322. dropped = true;
  323. key.AcceptChanges();
  324. key.SetAdded();
  325. }
  326. }
  327. break;
  328. }
  329. }
  330. }
  331. // Enumerate current indexes
  332. DataTable currentIndexes = IndexDescriptor.Enumerate(Connection, OldObjectID);
  333. Debug.Assert(currentIndexes != null && currentIndexes.Rows != null, "Failed to re-enumerate indexes");
  334. if (currentIndexes != null && currentIndexes.Rows != null)
  335. {
  336. foreach (DataRow index in Indexes.Select())
  337. {
  338. switch (index.RowState)
  339. {
  340. // Skip added and deleted indexes
  341. case DataRowState.Added:
  342. case DataRowState.Deleted:
  343. break;
  344. // Check if this index was changed and pre-droped
  345. default:
  346. DataRow currentIndex = currentIndexes.Rows.Find(new object[] {
  347. Schema,
  348. OldName,
  349. DataInterpreter.GetString(index, Index.Name) });
  350. // If no current index founded, mark it as new
  351. if (currentIndex == null)
  352. {
  353. dropped = true;
  354. index.AcceptChanges();
  355. index.SetAdded();
  356. }
  357. break;
  358. }
  359. }
  360. }
  361. // Call to base
  362. CallBaseSaveFailed();
  363. // Warn user if key or index was pre-droped
  364. if (dropped)
  365. UIHelper.ShowWarning(Resources.Warning_KeyWasPredropped);
  366. }
  367. #endregion
  368. #region Index changes handling
  369. /// <summary>
  370. /// Unsubscribe from old indexes table event
  371. /// </summary>
  372. private void ResetIndexesTable()
  373. {
  374. indexesTable.TableNewRow -= new DataTableNewRowEventHandler(OnNewIndex);
  375. indexesTable.RowChanged -= new DataRowChangeEventHandler(OnIndexRowChanged);
  376. indexesTable.RowDeleted -= new DataRowChangeEventHandler(OnIndexRowDeleted);
  377. indexesTable.Dispose();
  378. indexesTable = null;
  379. }
  380. /// <summary>
  381. /// Subscribe to new index table events
  382. /// </summary>
  383. private void SubscribeToIndexesTableEvents()
  384. {
  385. indexesTable.TableNewRow += new DataTableNewRowEventHandler(OnNewIndex);
  386. indexesTable.RowChanged += new DataRowChangeEventHandler(OnIndexRowChanged);
  387. indexesTable.RowDeleted += new DataRowChangeEventHandler(OnIndexRowDeleted);
  388. }
  389. /// <summary>
  390. /// Handles chnages for the index rows.
  391. /// </summary>
  392. /// <param name="sender">Event sender, unused.</param>
  393. /// <param name="e">Detailed information about event</param>>
  394. void OnIndexRowChanged(object sender, DataRowChangeEventArgs e)
  395. {
  396. Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
  397. // Extract changed index
  398. DataRow index = e.Row;
  399. if (index == null)
  400. return;
  401. // Chose right method for action
  402. switch (e.Action)
  403. {
  404. case DataRowAction.Change:
  405. HandleIndexChanges(index);
  406. break;
  407. default: break;
  408. }
  409. }
  410. /// <summary>
  411. /// Handles adding new index.
  412. /// </summary>
  413. /// <param name="sender">Event sender, unused.</param>
  414. /// <param name="e">Detailed information about event.</param>
  415. void OnNewIndex(object sender, DataTableNewRowEventArgs e)
  416. {
  417. Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
  418. // Extract added index
  419. DataRow newIndex = e.Row;
  420. if (newIndex == null)
  421. return;
  422. // Initialize new column attributes
  423. newIndex[Index.Schema] = Schema;
  424. newIndex[Index.Table] = OldName; // Old name is used to keep table name for all indexes the same.
  425. newIndex[Index.Name] = BuildNewIndexName();
  426. newIndex[Index.IndexKind] = IndexDescriptor.INDEX;
  427. newIndex[Index.IndexType] = IndexDescriptor.BTREE;
  428. }
  429. /// <summary>
  430. /// If index changed, ensures that all index columns will have right index name.
  431. /// </summary>
  432. /// <param name="index">DataRow with index data.</param>
  433. private void HandleIndexChanges(DataRow index)
  434. {
  435. string newName = DataInterpreter.GetStringNotNull(index, Index.Name);
  436. // Iterate through index columns
  437. string indexName;
  438. foreach (DataRow column in IndexColumns.Rows)
  439. {
  440. // Need to skip deleted columns if any
  441. if (column.RowState == DataRowState.Deleted)
  442. continue;
  443. // Extract index name
  444. indexName = DataInterpreter.GetStringNotNull(column, IndexColumn.Index);
  445. // Check if any index with this name (search for index with given schema, table and name)
  446. DataRow candidate = FindIndex(indexName);
  447. // If index was not found, need to change index name for index column.
  448. if (candidate == null)
  449. column[IndexColumn.Index] = newName;
  450. }
  451. }
  452. /// <summary>
  453. /// Handles indeex deletion and delete all index columns.
  454. /// </summary>
  455. /// <param name="sender">Event sender, unused.</param>
  456. /// <param name="e">Detailed information about event</param>>
  457. private void OnIndexRowDeleted(object sender, DataRowChangeEventArgs e)
  458. {
  459. string indexName;
  460. foreach (DataRow column in IndexColumns.Select())
  461. {
  462. // Need to skip deleted columns if any
  463. if (column.RowState == DataRowState.Deleted)
  464. continue;
  465. // Extract index name
  466. indexName = DataInterpreter.GetStringNotNull(column, IndexColumn.Index);
  467. // Check if any index with this name (search for index with given schema, table and name)
  468. DataRow candidate = FindIndex(indexName);
  469. // If index was not found, need to delete index column.
  470. if (candidate == null)
  471. column.Delete();
  472. }
  473. }
  474. /// <summary>
  475. /// Builds name for the new new index in format Index_N.
  476. /// </summary>
  477. /// <returns>Returns name for the new new index in format Index_N.</returns>
  478. private string BuildNewIndexName()
  479. {
  480. // Initialize search data
  481. int count = 0; string result; DataRow[] existsIndexes = null;
  482. // Generate new index name
  483. do
  484. {
  485. result = String.Format(CultureInfo.CurrentCulture, Resources.New_Index_Template, ++count);
  486. existsIndexes = DataInterpreter.Select(Indexes, Index.Name, result);
  487. }
  488. while (existsIndexes == null || existsIndexes.Length > 0);
  489. // Return results
  490. return result;
  491. }
  492. #endregion
  493. #region Indexes columns changes handling
  494. /// <summary>
  495. /// Unsubscribe from old indexes columns table event
  496. /// </summary>
  497. private void ResetIndexesColumnsTable()
  498. {
  499. indexColumnsTable.TableNewRow -= new DataTableNewRowEventHandler(OnNewIndexColumn);
  500. indexColumnsTable.RowChanged -= new DataRowChangeEventHandler(OnIndexColumnChanged);
  501. indexColumnsTable.RowDeleted -= new DataRowChangeEventHandler(OnIndexColumnChanged);
  502. indexColumnsTable.Dispose();
  503. indexColumnsTable = null;
  504. }
  505. /// <summary>
  506. /// Subscribe to new indexes columns table events
  507. /// </summary>
  508. private void SubscribeToIndexesColumnsTableEvents()
  509. {
  510. indexColumnsTable.TableNewRow += new DataTableNewRowEventHandler(OnNewIndexColumn);
  511. indexColumnsTable.RowChanged += new DataRowChangeEventHandler(OnIndexColumnChanged);
  512. indexColumnsTable.RowDeleted += new DataRowChangeEventHandler(OnIndexColumnChanged);
  513. }
  514. /// <summary>
  515. /// Handles adding new indes collumn.
  516. /// </summary>
  517. /// <param name="sender">Event sender, unused.</param>
  518. /// <param name="e">Detailed information about event.</param>
  519. void OnNewIndexColumn(object sender, DataTableNewRowEventArgs e)
  520. {
  521. Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
  522. // Extract added index column
  523. DataRow newColumn = e.Row;
  524. if (newColumn == null)
  525. return;
  526. // Initialize new column attributes
  527. newColumn[IndexColumn.Schema] = Schema;
  528. newColumn[IndexColumn.Table] = OldName; // Old name is used to keep table name for all columns the same.
  529. // Set index name to empty string
  530. newColumn[IndexColumn.Index] = String.Empty;
  531. // Search for any not deleted column and set name for index column
  532. DataRow dataColumn = DataInterpreter.GetNotDeletedRow(Columns);
  533. newColumn[IndexColumn.Name] = dataColumn != null
  534. ? DataInterpreter.GetStringNotNull(dataColumn, Column.Name)
  535. : String.Empty;
  536. }
  537. /// <summary>
  538. /// Handles index column changes and deletions. Adjust table columns primary key status.
  539. /// </summary>
  540. /// <param name="sender">Event sender, unused.</param>
  541. /// <param name="e">Detailed information about event.</param>
  542. void OnIndexColumnChanged(object sender, DataRowChangeEventArgs e)
  543. {
  544. Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
  545. // Extract added column
  546. DataRow column = e.Row;
  547. if (column == null)
  548. return;
  549. // Chose right method for action
  550. switch (e.Action)
  551. {
  552. case DataRowAction.Add:
  553. case DataRowAction.Change:
  554. HandleIndexColumnChanges(column);
  555. break;
  556. case DataRowAction.Delete:
  557. HandleIndexColumnDeleted();
  558. break;
  559. default: break;
  560. }
  561. }
  562. /// <summary>
  563. /// Then primary key index column is deleted, ensures that table column will be unmarked
  564. /// as primary key.
  565. /// </summary>
  566. private void HandleIndexColumnDeleted()
  567. {
  568. // Check each primary key column
  569. foreach (DataRow column in DataInterpreter.Select(Columns, Column.IsPrimaryKey, DataInterpreter.True))
  570. {
  571. // Skip deleted columns
  572. if (column.RowState == DataRowState.Deleted)
  573. continue;
  574. // Ensure that index column stil exists
  575. DataRow indexColumn = FindIndexColumn(IndexDescriptor.PRIMARY, DataInterpreter.GetStringNotNull(column, Column.Name));
  576. if(indexColumn == null)
  577. DataInterpreter.SetValueIfChanged(column, Column.IsPrimaryKey, DataInterpreter.False);
  578. }
  579. }
  580. /// <summary>
  581. /// Handles index column changes and mark/unmark primary key columns.
  582. /// </summary>
  583. /// <param name="column">DataRow with index column data.</param>
  584. private void HandleIndexColumnChanges(DataRow column)
  585. {
  586. // Extract index name
  587. string indexName = DataInterpreter.GetStringNotNull(column, IndexColumn.Index);
  588. // Extract related table column
  589. DataRow tableColumn = FindColumn(DataInterpreter.GetStringNotNull(column, IndexColumn.Name));
  590. if (tableColumn == null)
  591. return;
  592. // If index is primary key, mark column as primary key
  593. if (DataInterpreter.CompareInvariant(indexName, IndexDescriptor.PRIMARY))
  594. DataInterpreter.SetValueIfChanged(tableColumn, Column.IsPrimaryKey, DataInterpreter.True);
  595. }
  596. #endregion
  597. #region Foreign keys changes handling
  598. /// <summary>
  599. /// Unsubscribe from old foreign keys columns table event
  600. /// </summary>
  601. private void ResetForeignKeysTable()
  602. {
  603. foreignKeysTable.TableNewRow -= new DataTableNewRowEventHandler(OnNewForeignKey);
  604. foreignKeysTable.RowChanged -= new DataRowChangeEventHandler(OnForeignKeyRowChanged);
  605. foreignKeysTable.RowDeleted -= new DataRowChangeEventHandler(OnForeignKeyRowDeleted);
  606. foreignKeysTable.Dispose();
  607. foreignKeysTable = null;
  608. }
  609. /// <summary>
  610. /// Subscribe to new foreign keys columns table events
  611. /// </summary>
  612. private void SubscribeToForeignKeysTableEvents()
  613. {
  614. foreignKeysTable.TableNewRow += new DataTableNewRowEventHandler(OnNewForeignKey);
  615. foreignKeysTable.RowChanged += new DataRowChangeEventHandler(OnForeignKeyRowChanged);
  616. foreignKeysTable.RowDeleted += new DataRowChangeEventHandler(OnForeignKeyRowDeleted);
  617. }
  618. /// <summary>
  619. /// Handles chnages for the column rows.
  620. /// </summary>
  621. /// <param name="sender">Event sender, unused.</param>
  622. /// <param name="e">Detailed information about event</param>>
  623. void OnForeignKeyRowChanged(object sender, DataRowChangeEventArgs e)
  624. {
  625. Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
  626. // Extract changed key
  627. DataRow key = e.Row;
  628. if (key == null)
  629. return;
  630. // Chose right method for action
  631. switch (e.Action)
  632. {
  633. case DataRowAction.Change:
  634. HandleKeyChanges(key);
  635. break;
  636. default: break;
  637. }
  638. }
  639. /// <summary>
  640. /// Handles adding new key.
  641. /// </summary>
  642. /// <param name="sender">Event sender, unused.</param>
  643. /// <param name="e">Detailed information about event.</param>
  644. void OnNewForeignKey(object sender, DataTableNewRowEventArgs e)
  645. {
  646. Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
  647. // Extract added foreign key column
  648. DataRow newKey = e.Row;
  649. if (newKey == null)
  650. return;
  651. // Initialize new column attributes
  652. newKey[ForeignKey.Schema] = Schema;
  653. newKey[ForeignKey.Table] = OldName; // Old name is used to keep table name for all keys the same.
  654. newKey[ForeignKey.Name] = BuildNewKeyName();
  655. newKey[ForeignKey.OnDelete] = ForeignKeyDescriptor.RESTRICT;
  656. newKey[ForeignKey.OnUpdate] = ForeignKeyDescriptor.RESTRICT;
  657. }
  658. /// <summary>
  659. /// If foreign key changed, ensures that all foreign key columns will have right
  660. /// key name.
  661. /// </summary>
  662. /// <param name="key">DataRow with foreign key data.</param>
  663. private void HandleKeyChanges(DataRow key)
  664. {
  665. string newName = DataInterpreter.GetStringNotNull(key, ForeignKey.Name);
  666. // Iterate through foreign keys columns
  667. string fkName;
  668. foreach (DataRow column in ForeignKeysColumns.Rows)
  669. {
  670. // Need to skip deleted columns if any
  671. if (column.RowState == DataRowState.Deleted)
  672. continue;
  673. // Extract foreign key name
  674. fkName = DataInterpreter.GetStringNotNull(column, ForeignKeyColumn.ForeignKeyName);
  675. // Check if any foreign key with this name (search for key with given schema, table and name)
  676. DataRow candidate = FindForeignKey(fkName);
  677. // If key was not found, need to change key name for foreign key column.
  678. if (candidate == null)
  679. column[ForeignKeyColumn.ForeignKeyName] = newName;
  680. }
  681. }
  682. /// <summary>
  683. /// Handles foreign key deletion and delete all foreign key columns.
  684. /// </summary>
  685. /// <param name="sender">Event sender, unused.</param>
  686. /// <param name="e">Detailed information about event</param>>
  687. private void OnForeignKeyRowDeleted(object sender, DataRowChangeEventArgs e)
  688. {
  689. string fkName;
  690. foreach (DataRow column in ForeignKeysColumns.Select())
  691. {
  692. // Need to skip deleted columns if any
  693. if (column.RowState == DataRowState.Deleted)
  694. continue;
  695. // Extract foreign key name
  696. fkName = DataInterpreter.GetStringNotNull(column, ForeignKeyColumn.ForeignKeyName);
  697. // Check if any foreign key with this name (search for key with given schema, table and name)
  698. DataRow candidate = FindForeignKey(fkName);
  699. // If key was not found, need to delete foreign key column.
  700. if (candidate == null)
  701. column.Delete();
  702. }
  703. }
  704. /// <summary>
  705. /// Builds name for the new foreign key in format FK_{table name}_N.
  706. /// </summary>
  707. /// <returns>Returns name for the new foreign key in format FK_{table name}_N.</returns>
  708. private string BuildNewKeyName()
  709. {
  710. // Initialize searche data
  711. int count = 0; string result; DataRow[] existsKeys = null;
  712. // Generate new column name
  713. do
  714. {
  715. result = String.Format(CultureInfo.CurrentCulture, Resources.New_ForeignKey_Template, Name, ++count);
  716. existsKeys = DataInterpreter.Select(ForeignKeys, ForeignKey.Name, result);
  717. }
  718. while (existsKeys == null || existsKeys.Length > 0);
  719. // Return results
  720. return result;
  721. }
  722. #endregion
  723. #region Foreign keys column changes handling
  724. /// <summary>
  725. /// Unsubscribe from old foreign keys columns table event
  726. /// </summary>
  727. private void ResetForeignKeysColumnsTable()
  728. {
  729. foreignKeysColumnsTable.TableNewRow -= new DataTableNewRowEventHandler(OnNewForeignKeyColumn);
  730. foreignKeysColumnsTable.Dispose();
  731. foreignKeysColumnsTable = null;
  732. }
  733. /// <summary>
  734. /// Subscribe to new foreign keys columns table events
  735. /// </summary>
  736. private void SubscribeToForeignKeysColumnsTableEvents()
  737. {
  738. foreignKeysColumnsTable.TableNewRow += new DataTableNewRowEventHandler(OnNewForeignKeyColumn);
  739. }
  740. /// <summary>
  741. /// Handles adding new foreign key collumn.
  742. /// </summary>
  743. /// <param name="sender">Event sender, unused.</param>
  744. /// <param name="e">Detailed information about event.</param>
  745. void OnNewForeignKeyColumn(object sender, DataTableNewRowEventArgs e)
  746. {
  747. Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
  748. // Extract added foreign key column
  749. DataRow newColumn = e.Row;
  750. if (newColumn == null)
  751. return;
  752. // Initialize new column attributes
  753. newColumn[ForeignKeyColumn.Schema] = Schema;
  754. newColumn[ForeignKeyColumn.Table] = OldName; // Old name is used to keep table name for all columns the same.
  755. // Set foreign key name to empty string
  756. newColumn[ForeignKeyColumn.ForeignKeyName] = String.Empty;
  757. // Search for any not deleted column and set name for foreign key column
  758. DataRow dataColumn = DataInterpreter.GetNotDeletedRow(Columns);
  759. newColumn[ForeignKeyColumn.Name] = dataColumn != null
  760. ? DataInterpreter.GetStringNotNull(dataColumn, Column.Name)
  761. : String.Empty;
  762. }
  763. #endregion
  764. #region Columns changes handling
  765. /// <summary>
  766. /// Unsubscribe from old columns table event
  767. /// </summary>
  768. private void ResetColumnsTable()
  769. {
  770. columnsTable.TableNewRow -= new DataTableNewRowEventHandler(OnNewColumnRow);
  771. columnsTable.RowChanged -= new DataRowChangeEventHandler(OnColumnRowChanged);
  772. columnsTable.RowDeleted -= new DataRowChangeEventHandler(OnColumnDeleted);
  773. columnsTable.Dispose();
  774. columnsTable = null;
  775. // Clear column defaults dictionary
  776. columnDefaults.Clear();
  777. }
  778. /// <summary>
  779. /// Subscribe to new columns table events
  780. /// </summary>
  781. private void SubscribeToColumnsTableEvents()
  782. {
  783. columnsTable.TableNewRow += new DataTableNewRowEventHandler(OnNewColumnRow);
  784. columnsTable.RowChanged += new DataRowChangeEventHandler(OnColumnRowChanged);
  785. columnsTable.RowDeleted += new DataRowChangeEventHandler(OnColumnDeleted);
  786. // Add default value for each column into defaluts dictionary
  787. foreach (DataRow column in columnsTable.Rows)
  788. if (column != null && column.HasVersion(DataRowVersion.Current))
  789. columnDefaults[DataInterpreter.GetStringNotNull(column, Column.Name)]
  790. = DataInterpreter.GetString(column, Column.Default);
  791. }
  792. /// <summary>
  793. /// Handles chnages for the column rows.
  794. /// </summary>
  795. /// <param name="sender">Event sender, unused.</param>
  796. /// <param name="e">Detailed information about event</param>>
  797. void OnColumnRowChanged(object sender, DataRowChangeEventArgs e)
  798. {
  799. Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
  800. // Extract added column
  801. DataRow column = e.Row;
  802. if (column == null)
  803. return;
  804. // Chose right method for action
  805. switch (e.Action)
  806. {
  807. case DataRowAction.Add:
  808. CompleteNewCollumn(column);
  809. HandleColumnChanges(column);
  810. break;
  811. case DataRowAction.Change:
  812. HandleColumnChanges(column);
  813. break;
  814. default: break;
  815. }
  816. }
  817. /// <summary>
  818. /// Handles adding new collumn.
  819. /// </summary>
  820. /// <param name="sender">Event sender, unused.</param>
  821. /// <param name="e">Detailed information about event.</param>
  822. private void OnNewColumnRow(object sender, DataTableNewRowEventArgs e)
  823. {
  824. Debug.Assert(e != null && e.Row != null, "Empty event argumets provided!");
  825. // Extract added column
  826. DataRow newColumn = e.Row;
  827. if (newColumn == null)
  828. return;
  829. // Initialize new column attributes
  830. newColumn[Column.Schema] = Schema;
  831. newColumn[Column.Table] = OldName; // Old name is used to keep table name for all columns the same.
  832. newColumn[Column.Name] = BuildNewColumnName();
  833. }
  834. /// <summary>
  835. /// Handles chenges in the column attributes.
  836. /// </summary>
  837. /// <param name="column">Data row with column attributes.</param>
  838. private void HandleColumnChanges(DataRow column)
  839. {
  840. string newName = DataInterpreter.GetStringNotNull(column, Column.Name);
  841. // Iterate through foreign keys columns
  842. string fkName;
  843. foreach (DataRow fkColumn in ForeignKeysColumns.Rows)
  844. {
  845. // Need to skip deleted columns if any
  846. if (fkColumn.RowState == DataRowState.Deleted)
  847. continue;
  848. fkName = DataInterpreter.GetStringNotNull(fkColumn, ForeignKeyColumn.Name);
  849. // Check if any data column with proper name (search for columns with given schema, table and name)
  850. DataRow candidate = FindColumn(fkName);
  851. // If column was not found, need to change column name for foreign key column.
  852. if (candidate == null)
  853. fkColumn[ForeignKeyColumn.Name] = newName;
  854. }
  855. // Iterate through indexes columns
  856. string indexName;
  857. foreach (DataRow indexColumn in IndexColumns.Rows)
  858. {
  859. // Need to skip deleted columns if any
  860. if (indexColumn.RowState == DataRowState.Deleted)
  861. continue;
  862. indexName = DataInterpreter.GetStringNotNull(indexColumn, IndexColumn.Name);
  863. // Check if any data column with proper name (search for columns with given schema, table and name)
  864. DataRow candidate = FindColumn(indexName);
  865. // If column was not found, need to change column name for index column.
  866. if (candidate == null)
  867. indexColumn[IndexColumn.Name] = newName;
  868. }
  869. // If column is primary key, should be index column in index PRIMARY
  870. if (DataInterpreter.GetSqlBool(column, Column.IsPrimaryKey).IsTrue)
  871. IncludeInPrimaryKey(newName);
  872. // If column is not primary key, exclude it from primary key index
  873. if (DataInterpreter.GetSqlBool(column, Column.IsPrimaryKey).IsFalse)
  874. ExcludeFromPrimaryKey(newName);
  875. // If default value is null, reset nullable flag (only for not auto increment columns)
  876. if (!DataInterpreter.IsNotNull(column, Column.Default)
  877. && DataInterpreter.GetSqlBool(column, Column.IsAutoIncrement).IsFalse)
  878. {
  879. object oldDefault = null;
  880. // If previous default value is stored and it is not null, reset allow nulls flag
  881. if (columnDefaults.TryGetValue(DataInterpreter.GetStringNotNull(column, Column.Name), out oldDefault)
  882. && oldDefault != null)
  883. DataInterpreter.SetValueIfChanged(column, Column.Nullable, DataInterpreter.True);
  884. }
  885. // If not null flag set, reset default value to empty string (only for not auto increment columns)
  886. if (DataInterpreter.GetSqlBool(column, Column.Nullable).IsFalse
  887. && !DataInterpreter.IsNotNull(column, Column.Default)
  888. && DataInterpreter.GetSqlBool(column, Column.IsAutoIncrement).IsFalse)
  889. SetDefault(column);
  890. // Stores old default
  891. columnDefaults[DataInterpreter.GetStringNotNull(column, Column.Name)]
  892. = DataInterpreter.GetString(column, Column.Default);
  893. }
  894. /// <summary>
  895. /// Includes given column into primary key.
  896. /// </summary>
  897. /// <param name="newName">Name of column to include in primary key.</param>
  898. private void IncludeInPrimaryKey(string newName)
  899. {
  900. // Create primary key index if not created yet
  901. if (FindIndex(IndexDescriptor.PRIMARY) == null)
  902. {
  903. DataRow primaryKey = Indexes.NewRow();
  904. primaryKey[Index.Name] = IndexDescriptor.PRIMARY;
  905. primaryKey[Index.IndexKind] = IndexDescriptor.PRIMARY;
  906. Indexes.Rows.Add(primaryKey);
  907. }
  908. // Extract exists primary key columns
  909. DataRow[] columns = DataInterpreter.Select(
  910. IndexColumns,
  911. IndexColumn.Index,
  912. IndexDescriptor.PRIMARY);
  913. // Create primary key column if not exists
  914. if (FindIndexColumn(IndexDescriptor.PRIMARY, newName) == null)
  915. {
  916. DataRow pkColumn = IndexColumns.NewRow();
  917. pkColumn[IndexColumn.Name] = newName;
  918. pkColumn[IndexColumn.Index] = IndexDescriptor.PRIMARY;
  919. // Index ordinals are one-based
  920. pkColumn[IndexColumn.Ordinal] = GetMaximumOrdinal(columns);
  921. IndexColumns.Rows.Add(pkColumn);
  922. }
  923. }
  924. /// <summary>
  925. /// Excludes given column from primary key.
  926. /// </summary>
  927. /// <param name="newName">Name of column to exclude from primary key.</param>
  928. private void ExcludeFromPrimaryKey(string newName)
  929. {
  930. // Delete related primary key column
  931. DataRow pkColumn = FindIndexColumn(IndexDescriptor.PRIMARY, newName);
  932. if (pkColumn != null)
  933. pkColumn.Delete();
  934. // If no more primary key columns, delete primary key index
  935. DataRow[] pkColumns = DataInterpreter.Select(IndexColumns, IndexColumn.Index, IndexDescriptor.PRIMARY);
  936. if (pkColumns == null || pkColumns.Length <= 0)
  937. {
  938. // Delete primary key, if any
  939. DataRow primaryKey = FindIndex(IndexDescriptor.PRIMARY);
  940. if (primaryKey != null)
  941. primaryKey.Delete();
  942. }
  943. }
  944. /// <summary>
  945. /// Returns maximum ordinal value for the given index columns.
  946. /// </summary>
  947. /// <param name="columns">Array with index columns to process.</param>
  948. /// <returns>Returns maximum ordinal value for the given index columns.</returns>
  949. private static Int64 GetMaximumOrdinal(DataRow[] columns)
  950. {
  951. if (columns == null)
  952. return 1;
  953. Int64 result = columns.Length + 1;
  954. foreach (DataRow column in columns)
  955. if (DataInterpreter.GetInt(column, IndexColumn.Ordinal) >= result)
  956. result = (Int64)DataInterpreter.GetInt(column, IndexColumn.Ordinal) + 1;
  957. return result;
  958. }
  959. /// <summary>
  960. /// Completes definition of the command.
  961. /// </summary>
  962. /// <param name="newColumn">Command data row to complete.</param>
  963. private void CompleteNewCollumn(DataRow newColumn)
  964. {
  965. // Extract column name, if any
  966. string columnName = DataInterpreter.GetString(newColumn, Column.Name);
  967. // If column name is given and type is not, try to find out default options
  968. if (!String.IsNullOrEmpty(columnName)
  969. && !DataInterpreter.IsNotEmptyString(newColumn, Column.MySqlType))
  970. {
  971. // If column ends with ID, let it be integer, otherwise, let it be varchar
  972. if (columnName.EndsWith(Resources.ID_Column_Name, StringComparison.CurrentCulture))
  973. {
  974. newColumn[Column.MySqlType] = TableDescriptor.DefaultIntType;
  975. newColumn[Column.Unsigned] = DataInterpreter.True;
  976. }
  977. else
  978. newColumn[Column.MySqlType] = TableDescriptor.DefaultCharType;
  979. // If column is ID and table have not primary key yet, let column be primary key
  980. if (columnName.Equals(Resources.ID_Column_Name, StringComparison.CurrentCulture)
  981. && !HasPrimaryKey)
  982. {
  983. newColumn[Column.IsAutoIncrement] = DataInterpreter.True;
  984. newColumn[Column.IsPrimaryKey] = DataInterpreter.True;
  985. }
  986. else
  987. {
  988. // Set default value if columns is not marked as autoincrement
  989. SetDefault(newColumn);
  990. }
  991. // Let column be not nullabale
  992. newColumn[Column.Nullable] = DataInterpreter.False;
  993. }
  994. columnDefaults[DataInterpreter.GetStringNotNull(newColumn, Column.Name)]
  995. = DataInterpreter.GetString(newColumn, Column.Default);
  996. }
  997. /// <summary>
  998. /// Handles column deletion and deletes related foreign key columns.
  999. /// </summary>
  1000. /// <param name="sender">Event sender, unused.</param>
  1001. /// <param name="e">Detailed information about event.</param>
  1002. private void OnColumnDeleted(object sender, DataRowChangeEventArgs e)
  1003. {
  1004. // Iterate through foreign keys columns
  1005. string fkName;
  1006. foreach (DataRow fkColumn in ForeignKeysColumns.Select())
  1007. {
  1008. // Need to skip deleted columns if any
  1009. if (fkColumn.RowState == DataRowState.Deleted)
  1010. continue;
  1011. fkName = DataInterpreter.GetStringNotNull(fkColumn, ForeignKeyColumn.Name);
  1012. // Check if any data column with proper name (search for columns with given schema, table and name)
  1013. DataRow candidate = FindColumn(fkName);
  1014. // If column was not found, need to delete this foreign key column.
  1015. if (candidate == null)
  1016. fkColumn.Delete();
  1017. }
  1018. // Iterate through indexes columns
  1019. string indexName;
  1020. foreach (DataRow indexColumn in IndexColumns.Select())
  1021. {
  1022. // Need to skip deleted columns if any
  1023. if (indexColumn.RowState == DataRowState.Deleted)
  1024. continue;
  1025. indexName = DataInterpreter.GetStringNotNull(indexColumn, IndexColumn.Name);
  1026. // Check if any data column with proper name (search for columns with given schema, table and name)
  1027. DataRow candidate = FindColumn(indexName);
  1028. // If column was not found, need to delete this index column.
  1029. if (candidate == null)
  1030. indexColumn.Delete()…

Large files files are truncated, but you can click here to view the full file