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.

220 lines
6.9 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.Text;
  26. using System.Collections.Generic;
  27. using Externals.MySql.Data.MySqlClient.Properties;
  28. using System.Data;
  29. namespace Externals.MySql.Data.MySqlClient
  30. {
  31. /// <summary>
  32. /// Summary description for PreparedStatement.
  33. /// </summary>
  34. internal class PreparableStatement : Statement
  35. {
  36. private int executionCount;
  37. private int statementId;
  38. BitArray nullMap;
  39. List<MySqlParameter> parametersToSend = new List<MySqlParameter>();
  40. MySqlPacket packet;
  41. int dataPosition;
  42. int nullMapPosition;
  43. public PreparableStatement(MySqlCommand command, string text)
  44. : base(command, text)
  45. {
  46. }
  47. #region Properties
  48. public int ExecutionCount
  49. {
  50. get { return executionCount; }
  51. set { executionCount = value; }
  52. }
  53. public bool IsPrepared
  54. {
  55. get { return statementId > 0; }
  56. }
  57. public int StatementId
  58. {
  59. get { return statementId; }
  60. }
  61. #endregion
  62. public virtual void Prepare()
  63. {
  64. // strip out names from parameter markers
  65. string text;
  66. List<string> parameter_names = PrepareCommandText(out text);
  67. // ask our connection to send the prepare command
  68. MySqlField[] paramList = null;
  69. statementId = Driver.PrepareStatement(text, ref paramList);
  70. // now we need to assign our field names since we stripped them out
  71. // for the prepare
  72. for (int i = 0; i < parameter_names.Count; i++)
  73. {
  74. //paramList[i].ColumnName = (string) parameter_names[i];
  75. string parameterName = (string)parameter_names[i];
  76. MySqlParameter p = Parameters.GetParameterFlexible(parameterName, false);
  77. if (p == null)
  78. throw new InvalidOperationException(
  79. String.Format(Resources.ParameterNotFoundDuringPrepare, parameterName));
  80. p.Encoding = paramList[i].Encoding;
  81. parametersToSend.Add(p);
  82. }
  83. // now prepare our null map
  84. int numNullBytes = 0;
  85. if (paramList != null && paramList.Length > 0)
  86. {
  87. nullMap = new BitArray(paramList.Length);
  88. numNullBytes = (nullMap.Count + 7) / 8;
  89. }
  90. packet = new MySqlPacket(Driver.Encoding);
  91. // write out some values that do not change run to run
  92. packet.WriteByte(0);
  93. packet.WriteInteger(statementId, 4);
  94. packet.WriteByte((byte)0); // flags; always 0 for 4.1
  95. packet.WriteInteger(1, 4); // interation count; 1 for 4.1
  96. nullMapPosition = packet.Position;
  97. packet.Position += numNullBytes; // leave room for our null map
  98. packet.WriteByte(1); // rebound flag
  99. // write out the parameter types
  100. foreach (MySqlParameter p in parametersToSend)
  101. packet.WriteInteger(p.GetPSType(), 2);
  102. dataPosition = packet.Position;
  103. }
  104. public override void Execute()
  105. {
  106. // if we are not prepared, then call down to our base
  107. if (!IsPrepared)
  108. {
  109. base.Execute();
  110. return;
  111. }
  112. //TODO: support long data here
  113. // create our null bitmap
  114. // we check this because Mono doesn't ignore the case where nullMapBytes
  115. // is zero length.
  116. // if (nullMapBytes.Length > 0)
  117. // {
  118. // byte[] bits = packet.Buffer;
  119. // nullMap.CopyTo(bits,
  120. // nullMap.CopyTo(nullMapBytes, 0);
  121. // start constructing our packet
  122. // if (Parameters.Count > 0)
  123. // nullMap.CopyTo(packet.Buffer, nullMapPosition);
  124. //if (parameters != null && parameters.Count > 0)
  125. //else
  126. // packet.WriteByte( 0 );
  127. //TODO: only send rebound if parms change
  128. // now write out all non-null values
  129. packet.Position = dataPosition;
  130. for (int i = 0; i < parametersToSend.Count; i++)
  131. {
  132. MySqlParameter p = parametersToSend[i];
  133. nullMap[i] = (p.Value == DBNull.Value || p.Value == null) ||
  134. p.Direction == ParameterDirection.Output;
  135. if (nullMap[i]) continue;
  136. packet.Encoding = p.Encoding;
  137. p.Serialize(packet, true, Connection.Settings);
  138. }
  139. if (nullMap != null)
  140. nullMap.CopyTo(packet.Buffer, nullMapPosition);
  141. executionCount++;
  142. Driver.ExecuteStatement(packet);
  143. }
  144. public override bool ExecuteNext()
  145. {
  146. if (!IsPrepared)
  147. return base.ExecuteNext();
  148. return false;
  149. }
  150. /// <summary>
  151. /// Prepares CommandText for use with the Prepare method
  152. /// </summary>
  153. /// <returns>Command text stripped of all paramter names</returns>
  154. /// <remarks>
  155. /// Takes the output of TokenizeSql and creates a single string of SQL
  156. /// that only contains '?' markers for each parameter. It also creates
  157. /// the parameterMap array list that includes all the paramter names in the
  158. /// order they appeared in the SQL
  159. /// </remarks>
  160. private List<string> PrepareCommandText(out string stripped_sql)
  161. {
  162. StringBuilder newSQL = new StringBuilder();
  163. List<string> parameterMap = new List<string>();
  164. int startPos = 0;
  165. string sql = ResolvedCommandText;
  166. MySqlTokenizer tokenizer = new MySqlTokenizer(sql);
  167. string parameter = tokenizer.NextParameter();
  168. while (parameter != null)
  169. {
  170. if (parameter.IndexOf(StoredProcedure.ParameterPrefix) == -1)
  171. {
  172. newSQL.Append(sql.Substring(startPos, tokenizer.StartIndex - startPos));
  173. newSQL.Append("?");
  174. parameterMap.Add(parameter);
  175. startPos = tokenizer.StopIndex;
  176. }
  177. parameter = tokenizer.NextParameter();
  178. }
  179. newSQL.Append(sql.Substring(startPos));
  180. stripped_sql = newSQL.ToString();
  181. return parameterMap;
  182. }
  183. public virtual void CloseStatement()
  184. {
  185. if (!IsPrepared) return;
  186. Driver.CloseStatement(statementId);
  187. statementId = 0;
  188. }
  189. }
  190. }
  191. #endif