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.

171 lines
6.0 KiB

4 years ago
  1. #if MYSQL_6_10
  2. // Copyright ?2004, 2016, 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.Collections.Generic;
  25. using System.Globalization;
  26. using System.Text;
  27. using Externals.MySql.Data.MySqlClient;
  28. namespace Externals.MySql.Data.MySqlClient
  29. {
  30. internal class ProcedureCacheEntry
  31. {
  32. public MySqlSchemaCollection procedure;
  33. public MySqlSchemaCollection parameters;
  34. }
  35. internal class ProcedureCache
  36. {
  37. private readonly Dictionary<int, ProcedureCacheEntry> _procHash;
  38. private readonly Queue<int> _hashQueue;
  39. private readonly int _maxSize;
  40. public ProcedureCache(int size)
  41. {
  42. _maxSize = size;
  43. _hashQueue = new Queue<int>(_maxSize);
  44. _procHash = new Dictionary<int, ProcedureCacheEntry>(_maxSize);
  45. }
  46. public ProcedureCacheEntry GetProcedure(MySqlConnection conn, string spName, string cacheKey)
  47. {
  48. ProcedureCacheEntry proc = null;
  49. if (cacheKey != null)
  50. {
  51. int hash = cacheKey.GetHashCode();
  52. lock (_procHash)
  53. {
  54. _procHash.TryGetValue(hash, out proc);
  55. }
  56. }
  57. if (proc == null)
  58. {
  59. proc = AddNew(conn, spName);
  60. conn.PerfMonitor.AddHardProcedureQuery();
  61. if (conn.Settings.Logging)
  62. MySqlTrace.LogInformation(conn.ServerThread,
  63. String.Format(Resources.HardProcQuery, spName));
  64. }
  65. else
  66. {
  67. conn.PerfMonitor.AddSoftProcedureQuery();
  68. if (conn.Settings.Logging)
  69. MySqlTrace.LogInformation(conn.ServerThread,
  70. String.Format(Resources.SoftProcQuery, spName));
  71. }
  72. return proc;
  73. }
  74. internal string GetCacheKey(string spName, ProcedureCacheEntry proc)
  75. {
  76. string retValue = String.Empty;
  77. StringBuilder key = new StringBuilder(spName);
  78. key.Append("(");
  79. string delimiter = "";
  80. if (proc.parameters != null)
  81. {
  82. foreach (MySqlSchemaRow row in proc.parameters.Rows)
  83. {
  84. if (row["ORDINAL_POSITION"].Equals(0))
  85. retValue = "?=";
  86. else
  87. {
  88. key.AppendFormat(CultureInfo.InvariantCulture, "{0}?", delimiter);
  89. delimiter = ",";
  90. }
  91. }
  92. }
  93. key.Append(")");
  94. return retValue + key.ToString();
  95. }
  96. private ProcedureCacheEntry AddNew(MySqlConnection connection, string spName)
  97. {
  98. ProcedureCacheEntry procData = GetProcData(connection, spName);
  99. if (_maxSize <= 0) return procData;
  100. string cacheKey = GetCacheKey(spName, procData);
  101. int hash = cacheKey.GetHashCode();
  102. lock (_procHash)
  103. {
  104. if (_procHash.Keys.Count >= _maxSize)
  105. TrimHash();
  106. if (!_procHash.ContainsKey(hash))
  107. {
  108. _procHash[hash] = procData;
  109. _hashQueue.Enqueue(hash);
  110. }
  111. }
  112. return procData;
  113. }
  114. private void TrimHash()
  115. {
  116. int oldestHash = _hashQueue.Dequeue();
  117. _procHash.Remove(oldestHash);
  118. }
  119. private static ProcedureCacheEntry GetProcData(MySqlConnection connection, string spName)
  120. {
  121. string schema = String.Empty;
  122. string name = spName;
  123. int dotIndex = spName.IndexOf(".");
  124. if (dotIndex != -1)
  125. {
  126. schema = spName.Substring(0, dotIndex);
  127. name = spName.Substring(dotIndex + 1, spName.Length - dotIndex - 1);
  128. }
  129. string[] restrictions = new string[4];
  130. restrictions[1] = schema.Length > 0 ? schema : connection.CurrentDatabase();
  131. restrictions[2] = name;
  132. MySqlSchemaCollection proc = connection.GetSchemaCollection("procedures", restrictions);
  133. if (proc.Rows.Count > 1)
  134. throw new MySqlException(Resources.ProcAndFuncSameName);
  135. if (proc.Rows.Count == 0)
  136. throw new MySqlException(String.Format(Resources.InvalidProcName, name, schema));
  137. ProcedureCacheEntry entry = new ProcedureCacheEntry();
  138. entry.procedure = proc;
  139. // we don't use GetSchema here because that would cause another
  140. // query of procedures and we don't need that since we already
  141. // know the procedure we care about.
  142. ISSchemaProvider isp = new ISSchemaProvider(connection);
  143. string[] rest = isp.CleanRestrictions(restrictions);
  144. MySqlSchemaCollection parameters = isp.GetProcedureParameters(rest, proc);
  145. entry.parameters = parameters;
  146. return entry;
  147. }
  148. }
  149. }
  150. #endif