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.

368 lines
9.5 KiB

4 years ago
  1. #if MYSQL_6_9
  2. // Copyright © 2009, 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 Externals.MySql.Data.MySqlClient.Properties;
  27. using Externals.MySql.Data.Types;
  28. using System.Diagnostics;
  29. using System.Collections.Generic;
  30. namespace Externals.MySql.Data.MySqlClient
  31. {
  32. internal class ResultSet
  33. {
  34. private Driver driver;
  35. private bool hasRows;
  36. private bool[] uaFieldsUsed;
  37. private MySqlField[] fields;
  38. private IMySqlValue[] values;
  39. private Dictionary<string, int> fieldHashCS;
  40. private Dictionary<string, int> fieldHashCI;
  41. private int rowIndex;
  42. private bool readDone;
  43. private bool isSequential;
  44. private int seqIndex;
  45. private bool isOutputParameters;
  46. private int affectedRows;
  47. private long insertedId;
  48. private int statementId;
  49. private int totalRows;
  50. private int skippedRows;
  51. private bool cached;
  52. private List<IMySqlValue[]> cachedValues;
  53. public ResultSet(int affectedRows, long insertedId)
  54. {
  55. this.affectedRows = affectedRows;
  56. this.insertedId = insertedId;
  57. readDone = true;
  58. }
  59. public ResultSet(Driver d, int statementId, int numCols)
  60. {
  61. affectedRows = -1;
  62. insertedId = -1;
  63. driver = d;
  64. this.statementId = statementId;
  65. rowIndex = -1;
  66. LoadColumns(numCols);
  67. isOutputParameters = IsOutputParameterResultSet();
  68. hasRows = GetNextRow();
  69. readDone = !hasRows;
  70. }
  71. #region Properties
  72. public bool HasRows
  73. {
  74. get { return hasRows; }
  75. }
  76. public int Size
  77. {
  78. get { return fields == null ? 0 : fields.Length; }
  79. }
  80. public MySqlField[] Fields
  81. {
  82. get { return fields; }
  83. }
  84. public IMySqlValue[] Values
  85. {
  86. get { return values; }
  87. }
  88. public bool IsOutputParameters
  89. {
  90. get { return isOutputParameters; }
  91. set { isOutputParameters = value; }
  92. }
  93. public int AffectedRows
  94. {
  95. get { return affectedRows; }
  96. }
  97. public long InsertedId
  98. {
  99. get { return insertedId; }
  100. }
  101. public int TotalRows
  102. {
  103. get { return totalRows; }
  104. }
  105. public int SkippedRows
  106. {
  107. get { return skippedRows; }
  108. }
  109. public bool Cached
  110. {
  111. get { return cached; }
  112. set
  113. {
  114. cached = value;
  115. if (cached && cachedValues == null)
  116. cachedValues = new List<IMySqlValue[]>();
  117. }
  118. }
  119. #endregion
  120. /// <summary>
  121. /// return the ordinal for the given column name
  122. /// </summary>
  123. /// <param name="name"></param>
  124. /// <returns></returns>
  125. public int GetOrdinal(string name)
  126. {
  127. // first we try a quick hash lookup
  128. int ordinal;
  129. if (fieldHashCS.TryGetValue(name, out ordinal))
  130. return ordinal;
  131. // ok that failed so we use our CI hash
  132. if (fieldHashCI.TryGetValue( name, out ordinal ))
  133. return ordinal;
  134. // Throw an exception if the ordinal cannot be found.
  135. throw new IndexOutOfRangeException(
  136. String.Format(Resources.CouldNotFindColumnName, name));
  137. }
  138. /// <summary>
  139. /// Retrieve the value as the given column index
  140. /// </summary>
  141. /// <param name="index">The column value to retrieve</param>
  142. /// <returns>The value as the given column</returns>
  143. public IMySqlValue this[int index]
  144. {
  145. get
  146. {
  147. if (rowIndex < 0)
  148. throw new MySqlException(Resources.AttemptToAccessBeforeRead);
  149. // keep count of how many columns we have left to access
  150. uaFieldsUsed[index] = true;
  151. if (isSequential && index != seqIndex)
  152. {
  153. if (index < seqIndex)
  154. throw new MySqlException(Resources.ReadingPriorColumnUsingSeqAccess);
  155. while (seqIndex < (index - 1))
  156. driver.SkipColumnValue(values[++seqIndex]);
  157. values[index] = driver.ReadColumnValue(index, fields[index], values[index]);
  158. seqIndex = index;
  159. }
  160. return values[index];
  161. }
  162. }
  163. private bool GetNextRow()
  164. {
  165. bool fetched = driver.FetchDataRow(statementId, Size);
  166. if (fetched)
  167. totalRows++;
  168. return fetched;
  169. }
  170. public bool NextRow(CommandBehavior behavior)
  171. {
  172. if (readDone)
  173. {
  174. if (Cached) return CachedNextRow(behavior);
  175. return false;
  176. }
  177. if ((behavior & CommandBehavior.SingleRow) != 0 && rowIndex == 0)
  178. return false;
  179. isSequential = (behavior & CommandBehavior.SequentialAccess) != 0;
  180. seqIndex = -1;
  181. // if we are at row index >= 0 then we need to fetch the data row and load it
  182. if (rowIndex >= 0)
  183. {
  184. bool fetched = false;
  185. try
  186. {
  187. fetched = GetNextRow();
  188. }
  189. catch (MySqlException ex)
  190. {
  191. if (ex.IsQueryAborted)
  192. {
  193. // avoid hanging on Close()
  194. readDone = true;
  195. }
  196. throw;
  197. }
  198. if (!fetched)
  199. {
  200. readDone = true;
  201. return false;
  202. }
  203. }
  204. if (!isSequential) ReadColumnData(false);
  205. rowIndex++;
  206. return true;
  207. }
  208. private bool CachedNextRow(CommandBehavior behavior)
  209. {
  210. if ((behavior & CommandBehavior.SingleRow) != 0 && rowIndex == 0)
  211. return false;
  212. if (rowIndex == (totalRows - 1)) return false;
  213. rowIndex++;
  214. values = cachedValues[rowIndex];
  215. return true;
  216. }
  217. /// <summary>
  218. /// Closes the current resultset, dumping any data still on the wire
  219. /// </summary>
  220. public void Close()
  221. {
  222. if (!readDone)
  223. {
  224. // if we have rows but the user didn't read the first one then mark it as skipped
  225. if (HasRows && rowIndex == -1)
  226. skippedRows++;
  227. try
  228. {
  229. while (driver.IsOpen && driver.SkipDataRow())
  230. {
  231. totalRows++;
  232. skippedRows++;
  233. }
  234. }
  235. catch (System.IO.IOException)
  236. {
  237. // it is ok to eat IO exceptions here, we just want to
  238. // close the result set
  239. }
  240. readDone = true;
  241. }
  242. else if (driver == null)
  243. CacheClose();
  244. driver = null;
  245. if (Cached) CacheReset();
  246. }
  247. private void CacheClose()
  248. {
  249. skippedRows = totalRows - rowIndex - 1;
  250. }
  251. private void CacheReset()
  252. {
  253. if (!Cached) return;
  254. rowIndex = -1;
  255. affectedRows = -1;
  256. insertedId = -1;
  257. skippedRows = 0;
  258. }
  259. public bool FieldRead(int index)
  260. {
  261. Debug.Assert(Size > index);
  262. return uaFieldsUsed[index];
  263. }
  264. public void SetValueObject(int i, IMySqlValue valueObject)
  265. {
  266. Debug.Assert(values != null);
  267. Debug.Assert(i < values.Length);
  268. values[i] = valueObject;
  269. }
  270. private bool IsOutputParameterResultSet()
  271. {
  272. if (driver.HasStatus(ServerStatusFlags.OutputParameters)) return true;
  273. if (fields.Length == 0) return false;
  274. for (int x = 0; x < fields.Length; x++)
  275. if (!fields[x].ColumnName.StartsWith("@" + StoredProcedure.ParameterPrefix, StringComparison.OrdinalIgnoreCase)) return false;
  276. return true;
  277. }
  278. /// <summary>
  279. /// Loads the column metadata for the current resultset
  280. /// </summary>
  281. private void LoadColumns(int numCols)
  282. {
  283. fields = driver.GetColumns(numCols);
  284. values = new IMySqlValue[numCols];
  285. uaFieldsUsed = new bool[numCols];
  286. fieldHashCS = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
  287. fieldHashCI = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
  288. for (int i = 0; i < fields.Length; i++)
  289. {
  290. string columnName = fields[i].ColumnName;
  291. if (!fieldHashCS.ContainsKey(columnName))
  292. fieldHashCS.Add(columnName, i);
  293. if (!fieldHashCI.ContainsKey(columnName))
  294. fieldHashCI.Add(columnName, i);
  295. values[i] = fields[i].GetValueObject();
  296. }
  297. }
  298. private void ReadColumnData(bool outputParms)
  299. {
  300. for (int i = 0; i < Size; i++)
  301. values[i] = driver.ReadColumnValue(i, fields[i], values[i]);
  302. // if we are caching then we need to save a copy of this row of data values
  303. if (Cached)
  304. cachedValues.Add((IMySqlValue[])values.Clone());
  305. // we don't need to worry about caching the following since you won't have output
  306. // params with TableDirect commands
  307. if (outputParms)
  308. {
  309. bool rowExists = driver.FetchDataRow(statementId, fields.Length);
  310. rowIndex = 0;
  311. if (rowExists)
  312. throw new MySqlException(Resources.MoreThanOneOPRow);
  313. }
  314. }
  315. }
  316. }
  317. #endif