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.

173 lines
5.4 KiB

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