You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1101 lines
49 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. #if MYSQL_6_10
  2. // Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
  3. //
  4. // MySQL Connector/NET is licensed under the terms of the GPLv2
  5. // <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
  6. // MySQL Connectors. There are special exceptions to the terms and
  7. // conditions of the GPLv2 as it is applied to this software, see the
  8. // FLOSS License Exception
  9. // <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
  10. //
  11. // This program is free software; you can redistribute it and/or modify
  12. // it under the terms of the GNU General Public License as published
  13. // by the Free Software Foundation; version 2 of the License.
  14. //
  15. // This program is distributed in the hope that it will be useful, but
  16. // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  17. // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  18. // for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License along
  21. // with this program; if not, write to the Free Software Foundation, Inc.,
  22. // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  23. using System;
  24. using System.Globalization;
  25. using System.IO;
  26. using System.Reflection;
  27. using System.Text;
  28. using System.Text.RegularExpressions;
  29. using System.Collections.Generic;
  30. using System.Data;
  31. using System.Data.Common;
  32. using Externals.MySql.Data.Common;
  33. using Externals.MySql.Data.Types;
  34. namespace Externals.MySql.Data.MySqlClient
  35. {
  36. internal class SchemaProvider
  37. {
  38. protected MySqlConnection connection;
  39. public static string MetaCollection = "MetaDataCollections";
  40. public SchemaProvider(MySqlConnection connectionToUse)
  41. {
  42. connection = connectionToUse;
  43. }
  44. public virtual MySqlSchemaCollection GetSchema(string collection, String[] restrictions)
  45. {
  46. if (connection.State != ConnectionState.Open)
  47. throw new MySqlException("GetSchema can only be called on an open connection.");
  48. collection = StringUtility.ToUpperInvariant(collection);
  49. MySqlSchemaCollection c = GetSchemaInternal(collection, restrictions);
  50. if (c == null)
  51. throw new ArgumentException("Invalid collection name");
  52. return c;
  53. }
  54. public virtual MySqlSchemaCollection GetDatabases(string[] restrictions)
  55. {
  56. Regex regex = null;
  57. int caseSetting = Int32.Parse(connection.driver.Property("lower_case_table_names"), CultureInfo.InvariantCulture);
  58. string sql = "SHOW DATABASES";
  59. // if lower_case_table_names is zero, then case lookup should be sensitive
  60. // so we can use LIKE to do the matching.
  61. if (caseSetting == 0)
  62. {
  63. if (restrictions != null && restrictions.Length >= 1)
  64. sql = sql + " LIKE '" + restrictions[0] + "'";
  65. }
  66. MySqlSchemaCollection c = QueryCollection("Databases", sql);
  67. if (caseSetting != 0 && restrictions != null && restrictions.Length >= 1 && restrictions[0] != null)
  68. regex = new Regex(restrictions[0], RegexOptions.IgnoreCase);
  69. MySqlSchemaCollection c2 = new MySqlSchemaCollection("Databases");
  70. c2.AddColumn("CATALOG_NAME", typeof(string));
  71. c2.AddColumn("SCHEMA_NAME", typeof(string));
  72. foreach (MySqlSchemaRow row in c.Rows)
  73. {
  74. if (regex != null && !regex.Match(row[0].ToString()).Success) continue;
  75. MySqlSchemaRow newRow = c2.AddRow();
  76. newRow[1] = row[0];
  77. }
  78. return c2;
  79. }
  80. public virtual MySqlSchemaCollection GetTables(string[] restrictions)
  81. {
  82. MySqlSchemaCollection c = new MySqlSchemaCollection("Tables");
  83. c.AddColumn("TABLE_CATALOG", typeof(string));
  84. c.AddColumn("TABLE_SCHEMA", typeof(string));
  85. c.AddColumn("TABLE_NAME", typeof(string));
  86. c.AddColumn("TABLE_TYPE", typeof(string));
  87. c.AddColumn("ENGINE", typeof(string));
  88. c.AddColumn("VERSION", typeof(ulong));
  89. c.AddColumn("ROW_FORMAT", typeof(string));
  90. c.AddColumn("TABLE_ROWS", typeof(ulong));
  91. c.AddColumn("AVG_ROW_LENGTH", typeof(ulong));
  92. c.AddColumn("DATA_LENGTH", typeof(ulong));
  93. c.AddColumn("MAX_DATA_LENGTH", typeof(ulong));
  94. c.AddColumn("INDEX_LENGTH", typeof(ulong));
  95. c.AddColumn("DATA_FREE", typeof(ulong));
  96. c.AddColumn("AUTO_INCREMENT", typeof(ulong));
  97. c.AddColumn("CREATE_TIME", typeof(DateTime));
  98. c.AddColumn("UPDATE_TIME", typeof(DateTime));
  99. c.AddColumn("CHECK_TIME", typeof(DateTime));
  100. c.AddColumn("TABLE_COLLATION", typeof(string));
  101. c.AddColumn("CHECKSUM", typeof(ulong));
  102. c.AddColumn("CREATE_OPTIONS", typeof(string));
  103. c.AddColumn("TABLE_COMMENT", typeof(string));
  104. // we have to new up a new restriction array here since
  105. // GetDatabases takes the database in the first slot
  106. string[] dbRestriction = new string[4];
  107. if (restrictions != null && restrictions.Length >= 2)
  108. dbRestriction[0] = restrictions[1];
  109. MySqlSchemaCollection databases = GetDatabases(dbRestriction);
  110. if (restrictions != null)
  111. Array.Copy(restrictions, dbRestriction,
  112. Math.Min(dbRestriction.Length, restrictions.Length));
  113. foreach (MySqlSchemaRow row in databases.Rows)
  114. {
  115. dbRestriction[1] = row["SCHEMA_NAME"].ToString();
  116. FindTables(c, dbRestriction);
  117. }
  118. return c;
  119. }
  120. protected void QuoteDefaultValues(MySqlSchemaCollection schemaCollection)
  121. {
  122. if (schemaCollection == null) return;
  123. if (!schemaCollection.ContainsColumn("COLUMN_DEFAULT")) return;
  124. foreach (MySqlSchemaRow row in schemaCollection.Rows)
  125. {
  126. object defaultValue = row["COLUMN_DEFAULT"];
  127. if (MetaData.IsTextType(row["DATA_TYPE"].ToString()))
  128. row["COLUMN_DEFAULT"] = String.Format("{0}", defaultValue);
  129. }
  130. }
  131. public virtual MySqlSchemaCollection GetColumns(string[] restrictions)
  132. {
  133. MySqlSchemaCollection c = new MySqlSchemaCollection("Columns");
  134. c.AddColumn("TABLE_CATALOG", typeof(string));
  135. c.AddColumn("TABLE_SCHEMA", typeof(string));
  136. c.AddColumn("TABLE_NAME", typeof(string));
  137. c.AddColumn("COLUMN_NAME", typeof(string));
  138. c.AddColumn("ORDINAL_POSITION", typeof(ulong));
  139. c.AddColumn("COLUMN_DEFAULT", typeof(string));
  140. c.AddColumn("IS_NULLABLE", typeof(string));
  141. c.AddColumn("DATA_TYPE", typeof(string));
  142. c.AddColumn("CHARACTER_MAXIMUM_LENGTH", typeof(ulong));
  143. c.AddColumn("CHARACTER_OCTET_LENGTH", typeof(ulong));
  144. c.AddColumn("NUMERIC_PRECISION", typeof(ulong));
  145. c.AddColumn("NUMERIC_SCALE", typeof(ulong));
  146. c.AddColumn("CHARACTER_SET_NAME", typeof(string));
  147. c.AddColumn("COLLATION_NAME", typeof(string));
  148. c.AddColumn("COLUMN_TYPE", typeof(string));
  149. c.AddColumn("COLUMN_KEY", typeof(string));
  150. c.AddColumn("EXTRA", typeof(string));
  151. c.AddColumn("PRIVILEGES", typeof(string));
  152. c.AddColumn("COLUMN_COMMENT", typeof(string));
  153. c.AddColumn("GENERATION_EXPRESSION", typeof(string));
  154. // we don't allow restricting on table type here
  155. string columnName = null;
  156. if (restrictions != null && restrictions.Length == 4)
  157. {
  158. columnName = restrictions[3];
  159. restrictions[3] = null;
  160. }
  161. MySqlSchemaCollection tables = GetTables(restrictions);
  162. foreach (MySqlSchemaRow row in tables.Rows)
  163. LoadTableColumns(c, row["TABLE_SCHEMA"].ToString(),
  164. row["TABLE_NAME"].ToString(), columnName);
  165. QuoteDefaultValues(c);
  166. return c;
  167. }
  168. private void LoadTableColumns(MySqlSchemaCollection schemaCollection, string schema,
  169. string tableName, string columnRestriction)
  170. {
  171. string sql = String.Format("SHOW FULL COLUMNS FROM `{0}`.`{1}`",
  172. schema, tableName);
  173. MySqlCommand cmd = new MySqlCommand(sql, connection);
  174. int pos = 1;
  175. using (MySqlDataReader reader = cmd.ExecuteReader())
  176. {
  177. while (reader.Read())
  178. {
  179. string colName = reader.GetString(0);
  180. if (columnRestriction != null && colName != columnRestriction)
  181. continue;
  182. MySqlSchemaRow row = schemaCollection.AddRow();
  183. row["TABLE_CATALOG"] = DBNull.Value;
  184. row["TABLE_SCHEMA"] = schema;
  185. row["TABLE_NAME"] = tableName;
  186. row["COLUMN_NAME"] = colName;
  187. row["ORDINAL_POSITION"] = pos++;
  188. row["COLUMN_DEFAULT"] = reader.GetValue(5);
  189. row["IS_NULLABLE"] = reader.GetString(3);
  190. row["DATA_TYPE"] = reader.GetString(1);
  191. row["CHARACTER_MAXIMUM_LENGTH"] = DBNull.Value;
  192. row["CHARACTER_OCTET_LENGTH"] = DBNull.Value;
  193. row["NUMERIC_PRECISION"] = DBNull.Value;
  194. row["NUMERIC_SCALE"] = DBNull.Value;
  195. row["CHARACTER_SET_NAME"] = reader.GetValue(2);
  196. row["COLLATION_NAME"] = row["CHARACTER_SET_NAME"];
  197. row["COLUMN_TYPE"] = reader.GetString(1);
  198. row["COLUMN_KEY"] = reader.GetString(4);
  199. row["EXTRA"] = reader.GetString(6);
  200. row["PRIVILEGES"] = reader.GetString(7);
  201. row["COLUMN_COMMENT"] = reader.GetString(8);
  202. row["GENERATION_EXPRESION"] = reader.GetString(6).Contains("VIRTUAL") ? reader.GetString(9) : string.Empty;
  203. ParseColumnRow(row);
  204. }
  205. }
  206. }
  207. private static void ParseColumnRow(MySqlSchemaRow row)
  208. {
  209. // first parse the character set name
  210. string charset = row["CHARACTER_SET_NAME"].ToString();
  211. int index = charset.IndexOf('_');
  212. if (index != -1)
  213. row["CHARACTER_SET_NAME"] = charset.Substring(0, index);
  214. // now parse the data type
  215. string dataType = row["DATA_TYPE"].ToString();
  216. index = dataType.IndexOf('(');
  217. if (index == -1)
  218. return;
  219. row["DATA_TYPE"] = dataType.Substring(0, index);
  220. int stop = dataType.IndexOf(')', index);
  221. string dataLen = dataType.Substring(index + 1, stop - (index + 1));
  222. string lowerType = row["DATA_TYPE"].ToString().ToLower();
  223. if (lowerType == "char" || lowerType == "varchar")
  224. row["CHARACTER_MAXIMUM_LENGTH"] = dataLen;
  225. else if (lowerType == "real" || lowerType == "decimal")
  226. {
  227. string[] lenparts = dataLen.Split(new char[] { ',' });
  228. row["NUMERIC_PRECISION"] = lenparts[0];
  229. if (lenparts.Length == 2)
  230. row["NUMERIC_SCALE"] = lenparts[1];
  231. }
  232. }
  233. public virtual MySqlSchemaCollection GetIndexes(string[] restrictions)
  234. {
  235. MySqlSchemaCollection dt = new MySqlSchemaCollection("Indexes");
  236. dt.AddColumn("INDEX_CATALOG", typeof(string));
  237. dt.AddColumn("INDEX_SCHEMA", typeof(string));
  238. dt.AddColumn("INDEX_NAME", typeof(string));
  239. dt.AddColumn("TABLE_NAME", typeof(string));
  240. dt.AddColumn("UNIQUE", typeof(bool));
  241. dt.AddColumn("PRIMARY", typeof(bool));
  242. dt.AddColumn("TYPE", typeof(string));
  243. dt.AddColumn("COMMENT", typeof(string));
  244. // Get the list of tables first
  245. int max = restrictions?.Length ?? 4;
  246. string[] tableRestrictions = new string[Math.Max(max, 4)];
  247. restrictions?.CopyTo(tableRestrictions, 0);
  248. tableRestrictions[3] = "BASE TABLE";
  249. MySqlSchemaCollection tables = GetTables(tableRestrictions);
  250. foreach (MySqlSchemaRow table in tables.Rows)
  251. {
  252. string sql = String.Format("SHOW INDEX FROM `{0}`.`{1}`",
  253. MySqlHelper.DoubleQuoteString((string)table["TABLE_SCHEMA"]),
  254. MySqlHelper.DoubleQuoteString((string)table["TABLE_NAME"]));
  255. MySqlSchemaCollection indexes = QueryCollection("indexes", sql);
  256. foreach (MySqlSchemaRow index in indexes.Rows)
  257. {
  258. if (1 != (connection.driver.Version.isAtLeast(8, 0, 1) ?
  259. (uint)index["SEQ_IN_INDEX"] :
  260. (long)index["SEQ_IN_INDEX"]))
  261. continue;
  262. if (restrictions != null && restrictions.Length == 4 &&
  263. restrictions[3] != null &&
  264. !index["KEY_NAME"].Equals(restrictions[3])) continue;
  265. MySqlSchemaRow row = dt.AddRow();
  266. row["INDEX_CATALOG"] = null;
  267. row["INDEX_SCHEMA"] = table["TABLE_SCHEMA"];
  268. row["INDEX_NAME"] = index["KEY_NAME"];
  269. row["TABLE_NAME"] = index["TABLE"];
  270. row["UNIQUE"] = connection.driver.Version.isAtLeast(8, 0, 1) ?
  271. Convert.ToInt64(index["NON_UNIQUE"]) == 0 :
  272. (long)index["NON_UNIQUE"] == 0;
  273. row["PRIMARY"] = index["KEY_NAME"].Equals("PRIMARY");
  274. row["TYPE"] = index["INDEX_TYPE"];
  275. row["COMMENT"] = index["COMMENT"];
  276. }
  277. }
  278. return dt;
  279. }
  280. public virtual MySqlSchemaCollection GetIndexColumns(string[] restrictions)
  281. {
  282. MySqlSchemaCollection dt = new MySqlSchemaCollection("IndexColumns");
  283. dt.AddColumn("INDEX_CATALOG", typeof(string));
  284. dt.AddColumn("INDEX_SCHEMA", typeof(string));
  285. dt.AddColumn("INDEX_NAME", typeof(string));
  286. dt.AddColumn("TABLE_NAME", typeof(string));
  287. dt.AddColumn("COLUMN_NAME", typeof(string));
  288. dt.AddColumn("ORDINAL_POSITION", typeof(int));
  289. dt.AddColumn("SORT_ORDER", typeof(string));
  290. int max = restrictions == null ? 4 : restrictions.Length;
  291. string[] tableRestrictions = new string[Math.Max(max, 4)];
  292. if (restrictions != null)
  293. restrictions.CopyTo(tableRestrictions, 0);
  294. tableRestrictions[3] = "BASE TABLE";
  295. MySqlSchemaCollection tables = GetTables(tableRestrictions);
  296. foreach (MySqlSchemaRow table in tables.Rows)
  297. {
  298. string sql = String.Format("SHOW INDEX FROM `{0}`.`{1}`",
  299. table["TABLE_SCHEMA"], table["TABLE_NAME"]);
  300. MySqlCommand cmd = new MySqlCommand(sql, connection);
  301. using (MySqlDataReader reader = cmd.ExecuteReader())
  302. {
  303. while (reader.Read())
  304. {
  305. string key_name = GetString(reader, reader.GetOrdinal("KEY_NAME"));
  306. string col_name = GetString(reader, reader.GetOrdinal("COLUMN_NAME"));
  307. if (restrictions != null)
  308. {
  309. if (restrictions.Length >= 4 && restrictions[3] != null &&
  310. key_name != restrictions[3]) continue;
  311. if (restrictions.Length >= 5 && restrictions[4] != null &&
  312. col_name != restrictions[4]) continue;
  313. }
  314. MySqlSchemaRow row = dt.AddRow();
  315. row["INDEX_CATALOG"] = null;
  316. row["INDEX_SCHEMA"] = table["TABLE_SCHEMA"];
  317. row["INDEX_NAME"] = key_name;
  318. row["TABLE_NAME"] = GetString(reader, reader.GetOrdinal("TABLE"));
  319. row["COLUMN_NAME"] = col_name;
  320. row["ORDINAL_POSITION"] = reader.GetValue(reader.GetOrdinal("SEQ_IN_INDEX"));
  321. row["SORT_ORDER"] = reader.GetString("COLLATION");
  322. }
  323. }
  324. }
  325. return dt;
  326. }
  327. public virtual MySqlSchemaCollection GetForeignKeys(string[] restrictions)
  328. {
  329. MySqlSchemaCollection dt = new MySqlSchemaCollection("Foreign Keys");
  330. dt.AddColumn("CONSTRAINT_CATALOG", typeof(string));
  331. dt.AddColumn("CONSTRAINT_SCHEMA", typeof(string));
  332. dt.AddColumn("CONSTRAINT_NAME", typeof(string));
  333. dt.AddColumn("TABLE_CATALOG", typeof(string));
  334. dt.AddColumn("TABLE_SCHEMA", typeof(string));
  335. dt.AddColumn("TABLE_NAME", typeof(string));
  336. dt.AddColumn("MATCH_OPTION", typeof(string));
  337. dt.AddColumn("UPDATE_RULE", typeof(string));
  338. dt.AddColumn("DELETE_RULE", typeof(string));
  339. dt.AddColumn("REFERENCED_TABLE_CATALOG", typeof(string));
  340. dt.AddColumn("REFERENCED_TABLE_SCHEMA", typeof(string));
  341. dt.AddColumn("REFERENCED_TABLE_NAME", typeof(string));
  342. // first we use our restrictions to get a list of tables that should be
  343. // consulted. We save the keyname restriction since GetTables doesn't
  344. // understand that.
  345. string keyName = null;
  346. if (restrictions != null && restrictions.Length >= 4)
  347. {
  348. keyName = restrictions[3];
  349. restrictions[3] = null;
  350. }
  351. MySqlSchemaCollection tables = GetTables(restrictions);
  352. // now for each table retrieved, we call our helper function to
  353. // parse it's foreign keys
  354. foreach (MySqlSchemaRow table in tables.Rows)
  355. GetForeignKeysOnTable(dt, table, keyName, false);
  356. return dt;
  357. }
  358. public virtual MySqlSchemaCollection GetForeignKeyColumns(string[] restrictions)
  359. {
  360. MySqlSchemaCollection dt = new MySqlSchemaCollection("Foreign Keys");
  361. dt.AddColumn("CONSTRAINT_CATALOG", typeof(string));
  362. dt.AddColumn("CONSTRAINT_SCHEMA", typeof(string));
  363. dt.AddColumn("CONSTRAINT_NAME", typeof(string));
  364. dt.AddColumn("TABLE_CATALOG", typeof(string));
  365. dt.AddColumn("TABLE_SCHEMA", typeof(string));
  366. dt.AddColumn("TABLE_NAME", typeof(string));
  367. dt.AddColumn("COLUMN_NAME", typeof(string));
  368. dt.AddColumn("ORDINAL_POSITION", typeof(int));
  369. dt.AddColumn("REFERENCED_TABLE_CATALOG", typeof(string));
  370. dt.AddColumn("REFERENCED_TABLE_SCHEMA", typeof(string));
  371. dt.AddColumn("REFERENCED_TABLE_NAME", typeof(string));
  372. dt.AddColumn("REFERENCED_COLUMN_NAME", typeof(string));
  373. // first we use our restrictions to get a list of tables that should be
  374. // consulted. We save the keyname restriction since GetTables doesn't
  375. // understand that.
  376. string keyName = null;
  377. if (restrictions != null && restrictions.Length >= 4)
  378. {
  379. keyName = restrictions[3];
  380. restrictions[3] = null;
  381. }
  382. MySqlSchemaCollection tables = GetTables(restrictions);
  383. // now for each table retrieved, we call our helper function to
  384. // parse it's foreign keys
  385. foreach (MySqlSchemaRow table in tables.Rows)
  386. GetForeignKeysOnTable(dt, table, keyName, true);
  387. return dt;
  388. }
  389. private string GetSqlMode()
  390. {
  391. MySqlCommand cmd = new MySqlCommand("SELECT @@SQL_MODE", connection);
  392. return cmd.ExecuteScalar().ToString();
  393. }
  394. #region Foreign Key routines
  395. /// <summary>
  396. /// GetForeignKeysOnTable retrieves the foreign keys on the given table.
  397. /// Since MySQL supports foreign keys on versions prior to 5.0, we can't use
  398. /// information schema. MySQL also does not include any type of SHOW command
  399. /// for foreign keys so we have to resort to use SHOW CREATE TABLE and parsing
  400. /// the output.
  401. /// </summary>
  402. /// <param name="fkTable">The table to store the key info in.</param>
  403. /// <param name="tableToParse">The table to get the foeign key info for.</param>
  404. /// <param name="filterName">Only get foreign keys that match this name.</param>
  405. /// <param name="includeColumns">Should column information be included in the table.</param>
  406. private void GetForeignKeysOnTable(MySqlSchemaCollection fkTable, MySqlSchemaRow tableToParse,
  407. string filterName, bool includeColumns)
  408. {
  409. string sqlMode = GetSqlMode();
  410. if (filterName != null)
  411. filterName = StringUtility.ToLowerInvariant(filterName);
  412. string sql = string.Format("SHOW CREATE TABLE `{0}`.`{1}`",
  413. tableToParse["TABLE_SCHEMA"], tableToParse["TABLE_NAME"]);
  414. string lowerBody = null, body = null;
  415. MySqlCommand cmd = new MySqlCommand(sql, connection);
  416. using (MySqlDataReader reader = cmd.ExecuteReader())
  417. {
  418. reader.Read();
  419. body = reader.GetString(1);
  420. lowerBody = StringUtility.ToLowerInvariant(body);
  421. }
  422. MySqlTokenizer tokenizer = new MySqlTokenizer(lowerBody);
  423. tokenizer.AnsiQuotes = sqlMode.IndexOf("ANSI_QUOTES") != -1;
  424. tokenizer.BackslashEscapes = sqlMode.IndexOf("NO_BACKSLASH_ESCAPES") != -1;
  425. while (true)
  426. {
  427. string token = tokenizer.NextToken();
  428. // look for a starting contraint
  429. while (token != null && (token != "constraint" || tokenizer.Quoted))
  430. token = tokenizer.NextToken();
  431. if (token == null) break;
  432. ParseConstraint(fkTable, tableToParse, tokenizer, includeColumns);
  433. }
  434. }
  435. private static void ParseConstraint(MySqlSchemaCollection fkTable, MySqlSchemaRow table,
  436. MySqlTokenizer tokenizer, bool includeColumns)
  437. {
  438. string name = tokenizer.NextToken();
  439. MySqlSchemaRow row = fkTable.AddRow();
  440. // make sure this constraint is a FK
  441. string token = tokenizer.NextToken();
  442. if (token != "foreign" || tokenizer.Quoted)
  443. return;
  444. tokenizer.NextToken(); // read off the 'KEY' symbol
  445. tokenizer.NextToken(); // read off the '(' symbol
  446. row["CONSTRAINT_CATALOG"] = table["TABLE_CATALOG"];
  447. row["CONSTRAINT_SCHEMA"] = table["TABLE_SCHEMA"];
  448. row["TABLE_CATALOG"] = table["TABLE_CATALOG"];
  449. row["TABLE_SCHEMA"] = table["TABLE_SCHEMA"];
  450. row["TABLE_NAME"] = table["TABLE_NAME"];
  451. row["REFERENCED_TABLE_CATALOG"] = null;
  452. row["CONSTRAINT_NAME"] = name.Trim(new char[] { '\'', '`' });
  453. List<string> srcColumns = includeColumns ? ParseColumns(tokenizer) : null;
  454. // now look for the references section
  455. while (token != "references" || tokenizer.Quoted)
  456. token = tokenizer.NextToken();
  457. string target1 = tokenizer.NextToken();
  458. string target2 = tokenizer.NextToken();
  459. if (target2.StartsWith(".", StringComparison.Ordinal))
  460. {
  461. row["REFERENCED_TABLE_SCHEMA"] = target1;
  462. row["REFERENCED_TABLE_NAME"] = target2.Substring(1).Trim(new char[] { '\'', '`' });
  463. tokenizer.NextToken(); // read off the '('
  464. }
  465. else
  466. {
  467. row["REFERENCED_TABLE_SCHEMA"] = table["TABLE_SCHEMA"];
  468. row["REFERENCED_TABLE_NAME"] = target1.Substring(1).Trim(new char[] { '\'', '`' }); ;
  469. }
  470. // if we are supposed to include columns, read the target columns
  471. List<string> targetColumns = includeColumns ? ParseColumns(tokenizer) : null;
  472. if (includeColumns)
  473. ProcessColumns(fkTable, row, srcColumns, targetColumns);
  474. else
  475. fkTable.Rows.Add(row);
  476. }
  477. private static List<string> ParseColumns(MySqlTokenizer tokenizer)
  478. {
  479. List<string> sc = new List<string>();
  480. string token = tokenizer.NextToken();
  481. while (token != ")")
  482. {
  483. if (token != ",")
  484. sc.Add(token);
  485. token = tokenizer.NextToken();
  486. }
  487. return sc;
  488. }
  489. private static void ProcessColumns(MySqlSchemaCollection fkTable, MySqlSchemaRow row, List<string> srcColumns, List<string> targetColumns)
  490. {
  491. for (int i = 0; i < srcColumns.Count; i++)
  492. {
  493. MySqlSchemaRow newRow = fkTable.AddRow();
  494. row.CopyRow(newRow);
  495. newRow["COLUMN_NAME"] = srcColumns[i];
  496. newRow["ORDINAL_POSITION"] = i;
  497. newRow["REFERENCED_COLUMN_NAME"] = targetColumns[i];
  498. fkTable.Rows.Add(newRow);
  499. }
  500. }
  501. #endregion
  502. public virtual MySqlSchemaCollection GetUsers(string[] restrictions)
  503. {
  504. StringBuilder sb = new StringBuilder("SELECT Host, User FROM mysql.user");
  505. if (restrictions != null && restrictions.Length > 0)
  506. sb.AppendFormat(CultureInfo.InvariantCulture, " WHERE User LIKE '{0}'", restrictions[0]);
  507. MySqlSchemaCollection c = QueryCollection("Users", sb.ToString());
  508. c.Columns[0].Name = "HOST";
  509. c.Columns[1].Name = "USERNAME";
  510. return c;
  511. }
  512. public virtual MySqlSchemaCollection GetProcedures(string[] restrictions)
  513. {
  514. MySqlSchemaCollection dt = new MySqlSchemaCollection("Procedures");
  515. dt.AddColumn("SPECIFIC_NAME", typeof(string));
  516. dt.AddColumn("ROUTINE_CATALOG", typeof(string));
  517. dt.AddColumn("ROUTINE_SCHEMA", typeof(string));
  518. dt.AddColumn("ROUTINE_NAME", typeof(string));
  519. dt.AddColumn("ROUTINE_TYPE", typeof(string));
  520. dt.AddColumn("DTD_IDENTIFIER", typeof(string));
  521. dt.AddColumn("ROUTINE_BODY", typeof(string));
  522. dt.AddColumn("ROUTINE_DEFINITION", typeof(string));
  523. dt.AddColumn("EXTERNAL_NAME", typeof(string));
  524. dt.AddColumn("EXTERNAL_LANGUAGE", typeof(string));
  525. dt.AddColumn("PARAMETER_STYLE", typeof(string));
  526. dt.AddColumn("IS_DETERMINISTIC", typeof(string));
  527. dt.AddColumn("SQL_DATA_ACCESS", typeof(string));
  528. dt.AddColumn("SQL_PATH", typeof(string));
  529. dt.AddColumn("SECURITY_TYPE", typeof(string));
  530. dt.AddColumn("CREATED", typeof(DateTime));
  531. dt.AddColumn("LAST_ALTERED", typeof(DateTime));
  532. dt.AddColumn("SQL_MODE", typeof(string));
  533. dt.AddColumn("ROUTINE_COMMENT", typeof(string));
  534. dt.AddColumn("DEFINER", typeof(string));
  535. StringBuilder sql = null;
  536. if (connection.driver.Version.isAtLeast(8, 0, 1))
  537. {
  538. sql = new StringBuilder("SELECT * FROM information_schema.routines WHERE 1=1");
  539. if (restrictions != null)
  540. {
  541. if (restrictions.Length >= 2 && restrictions[1] != null)
  542. sql.AppendFormat(CultureInfo.InvariantCulture,
  543. " AND routine_schema LIKE '{0}'", restrictions[1]);
  544. if (restrictions.Length >= 3 && restrictions[2] != null)
  545. sql.AppendFormat(CultureInfo.InvariantCulture,
  546. " AND routine_name LIKE '{0}'", restrictions[2]);
  547. if (restrictions.Length >= 4 && restrictions[3] != null)
  548. sql.AppendFormat(CultureInfo.InvariantCulture,
  549. " AND routine_type LIKE '{0}'", restrictions[3]);
  550. }
  551. MySqlCommand cmd = new MySqlCommand(sql.ToString(), connection);
  552. using (MySqlDataReader reader = cmd.ExecuteReader())
  553. {
  554. while (reader.Read())
  555. {
  556. MySqlSchemaRow row = dt.AddRow();
  557. row["SPECIFIC_NAME"] = reader.GetString("specific_name");
  558. row["ROUTINE_CATALOG"] = DBNull.Value;
  559. row["ROUTINE_SCHEMA"] = reader.GetString("routine_schema");
  560. row["ROUTINE_NAME"] = reader.GetString("routine_name");
  561. string routineType = reader.GetString("routine_type");
  562. row["ROUTINE_TYPE"] = routineType;
  563. row["DTD_IDENTIFIER"] = StringUtility.ToLowerInvariant(routineType) == "function" ?
  564. (object)reader.GetString("DTD_IDENTIFIER") : DBNull.Value;
  565. row["ROUTINE_BODY"] = "SQL";
  566. row["ROUTINE_DEFINITION"] = reader.GetString("routine_definition");
  567. row["EXTERNAL_NAME"] = DBNull.Value;
  568. row["EXTERNAL_LANGUAGE"] = DBNull.Value;
  569. row["PARAMETER_STYLE"] = "SQL";
  570. row["IS_DETERMINISTIC"] = reader.GetString("is_deterministic");
  571. row["SQL_DATA_ACCESS"] = reader.GetString("sql_data_access");
  572. row["SQL_PATH"] = DBNull.Value;
  573. row["SECURITY_TYPE"] = reader.GetString("security_type");
  574. row["CREATED"] = reader.GetDateTime("created");
  575. row["LAST_ALTERED"] = reader.GetDateTime("last_altered");
  576. row["SQL_MODE"] = reader.GetString("sql_mode");
  577. row["ROUTINE_COMMENT"] = reader.GetString("routine_comment");
  578. row["DEFINER"] = reader.GetString("definer");
  579. }
  580. }
  581. }
  582. else
  583. {
  584. sql = new StringBuilder("SELECT * FROM mysql.proc WHERE 1=1");
  585. if (restrictions != null)
  586. {
  587. if (restrictions.Length >= 2 && restrictions[1] != null)
  588. sql.AppendFormat(CultureInfo.InvariantCulture,
  589. " AND db LIKE '{0}'", restrictions[1]);
  590. if (restrictions.Length >= 3 && restrictions[2] != null)
  591. sql.AppendFormat(CultureInfo.InvariantCulture,
  592. " AND name LIKE '{0}'", restrictions[2]);
  593. if (restrictions.Length >= 4 && restrictions[3] != null)
  594. sql.AppendFormat(CultureInfo.InvariantCulture,
  595. " AND type LIKE '{0}'", restrictions[3]);
  596. }
  597. MySqlCommand cmd = new MySqlCommand(sql.ToString(), connection);
  598. using (MySqlDataReader reader = cmd.ExecuteReader())
  599. {
  600. while (reader.Read())
  601. {
  602. MySqlSchemaRow row = dt.AddRow();
  603. row["SPECIFIC_NAME"] = reader.GetString("specific_name");
  604. row["ROUTINE_CATALOG"] = DBNull.Value;
  605. row["ROUTINE_SCHEMA"] = reader.GetString("db");
  606. row["ROUTINE_NAME"] = reader.GetString("name");
  607. string routineType = reader.GetString("type");
  608. row["ROUTINE_TYPE"] = routineType;
  609. row["DTD_IDENTIFIER"] = StringUtility.ToLowerInvariant(routineType) == "function" ?
  610. (object)reader.GetString("returns") : DBNull.Value;
  611. row["ROUTINE_BODY"] = "SQL";
  612. row["ROUTINE_DEFINITION"] = reader.GetString("body");
  613. row["EXTERNAL_NAME"] = DBNull.Value;
  614. row["EXTERNAL_LANGUAGE"] = DBNull.Value;
  615. row["PARAMETER_STYLE"] = "SQL";
  616. row["IS_DETERMINISTIC"] = reader.GetString("is_deterministic");
  617. row["SQL_DATA_ACCESS"] = reader.GetString("sql_data_access");
  618. row["SQL_PATH"] = DBNull.Value;
  619. row["SECURITY_TYPE"] = reader.GetString("security_type");
  620. row["CREATED"] = reader.GetDateTime("created");
  621. row["LAST_ALTERED"] = reader.GetDateTime("modified");
  622. row["SQL_MODE"] = reader.GetString("sql_mode");
  623. row["ROUTINE_COMMENT"] = reader.GetString("comment");
  624. row["DEFINER"] = reader.GetString("definer");
  625. }
  626. }
  627. }
  628. return dt;
  629. }
  630. protected virtual MySqlSchemaCollection GetCollections()
  631. {
  632. object[][] collections = {
  633. new object[] {"MetaDataCollections", 0, 0},
  634. new object[] {"DataSourceInformation", 0, 0},
  635. new object[] {"DataTypes", 0, 0},
  636. new object[] {"Restrictions", 0, 0},
  637. new object[] {"ReservedWords", 0, 0},
  638. new object[] {"Databases", 1, 1},
  639. new object[] {"Tables", 4, 2},
  640. new object[] {"Columns", 4, 4},
  641. new object[] {"Users", 1, 1},
  642. new object[] {"Foreign Keys", 4, 3},
  643. new object[] {"IndexColumns", 5, 4},
  644. new object[] {"Indexes", 4, 3},
  645. new object[] {"Foreign Key Columns", 4, 3},
  646. new object[] {"UDF", 1, 1}
  647. };
  648. MySqlSchemaCollection dt = new MySqlSchemaCollection("MetaDataCollections");
  649. dt.AddColumn("CollectionName", typeof(string));
  650. dt.AddColumn("NumberOfRestrictions", typeof(int));
  651. dt.AddColumn("NumberOfIdentifierParts", typeof(int));
  652. FillTable(dt, collections);
  653. return dt;
  654. }
  655. #if !NETSTANDARD1_3
  656. private MySqlSchemaCollection GetDataSourceInformation()
  657. {
  658. MySqlSchemaCollection dt = new MySqlSchemaCollection("DataSourceInformation");
  659. dt.AddColumn("CompositeIdentifierSeparatorPattern", typeof(string));
  660. dt.AddColumn("DataSourceProductName", typeof(string));
  661. dt.AddColumn("DataSourceProductVersion", typeof(string));
  662. dt.AddColumn("DataSourceProductVersionNormalized", typeof(string));
  663. dt.AddColumn("GroupByBehavior", typeof(GroupByBehavior));
  664. dt.AddColumn("IdentifierPattern", typeof(string));
  665. dt.AddColumn("IdentifierCase", typeof(IdentifierCase));
  666. dt.AddColumn("OrderByColumnsInSelect", typeof(bool));
  667. dt.AddColumn("ParameterMarkerFormat", typeof(string));
  668. dt.AddColumn("ParameterMarkerPattern", typeof(string));
  669. dt.AddColumn("ParameterNameMaxLength", typeof(int));
  670. dt.AddColumn("ParameterNamePattern", typeof(string));
  671. dt.AddColumn("QuotedIdentifierPattern", typeof(string));
  672. dt.AddColumn("QuotedIdentifierCase", typeof(IdentifierCase));
  673. dt.AddColumn("StatementSeparatorPattern", typeof(string));
  674. dt.AddColumn("StringLiteralPattern", typeof(string));
  675. dt.AddColumn("SupportedJoinOperators", typeof(SupportedJoinOperators));
  676. DBVersion v = connection.driver.Version;
  677. string ver = $"{v.Major:0}.{v.Minor:0}.{v.Build:0}";
  678. MySqlSchemaRow row = dt.AddRow();
  679. row["CompositeIdentifierSeparatorPattern"] = "\\.";
  680. row["DataSourceProductName"] = "MySQL";
  681. row["DataSourceProductVersion"] = connection.ServerVersion;
  682. row["DataSourceProductVersionNormalized"] = ver;
  683. row["GroupByBehavior"] = GroupByBehavior.Unrelated;
  684. row["IdentifierPattern"] =
  685. @"(^\`\p{Lo}\p{Lu}\p{Ll}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Nd}@$#_]*$)|(^\`[^\`\0]|\`\`+\`$)|(^\"" + [^\""\0]|\""\""+\""$)";
  686. row["IdentifierCase"] = IdentifierCase.Insensitive;
  687. row["OrderByColumnsInSelect"] = false;
  688. row["ParameterMarkerFormat"] = "{0}";
  689. row["ParameterMarkerPattern"] = "(@[A-Za-z0-9_$#]*)";
  690. row["ParameterNameMaxLength"] = 128;
  691. row["ParameterNamePattern"] =
  692. @"^[\p{Lo}\p{Lu}\p{Ll}\p{Lm}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Lm}\p{Nd}\uff3f_@#\$]*(?=\s+|$)";
  693. row["QuotedIdentifierPattern"] = @"(([^\`]|\`\`)*)";
  694. row["QuotedIdentifierCase"] = IdentifierCase.Sensitive;
  695. row["StatementSeparatorPattern"] = ";";
  696. row["StringLiteralPattern"] = "'(([^']|'')*)'";
  697. row["SupportedJoinOperators"] = 15;
  698. dt.Rows.Add(row);
  699. return dt;
  700. }
  701. #endif
  702. private static MySqlSchemaCollection GetDataTypes()
  703. {
  704. MySqlSchemaCollection dt = new MySqlSchemaCollection("DataTypes");
  705. dt.AddColumn("TypeName", typeof(string));
  706. dt.AddColumn("ProviderDbType", typeof(int));
  707. dt.AddColumn("ColumnSize", typeof(long));
  708. dt.AddColumn("CreateFormat", typeof(string));
  709. dt.AddColumn("CreateParameters", typeof(string));
  710. dt.AddColumn("DataType", typeof(string));
  711. dt.AddColumn("IsAutoincrementable", typeof(bool));
  712. dt.AddColumn("IsBestMatch", typeof(bool));
  713. dt.AddColumn("IsCaseSensitive", typeof(bool));
  714. dt.AddColumn("IsFixedLength", typeof(bool));
  715. dt.AddColumn("IsFixedPrecisionScale", typeof(bool));
  716. dt.AddColumn("IsLong", typeof(bool));
  717. dt.AddColumn("IsNullable", typeof(bool));
  718. dt.AddColumn("IsSearchable", typeof(bool));
  719. dt.AddColumn("IsSearchableWithLike", typeof(bool));
  720. dt.AddColumn("IsUnsigned", typeof(bool));
  721. dt.AddColumn("MaximumScale", typeof(short));
  722. dt.AddColumn("MinimumScale", typeof(short));
  723. dt.AddColumn("IsConcurrencyType", typeof(bool));
  724. dt.AddColumn("IsLiteralSupported", typeof(bool));
  725. dt.AddColumn("LiteralPrefix", typeof(string));
  726. dt.AddColumn("LiteralSuffix", typeof(string));
  727. dt.AddColumn("NativeDataType", typeof(string));
  728. // have each one of the types contribute to the datatypes collection
  729. MySqlBit.SetDSInfo(dt);
  730. MySqlBinary.SetDSInfo(dt);
  731. MySqlDateTime.SetDSInfo(dt);
  732. MySqlTimeSpan.SetDSInfo(dt);
  733. MySqlString.SetDSInfo(dt);
  734. MySqlDouble.SetDSInfo(dt);
  735. MySqlSingle.SetDSInfo(dt);
  736. MySqlByte.SetDSInfo(dt);
  737. MySqlInt16.SetDSInfo(dt);
  738. MySqlInt32.SetDSInfo(dt);
  739. MySqlInt64.SetDSInfo(dt);
  740. MySqlDecimal.SetDSInfo(dt);
  741. MySqlUByte.SetDSInfo(dt);
  742. MySqlUInt16.SetDSInfo(dt);
  743. MySqlUInt32.SetDSInfo(dt);
  744. MySqlUInt64.SetDSInfo(dt);
  745. return dt;
  746. }
  747. protected virtual MySqlSchemaCollection GetRestrictions()
  748. {
  749. object[][] restrictions = {
  750. new object[] {"Users", "Name", "", 0},
  751. new object[] {"Databases", "Name", "", 0},
  752. new object[] {"Tables", "Database", "", 0},
  753. new object[] {"Tables", "Schema", "", 1},
  754. new object[] {"Tables", "Table", "", 2},
  755. new object[] {"Tables", "TableType", "", 3},
  756. new object[] {"Columns", "Database", "", 0},
  757. new object[] {"Columns", "Schema", "", 1},
  758. new object[] {"Columns", "Table", "", 2},
  759. new object[] {"Columns", "Column", "", 3},
  760. new object[] {"Indexes", "Database", "", 0},
  761. new object[] {"Indexes", "Schema", "", 1},
  762. new object[] {"Indexes", "Table", "", 2},
  763. new object[] {"Indexes", "Name", "", 3},
  764. new object[] {"IndexColumns", "Database", "", 0},
  765. new object[] {"IndexColumns", "Schema", "", 1},
  766. new object[] {"IndexColumns", "Table", "", 2},
  767. new object[] {"IndexColumns", "ConstraintName", "", 3},
  768. new object[] {"IndexColumns", "Column", "", 4},
  769. new object[] {"Foreign Keys", "Database", "", 0},
  770. new object[] {"Foreign Keys", "Schema", "", 1},
  771. new object[] {"Foreign Keys", "Table", "", 2},
  772. new object[] {"Foreign Keys", "Constraint Name", "", 3},
  773. new object[] {"Foreign Key Columns", "Catalog", "", 0},
  774. new object[] {"Foreign Key Columns", "Schema", "", 1},
  775. new object[] {"Foreign Key Columns", "Table", "", 2},
  776. new object[] {"Foreign Key Columns", "Constraint Name", "", 3},
  777. new object[] {"UDF", "Name", "", 0}
  778. };
  779. MySqlSchemaCollection dt = new MySqlSchemaCollection("Restrictions");
  780. dt.AddColumn("CollectionName", typeof(string));
  781. dt.AddColumn("RestrictionName", typeof(string));
  782. dt.AddColumn("RestrictionDefault", typeof(string));
  783. dt.AddColumn("RestrictionNumber", typeof(int));
  784. FillTable(dt, restrictions);
  785. return dt;
  786. }
  787. private static MySqlSchemaCollection GetReservedWords()
  788. {
  789. MySqlSchemaCollection dt = new MySqlSchemaCollection("ReservedWords");
  790. #if !NETSTANDARD1_3
  791. dt.AddColumn(DbMetaDataColumnNames.ReservedWord, typeof(string));
  792. Stream str = Assembly.GetExecutingAssembly().GetManifestResourceStream(
  793. "Externals.MySql.Data.Properties.ReservedWords.txt");
  794. #else
  795. dt.AddColumn("ReservedWord", typeof(string));
  796. Stream str = typeof(SchemaProvider).GetTypeInfo().Assembly.GetManifestResourceStream("Externals.MySql.Data.Properties.ReservedWords.txt");
  797. #endif
  798. StreamReader sr = new StreamReader(str);
  799. string line = sr.ReadLine();
  800. while (line != null)
  801. {
  802. MySqlSchemaRow row = dt.AddRow();
  803. row[0] = line;
  804. line = sr.ReadLine();
  805. }
  806. sr.Dispose();
  807. return dt;
  808. }
  809. protected static void FillTable(MySqlSchemaCollection dt, object[][] data)
  810. {
  811. foreach (object[] dataItem in data)
  812. {
  813. MySqlSchemaRow row = dt.AddRow();
  814. for (int i = 0; i < dataItem.Length; i++)
  815. row[i] = dataItem[i];
  816. }
  817. }
  818. private void FindTables(MySqlSchemaCollection schema, string[] restrictions)
  819. {
  820. StringBuilder sql = new StringBuilder();
  821. StringBuilder where = new StringBuilder();
  822. sql.AppendFormat(CultureInfo.InvariantCulture,
  823. "SHOW TABLE STATUS FROM `{0}`", restrictions[1]);
  824. if (restrictions != null && restrictions.Length >= 3 &&
  825. restrictions[2] != null)
  826. where.AppendFormat(CultureInfo.InvariantCulture,
  827. " LIKE '{0}'", restrictions[2]);
  828. sql.Append(where.ToString());
  829. string table_type = restrictions[1].ToLower() == "information_schema"
  830. ?
  831. "SYSTEM VIEW"
  832. : "BASE TABLE";
  833. MySqlCommand cmd = new MySqlCommand(sql.ToString(), connection);
  834. using (MySqlDataReader reader = cmd.ExecuteReader())
  835. {
  836. while (reader.Read())
  837. {
  838. MySqlSchemaRow row = schema.AddRow();
  839. row["TABLE_CATALOG"] = null;
  840. row["TABLE_SCHEMA"] = restrictions[1];
  841. row["TABLE_NAME"] = reader.GetString(0);
  842. row["TABLE_TYPE"] = table_type;
  843. row["ENGINE"] = GetString(reader, 1);
  844. row["VERSION"] = reader.GetValue(2);
  845. row["ROW_FORMAT"] = GetString(reader, 3);
  846. row["TABLE_ROWS"] = reader.GetValue(4);
  847. row["AVG_ROW_LENGTH"] = reader.GetValue(5);
  848. row["DATA_LENGTH"] = reader.GetValue(6);
  849. row["MAX_DATA_LENGTH"] = reader.GetValue(7);
  850. row["INDEX_LENGTH"] = reader.GetValue(8);
  851. row["DATA_FREE"] = reader.GetValue(9);
  852. row["AUTO_INCREMENT"] = reader.GetValue(10);
  853. row["CREATE_TIME"] = reader.GetValue(11);
  854. row["UPDATE_TIME"] = reader.GetValue(12);
  855. row["CHECK_TIME"] = reader.GetValue(13);
  856. row["TABLE_COLLATION"] = GetString(reader, 14);
  857. row["CHECKSUM"] = reader.GetValue(15);
  858. row["CREATE_OPTIONS"] = GetString(reader, 16);
  859. row["TABLE_COMMENT"] = GetString(reader, 17);
  860. }
  861. }
  862. }
  863. private static string GetString(MySqlDataReader reader, int index)
  864. {
  865. if (reader.IsDBNull(index))
  866. return null;
  867. return reader.GetString(index);
  868. }
  869. public virtual MySqlSchemaCollection GetUDF(string[] restrictions)
  870. {
  871. string sql = "SELECT name,ret,dl FROM mysql.func";
  872. if (restrictions?.Length >= 1 && !String.IsNullOrEmpty(restrictions[0]))
  873. sql += $" WHERE name LIKE '{restrictions[0]}'";
  874. MySqlSchemaCollection dt = new MySqlSchemaCollection("User-defined Functions");
  875. dt.AddColumn("NAME", typeof(string));
  876. dt.AddColumn("RETURN_TYPE", typeof(int));
  877. dt.AddColumn("LIBRARY_NAME", typeof(string));
  878. MySqlCommand cmd = new MySqlCommand(sql, connection);
  879. try
  880. {
  881. using (MySqlDataReader reader = cmd.ExecuteReader())
  882. {
  883. while (reader.Read())
  884. {
  885. MySqlSchemaRow row = dt.AddRow();
  886. row[0] = reader.GetString(0);
  887. row[1] = reader.GetInt32(1);
  888. row[2] = reader.GetString(2);
  889. }
  890. }
  891. }
  892. catch (MySqlException ex)
  893. {
  894. if (ex.Number != (int)MySqlErrorCode.TableAccessDenied)
  895. throw;
  896. throw new MySqlException(Resources.UnableToEnumerateUDF, ex);
  897. }
  898. return dt;
  899. }
  900. protected virtual MySqlSchemaCollection GetSchemaInternal(string collection, string[] restrictions)
  901. {
  902. switch (collection)
  903. {
  904. // common collections
  905. case "METADATACOLLECTIONS":
  906. return GetCollections();
  907. #if !NETSTANDARD1_3
  908. case "DATASOURCEINFORMATION":
  909. return GetDataSourceInformation();
  910. #endif
  911. case "DATATYPES":
  912. return GetDataTypes();
  913. case "RESTRICTIONS":
  914. return GetRestrictions();
  915. case "RESERVEDWORDS":
  916. return GetReservedWords();
  917. // collections specific to our provider
  918. case "USERS":
  919. return GetUsers(restrictions);
  920. case "DATABASES":
  921. return GetDatabases(restrictions);
  922. case "UDF":
  923. return GetUDF(restrictions);
  924. }
  925. // if we have a current database and our users have
  926. // not specified a database, then default to the currently
  927. // selected one.
  928. if (restrictions == null)
  929. restrictions = new string[2];
  930. if (connection != null &&
  931. connection.Database != null &&
  932. connection.Database.Length > 0 &&
  933. restrictions.Length > 1 &&
  934. restrictions[1] == null)
  935. restrictions[1] = connection.Database;
  936. switch (collection)
  937. {
  938. case "TABLES":
  939. return GetTables(restrictions);
  940. case "COLUMNS":
  941. return GetColumns(restrictions);
  942. case "INDEXES":
  943. return GetIndexes(restrictions);
  944. case "INDEXCOLUMNS":
  945. return GetIndexColumns(restrictions);
  946. case "FOREIGN KEYS":
  947. return GetForeignKeys(restrictions);
  948. case "FOREIGN KEY COLUMNS":
  949. return GetForeignKeyColumns(restrictions);
  950. }
  951. return null;
  952. }
  953. internal string[] CleanRestrictions(string[] restrictionValues)
  954. {
  955. string[] restrictions = null;
  956. if (restrictionValues != null)
  957. {
  958. restrictions = (string[])restrictionValues.Clone();
  959. for (int x = 0; x < restrictions.Length; x++)
  960. {
  961. string s = restrictions[x];
  962. if (s == null) continue;
  963. restrictions[x] = s.Trim('`');
  964. }
  965. }
  966. return restrictions;
  967. }
  968. protected MySqlSchemaCollection QueryCollection(string name, string sql)
  969. {
  970. MySqlSchemaCollection c = new MySqlSchemaCollection(name);
  971. MySqlCommand cmd = new MySqlCommand(sql, connection);
  972. MySqlDataReader reader = cmd.ExecuteReader();
  973. for (int i = 0; i < reader.FieldCount; i++)
  974. c.AddColumn(reader.GetName(i), reader.GetFieldType(i));
  975. using (reader)
  976. {
  977. while (reader.Read())
  978. {
  979. MySqlSchemaRow row = c.AddRow();
  980. for (int i = 0; i < reader.FieldCount; i++)
  981. row[i] = reader.GetValue(i);
  982. }
  983. }
  984. return c;
  985. }
  986. }
  987. }
  988. #endif