PageRenderTime 71ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/VisualStudio/Descriptors/TableDescriptor.cs

https://github.com/rykr/connector-net
C# | 556 lines | 340 code | 64 blank | 152 comment | 55 complexity | 35dc2597a52f0fedc77e62ad6aeb6f8f MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, MPL-2.0-no-copyleft-exception
  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 Table object descriptor
  18. */
  19. using System;
  20. using System.Collections.Generic;
  21. using System.Text;
  22. using System.Data;
  23. using MySql.Data.VisualStudio.Utils;
  24. using System.Diagnostics;
  25. using MySql.Data.VisualStudio.Properties;
  26. using System.Globalization;
  27. using System.Data.Common;
  28. namespace MySql.Data.VisualStudio.Descriptors
  29. {
  30. /// <summary>
  31. /// This is the Table object descriptor.
  32. /// </summary>
  33. [ObjectDescriptor(TableDescriptor.TypeName, typeof(TableDescriptor))]
  34. [IdLength(3)]
  35. public class TableDescriptor: ObjectDescriptor
  36. {
  37. /// <summary>
  38. /// Table object type name.
  39. /// </summary>
  40. public new const string TypeName = "Table";
  41. #region Enumerate SQL
  42. /// <summary>
  43. /// Table enumerate SQL template.
  44. /// </summary>
  45. protected new const string EnumerateSqlTemplate =
  46. "SELECT "
  47. + "t.TABLE_CATALOG, "
  48. + "t.TABLE_SCHEMA, "
  49. + "t.TABLE_NAME, "
  50. + "t.TABLE_TYPE, "
  51. + "t.`ENGINE`, "
  52. + "t.VERSION, "
  53. + "t.`ROW_FORMAT`, "
  54. + "t.TABLE_ROWS, "
  55. + "t.AVG_ROW_LENGTH, "
  56. + "t.DATA_LENGTH, "
  57. + "t.MAX_DATA_LENGTH, "
  58. + "t.INDEX_LENGTH, "
  59. + "t.DATA_FREE, "
  60. + "t.AUTO_INCREMENT, "
  61. + "t.CREATE_TIME, "
  62. + "t.UPDATE_TIME, "
  63. + "t.CHECK_TIME, "
  64. + "ca.CHARACTER_SET_NAME AS TABLE_CHARACTER_SET, "
  65. + "t.TABLE_COLLATION, "
  66. + "t.`CHECKSUM`, "
  67. + "t.CREATE_OPTIONS, "
  68. + "t.TABLE_COMMENT "
  69. + "FROM information_schema.`TABLES` t "
  70. + "LEFT JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY ca "
  71. + "ON t.TABLE_COLLATION = ca.COLLATION_NAME "
  72. + "WHERE TABLE_SCHEMA = {1} AND TABLE_NAME = {2} AND TABLE_TYPE = {3} "
  73. + "AND TABLE_TYPE != 'VIEW'";
  74. /// <summary>
  75. /// Table enumeration defaults.
  76. /// </summary>
  77. protected new static readonly string[] DefaultRestrictions =
  78. {
  79. "",
  80. "TABLE_SCHEMA",
  81. "TABLE_NAME",
  82. "TABLE_TYPE"
  83. };
  84. /// <summary>
  85. /// Table default sort fields.
  86. /// </summary>
  87. protected new const string DefaultSortString = "TABLE_SCHEMA, TABLE_NAME";
  88. #endregion
  89. #region Attributes
  90. /// <summary>
  91. /// List of known attributes for Table object
  92. /// </summary>
  93. public static new class Attributes
  94. {
  95. [Field(FieldType = TypeCode.String)]
  96. public const string Database = "TABLE_CATALOG";
  97. [Field(OptionName = SchemaField, FieldType = TypeCode.String)]
  98. [Identifier(IsSchema = true)]
  99. public const string Schema = "TABLE_SCHEMA";
  100. [Field(FieldType = TypeCode.String)]
  101. [Identifier(IsName=true)]
  102. public const string Name = "TABLE_NAME";
  103. [Field(FieldType = TypeCode.String)]
  104. public const string Type = "TABLE_TYPE";
  105. [Field(FieldType = TypeCode.String)]
  106. public const string Engine = "ENGINE";
  107. [Field(FieldType = TypeCode.Int64)]
  108. public const string Version = "VERSION";
  109. [Field(FieldType = TypeCode.String)]
  110. public const string RowFormat = "ROW_FORMAT";
  111. [Field(FieldType = TypeCode.Int64)]
  112. public const string TableRows = "TABLE_ROWS";
  113. [Field(FieldType = TypeCode.Int64)]
  114. public const string AverageRowLength = "AVG_ROW_LENGTH";
  115. [Field(FieldType = TypeCode.Int64)]
  116. public const string DataLength = "DATA_LENGTH";
  117. [Field(FieldType = TypeCode.Int64)]
  118. public const string MaxDataLength = "MAX_DATA_LENGTH";
  119. [Field(FieldType = TypeCode.Int64)]
  120. public const string IndexLength = "INDEX_LENGTH";
  121. [Field(FieldType = TypeCode.Int64)]
  122. public const string DataFree = "DATA_FREE";
  123. [Field(FieldType = TypeCode.Int64)]
  124. public const string AutoIncrement = "AUTO_INCREMENT";
  125. [Field(FieldType = TypeCode.DateTime)]
  126. public const string CreateDateTime = "CREATE_TIME";
  127. [Field(FieldType = TypeCode.DateTime)]
  128. public const string UpdateDateTime = "UPDATE_TIME";
  129. [Field(FieldType = TypeCode.DateTime)]
  130. public const string CheckDateTime = "CHECK_TIME";
  131. [Field(OptionName = CharacterSetField, FieldType = TypeCode.String)]
  132. public const string CharacterSet = "TABLE_CHARACTER_SET";
  133. [Field(FieldType = TypeCode.String)]
  134. public const string Collation = "TABLE_COLLATION";
  135. [Field(FieldType = TypeCode.Int64)]
  136. public const string Checksum = "CHECKSUM";
  137. [Field(FieldType = TypeCode.String)]
  138. [OptionString]
  139. public const string CreateOptions = "CREATE_OPTIONS";
  140. [Field(FieldType = TypeCode.String)]
  141. public const string Comments = "TABLE_COMMENT";
  142. [Field(OptionName = "avg_row_length", FieldType = TypeCode.Int64)]
  143. public const string AverageRowLengthField = "AverageRowLengthField";
  144. [Field(OptionName = "checksum", FieldType = TypeCode.Int64)]
  145. public const string ChecksumField = "ChecksumField";
  146. [Field(OptionName = "min_rows", FieldType = TypeCode.Int64)]
  147. public const string MinRows = "MinRowsField";
  148. [Field(OptionName = "row_format", FieldType = TypeCode.String)]
  149. public const string RowFormatField = "RowFormatField";
  150. [Field(OptionName = "max_rows", FieldType = TypeCode.Int64)]
  151. public const string MaxRows = "MaxRowsField";
  152. [Field(OptionName = ConnectionField, FieldType = TypeCode.String)]
  153. public const string Connection = "ConnectionField";
  154. [Field(OptionName = DataDirectoryField, FieldType = TypeCode.String)]
  155. public const string DataDirectory = "DataDirectoryField";
  156. [Field(OptionName = IndexDirectoryField, FieldType = TypeCode.String)]
  157. public const string IndexDirectory = "IndexDirectoryField";
  158. [Field(OptionName = "delay_key_write", FieldType = TypeCode.Int64)]
  159. public const string DelayKeyWrite = "DelayKeyWriteField";
  160. [Field(OptionName = "pack_keys", FieldType = TypeCode.String)]
  161. public const string PackKeys = "PackKeysField";
  162. [Field(OptionName = "password", FieldType = TypeCode.String)]
  163. public const string Password = "PasswordField";
  164. [Field(OptionName = InsertMethodField, FieldType = TypeCode.String)]
  165. public const string InsertMethod = "InsertMethodField";
  166. [Field(OptionName = UnionField, FieldType = TypeCode.String)]
  167. public const string Union = "UnionField";
  168. }
  169. #endregion
  170. #region Enumerate method
  171. /// <summary>
  172. /// Enumerates tables with given restrictions into DataTable.
  173. /// </summary>
  174. /// <param name="connection">The DataConnectionWrapper to be used for enumeration.</param>
  175. /// <param name="restrictions">The restrictions to be putted on the retrieved objects set.</param>
  176. /// <returns>
  177. /// Returns DataTable which contains all tables which satisfy given restrictions.
  178. /// </returns>
  179. public static DataTable Enumerate(DataConnectionWrapper connection, object[] restrictions)
  180. {
  181. if (connection == null)
  182. throw new ArgumentNullException("connection");
  183. return ObjectDescriptor.EnumerateObjects(connection, TypeName, restrictions);
  184. }
  185. #endregion
  186. #region Dropping
  187. /// <summary>
  188. /// Tables can be dropped. Returns true.
  189. /// </summary>
  190. public override bool CanBeDropped
  191. {
  192. get { return true; }
  193. }
  194. /// <summary>
  195. /// Returns DROP TABLE statement.
  196. /// </summary>
  197. /// <param name="identifier">Database object identifier.</param>
  198. /// <returns>Returns DROP TABLE statement.</returns>
  199. public override string BuildDropSql(object[] identifier)
  200. {
  201. if (identifier == null)
  202. throw new ArgumentNullException("identifier");
  203. if (identifier.Length != 3 || String.IsNullOrEmpty(identifier[1] as string) || String.IsNullOrEmpty(identifier[2] as string))
  204. throw new ArgumentException(
  205. String.Format(
  206. CultureInfo.CurrentCulture,
  207. Resources.Error_InvlaidIdentifier,
  208. identifier.Length,
  209. TypeName,
  210. 3),
  211. "id");
  212. // Build query
  213. StringBuilder query = new StringBuilder("DROP TABLE ");
  214. QueryBuilder.WriteIdentifier(identifier[1] as string, identifier[2] as string, query);
  215. return query.ToString();
  216. }
  217. #endregion
  218. #region Aditional constants
  219. /// <summary>
  220. /// Un-displayable name MRG_MyISAM
  221. /// </summary>
  222. public const string MRG_MyISAM = "MRG_MyISAM";
  223. /// <summary>
  224. /// Displayable name MERGE
  225. /// </summary>
  226. public const string MERGE = "MERGE";
  227. /// <summary>
  228. /// MEMORY engine name
  229. /// </summary>
  230. public const string MEMORY = "MEMORY";
  231. /// <summary>
  232. /// MyISAM engine name
  233. /// </summary>
  234. public const string MyISAM = "MyISAM";
  235. /// <summary>
  236. /// InnoDB engine name
  237. /// </summary>
  238. public const string InnoDB = "InnoDB";
  239. /// <summary>
  240. /// ARCHIVE engine name
  241. /// </summary>
  242. public const string ARCHIVE = "ARCHIVE";
  243. /// <summary>
  244. /// BDB engine name
  245. /// </summary>
  246. public const string BDB = "BDB";
  247. /// <summary>
  248. /// NDB engine name
  249. /// </summary>
  250. public const string NDB = "NDB";
  251. /// <summary>
  252. /// Default type for columns ends with ID
  253. /// </summary>
  254. public const string DefaultIntType = "int(10)";
  255. /// <summary>
  256. /// Default type for othe columns
  257. /// </summary>
  258. public const string DefaultCharType = "varchar(45)";
  259. #endregion
  260. #region Private constants
  261. private const string ConnectionQueryPart = "CONNECTION=";
  262. private const string ConnectionField = "connection";
  263. private const string InsertMethodQueryPart = "INSERT_METHOD=";
  264. private const string InsertMethodField = "insert_method";
  265. private const string UnionQueryPart = "UNION=";
  266. private const string UnionField = "union";
  267. private const string DataDirectoryQueryPart = "DATA DIRECTORY=";
  268. private const string DataDirectoryField = "data_directory";
  269. private const string IndexDirectoryQueryPart = "INDEX DIRECTORY=";
  270. private const string IndexDirectoryField = "index_directory";
  271. private const string CharacterSetField = "character_set";
  272. private const string SchemaField = "schema";
  273. private const string CreateTableColumn = "Create Table";
  274. #endregion
  275. #region Legacy MySQL version support
  276. /// <summary>
  277. /// Builds enumerate SQL query for object of this type with given restrictions. For legacy version uses
  278. /// SHOW TABLE STATUS instead INFORMATION_SCHEMA.
  279. /// </summary>
  280. /// <param name="connection">The DataConnectionWrapper to be used for enumeration.</param>
  281. /// <param name="restrictions">Restrictions to enumerated objects.</param>
  282. /// <param name="sort">Sort expression to use.</param>
  283. /// <returns>Enumerating SQL query string.</returns>
  284. protected override string BuildEnumerateSql(DataConnectionWrapper connection, object[] restrictions, string sort)
  285. {
  286. if (connection == null)
  287. throw new ArgumentNullException("connection");
  288. // Extract server version
  289. Version serverVersion = connection.ServerVersion;
  290. // For latest version just call base
  291. if (serverVersion == null || serverVersion.Major >= 5)
  292. return base.BuildEnumerateSql(connection, restrictions, sort);
  293. // Build SHOW TABLE STATUS
  294. StringBuilder query = new StringBuilder("SHOW TABLE STATUS");
  295. // If there is a restriction on schema, apply it
  296. if (restrictions != null && restrictions.Length >= 2 && !String.IsNullOrEmpty(restrictions[1] as string))
  297. {
  298. query.Append(" FROM ");
  299. QueryBuilder.WriteIdentifier(restrictions[1] as string, query);
  300. }
  301. // If there is a restriction on table, apply it
  302. if (restrictions != null && restrictions.Length >= 3)
  303. QueryBuilder.WriteIfNotEmptyString(restrictions[2], " LIKE ", query);
  304. // Return result
  305. return query.ToString();
  306. }
  307. /// <summary>
  308. /// Reads table with Database Objects which satisfy given restriction. Base implementation
  309. /// uses direct SQL query to the INFORMATION_SCHEMA.
  310. /// </summary>
  311. /// <param name="connection">The DataConnectionWrapper to be used for enumeration.</param>
  312. /// <param name="restrictions">The restrictions to be putted on the retrieved objects set.</param>
  313. /// <param name="sort">Sort expresion to append after ORDER BY clause.</param>
  314. /// <returns>Returns table with Database Objects which satisfy given restriction.</returns>
  315. protected override DataTable ReadTable(DataConnectionWrapper connection, object[] restrictions, string sort)
  316. {
  317. if (connection == null)
  318. throw new ArgumentNullException("connection");
  319. // Extract server version
  320. Version serverVersion = connection.ServerVersion;
  321. // For latest version just return base result
  322. if (serverVersion == null || serverVersion.Major >= 5)
  323. return base.ReadTable(connection, restrictions, sort);
  324. // Execute base method
  325. DataTable result;
  326. try
  327. {
  328. result = base.ReadTable(connection, restrictions, sort);
  329. }
  330. catch(DbException)
  331. {
  332. // This most probably meanes that table is not exists. Return empty table on this
  333. return new DataTable();
  334. }
  335. // If result is null, exit
  336. if (result == null)
  337. return null;
  338. // For legacy version rename columns
  339. RenameColumn("Name", Attributes.Name, result);
  340. RenameColumn("Version", Attributes.Version, result);
  341. RenameColumn("Row_format", Attributes.RowFormat, result);
  342. RenameColumn("Rows", Attributes.TableRows, result);
  343. RenameColumn("Avg_row_length", Attributes.AverageRowLength, result);
  344. RenameColumn("Data_length", Attributes.DataLength, result);
  345. RenameColumn("Max_data_length", Attributes.MaxDataLength, result);
  346. RenameColumn("Index_length", Attributes.IndexLength, result);
  347. RenameColumn("Data_free", Attributes.DataFree, result);
  348. RenameColumn("Auto_increment", Attributes.AutoIncrement, result);
  349. RenameColumn("Create_time", Attributes.CreateDateTime, result);
  350. RenameColumn("Update_time", Attributes.UpdateDateTime, result);
  351. RenameColumn("Check_time", Attributes.CheckDateTime, result);
  352. RenameColumn("Create_options", Attributes.CreateOptions, result);
  353. RenameColumn("Comment", Attributes.Comments, result);
  354. // Engine was called type before 4.1.2
  355. if (serverVersion < new Version(4, 1, 2))
  356. RenameColumn("Type", Attributes.Engine, result);
  357. else
  358. RenameColumn("Engine", Attributes.Engine, result);
  359. // Engine collation and checksum are implemented only in 4.1.1
  360. if (serverVersion < new Version(4, 1, 1))
  361. {
  362. result.Columns.Add(Attributes.Collation, typeof(string));
  363. result.Columns.Add(Attributes.Checksum, typeof(Int64));
  364. }
  365. else
  366. {
  367. RenameColumn("Collation", Attributes.Collation, result);
  368. RenameColumn("Checksum", Attributes.Checksum, result);
  369. }
  370. // Calculate schema name
  371. string schema;
  372. if (restrictions != null && restrictions.Length >= 2 && !String.IsNullOrEmpty(restrictions[1] as string))
  373. schema = restrictions[1] as string;
  374. else
  375. schema = connection.Schema;
  376. // Add catalog, schema and type column
  377. result.Columns.Add(Attributes.Database, typeof(string));
  378. result.Columns.Add(Attributes.Schema, typeof(string));
  379. result.Columns.Add(Attributes.Type, typeof(string));
  380. // Set schema and type name for each row
  381. foreach (DataRow table in result.Rows)
  382. {
  383. DataInterpreter.SetValueIfChanged(table, Attributes.Schema, schema);
  384. DataInterpreter.SetValueIfChanged(table, Attributes.Type, "BASE TABLE");
  385. }
  386. // Finaly, return result
  387. return result;
  388. }
  389. #endregion
  390. #region Aditional fields extracting
  391. /// <summary>
  392. /// Extracts field values for given DataRow. Base implementation simply uses Parser.
  393. /// </summary>
  394. /// <param name="connection">The DataConnectionWrapper to be used for enumeration.</param>
  395. /// <param name="row">DataRow to extract values.</param>
  396. /// <returns>Returns field values for given DataRow.</returns>
  397. protected override Dictionary<string, string> ExtractOptions(DataConnectionWrapper connection, DataRow row)
  398. {
  399. if (connection == null)
  400. throw new ArgumentNullException("connection");
  401. if (row == null)
  402. throw new ArgumentNullException("row");
  403. // Call to base
  404. Dictionary<string, string> result = base.ExtractOptions(connection, row);
  405. if (result == null)
  406. result = new Dictionary<string, string>();
  407. // Extract CREATE TABLE sql
  408. string createTableQuery = GetCreateTableQuery(connection, row);
  409. if (String.IsNullOrEmpty(createTableQuery))
  410. return result;
  411. // Extract a connection field
  412. Parser.ExtractAdvancedFieldUnquoted(result, createTableQuery, ConnectionQueryPart, ConnectionField);
  413. // Extract an insert method field
  414. Parser.ExtractAdvancedFieldToken(result, createTableQuery, InsertMethodQueryPart, InsertMethodField);
  415. // Extract a union field
  416. Parser.ExtractAdvancedFieldUnbraced(result, createTableQuery, UnionQueryPart, UnionField);
  417. // Extract the DATA_DIRECTORY field
  418. Parser.ExtractAdvancedFieldUnquoted(result, createTableQuery, DataDirectoryQueryPart, DataDirectoryField);
  419. // Extract the INDEX_DIRECTORY field
  420. Parser.ExtractAdvancedFieldUnquoted(result, createTableQuery, IndexDirectoryQueryPart, IndexDirectoryField);
  421. // For legacy version calculate character set
  422. // Extract server version
  423. Version serverVersion = connection.ServerVersion;
  424. // For MySQL greater then 4.1.1 we can calculate character set
  425. if (serverVersion != null && serverVersion.Major < 5
  426. && serverVersion >= new Version(4, 1, 1))
  427. {
  428. // Extract collation
  429. string collation = DataInterpreter.GetString(row, Attributes.Collation);
  430. if (!String.IsNullOrEmpty(collation))
  431. {
  432. string characterSet = connection.GetCharacterSetForCollation(collation);
  433. if (!String.IsNullOrEmpty(characterSet))
  434. result[CharacterSetField] = characterSet;
  435. }
  436. }
  437. // Return results
  438. return result;
  439. }
  440. /// <summary>
  441. /// Returns string with CREATE TABLE sql for this table.
  442. /// </summary>
  443. /// <param name="connection">Connection to use to execute query.</param>
  444. /// <param name="row">DataRow with information about table.</param>
  445. /// <returns>Returns string with CREATE TABLE sql for this table.</returns>
  446. private static string GetCreateTableQuery(DataConnectionWrapper connection, DataRow row)
  447. {
  448. // Extract schema and table name
  449. string schemaName = DataInterpreter.GetString(row, Attributes.Schema);
  450. string tableName = DataInterpreter.GetString(row, Attributes.Name);
  451. if (String.IsNullOrEmpty(schemaName) || String.IsNullOrEmpty(tableName))
  452. {
  453. Debug.Fail("Unable to get table or schema name");
  454. return String.Empty;
  455. }
  456. return GetCreateTableQuery(connection, schemaName, tableName);
  457. }
  458. /// <summary>
  459. /// Returns string with CREATE TABLE sql for table by gicen schema name and table name.
  460. /// </summary>
  461. /// <param name="connection">Connection to use to execute query.</param>
  462. /// <param name="schemaName">Name of table schema.</param>
  463. /// <param name="tableName">Name of the table.</param>
  464. /// <returns>Returns string with CREATE TABLE sql for this table.</returns>
  465. public static string GetCreateTableQuery(DataConnectionWrapper connection, string schemaName, string tableName)
  466. {
  467. if (connection == null)
  468. throw new ArgumentNullException("connection");
  469. if (String.IsNullOrEmpty(schemaName))
  470. throw new ArgumentException(Resources.Error_EmptyString, "schemaName");
  471. if (String.IsNullOrEmpty(tableName))
  472. throw new ArgumentException(Resources.Error_EmptyString, "tableName");
  473. // Build SHOW CREATE TABLE table
  474. StringBuilder query = new StringBuilder();
  475. query.Append("SHOW CREATE TABLE ");
  476. QueryBuilder.WriteIdentifier(schemaName, tableName, query);
  477. // Execute query and check table
  478. IDataReader reader = connection.ExecuteReader(query.ToString(),
  479. false, CommandBehavior.Default);
  480. if (reader == null || !reader.Read())
  481. {
  482. Debug.Fail("Failed to read CREATE TABLE query!");
  483. return String.Empty;
  484. }
  485. // Extract result
  486. string result = reader.GetString(1);
  487. reader.Close();
  488. return result;
  489. }
  490. #endregion
  491. }
  492. }