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.

1050 lines
32 KiB

  1. // Copyright ?2004, 2018 Oracle and/or its affiliates. All rights reserved.
  2. //
  3. // MySQL Connector/NET is licensed under the terms of the GPLv2
  4. // <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
  5. // MySQL Connectors. There are special exceptions to the terms and
  6. // conditions of the GPLv2 as it is applied to this software, see the
  7. // FLOSS License Exception
  8. // <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
  9. //
  10. // This program is free software; you can redistribute it and/or modify
  11. // it under the terms of the GNU General Public License as published
  12. // by the Free Software Foundation; version 2 of the License.
  13. //
  14. // This program is distributed in the hope that it will be useful, but
  15. // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16. // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  17. // for more details.
  18. //
  19. // You should have received a copy of the GNU General Public License along
  20. // with this program; if not, write to the Free Software Foundation, Inc.,
  21. // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  22. #if NET40 || NET461
  23. using System;
  24. using System.Data;
  25. using System.Data.Common;
  26. using System.Collections;
  27. using Externals.MySql.Data.Types;
  28. using System.Collections.Generic;
  29. using System.Globalization;
  30. using Externals.MySql.Data.MySqlClient.Properties;
  31. using Externals.MySql.Data.Common;
  32. using Externals.MySql.Data.MySqlClient;
  33. using System.Threading;
  34. namespace Externals.MySql.Data.MySqlClient
  35. {
  36. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/ClassSummary/*'/>
  37. internal sealed partial class MySqlDataReader : IDisposable
  38. {
  39. // The DataReader should always be open when returned to the user.
  40. private bool isOpen = true;
  41. private CommandBehavior commandBehavior;
  42. private MySqlCommand command;
  43. internal long affectedRows;
  44. internal Driver driver;
  45. private PreparableStatement statement;
  46. private ResultSet resultSet;
  47. private bool disposed = false;
  48. // Used in special circumstances with stored procs to avoid exceptions from DbDataAdapter
  49. // If set, AffectedRows returns -1 instead of 0.
  50. private bool disableZeroAffectedRows;
  51. /*
  52. * Keep track of the connection in order to implement the
  53. * CommandBehavior.CloseConnection flag. A null reference means
  54. * normal behavior (do not automatically close).
  55. */
  56. private MySqlConnection connection;
  57. /*
  58. * Because the user should not be able to directly create a
  59. * DataReader object, the constructors are
  60. * marked as internal.
  61. */
  62. internal MySqlDataReader(MySqlCommand cmd, PreparableStatement statement, CommandBehavior behavior)
  63. {
  64. this.command = cmd;
  65. connection = (MySqlConnection)command.Connection;
  66. commandBehavior = behavior;
  67. driver = connection.driver;
  68. affectedRows = -1;
  69. this.statement = statement;
  70. if (cmd.CommandType == CommandType.StoredProcedure
  71. && cmd.UpdatedRowSource == UpdateRowSource.FirstReturnedRecord
  72. )
  73. {
  74. disableZeroAffectedRows = true;
  75. }
  76. }
  77. #region Properties
  78. internal PreparableStatement Statement
  79. {
  80. get { return statement; }
  81. }
  82. internal MySqlCommand Command
  83. {
  84. get { return command; }
  85. }
  86. internal ResultSet ResultSet
  87. {
  88. get { return resultSet; }
  89. }
  90. internal CommandBehavior CommandBehavior
  91. {
  92. get { return commandBehavior; }
  93. }
  94. /// <summary>
  95. /// Gets the number of columns in the current row.
  96. /// </summary>
  97. public override int FieldCount
  98. {
  99. get { return resultSet == null ? 0 : resultSet.Size; }
  100. }
  101. /// <summary>
  102. /// Gets a value indicating whether the MySqlDataReader contains one or more rows.
  103. /// </summary>
  104. public override bool HasRows
  105. {
  106. get { return resultSet == null ? false : resultSet.HasRows; }
  107. }
  108. /// <summary>
  109. /// Gets a value indicating whether the data reader is closed.
  110. /// </summary>
  111. public override bool IsClosed
  112. {
  113. get { return !isOpen; }
  114. }
  115. /// <summary>
  116. /// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement.
  117. /// </summary>
  118. public override int RecordsAffected
  119. {
  120. // RecordsAffected returns the number of rows affected in batch
  121. // statments from insert/delete/update statments. This property
  122. // is not completely accurate until .Close() has been called.
  123. get
  124. {
  125. if (disableZeroAffectedRows)
  126. {
  127. // In special case of updating stored procedure called from
  128. // within data adapter, we return -1 to avoid exceptions
  129. // (s. Bug#54895)
  130. if (affectedRows == 0)
  131. return -1;
  132. }
  133. return (int)affectedRows;
  134. }
  135. }
  136. /// <summary>
  137. /// Overloaded. Gets the value of a column in its native format.
  138. /// In C#, this property is the indexer for the MySqlDataReader class.
  139. /// </summary>
  140. public override object this[int i]
  141. {
  142. get { return GetValue(i); }
  143. }
  144. /// <summary>
  145. /// Gets the value of a column in its native format.
  146. /// [C#] In C#, this property is the indexer for the MySqlDataReader class.
  147. /// </summary>
  148. public override object this[String name]
  149. {
  150. // Look up the ordinal and return
  151. // the value at that position.
  152. get { return this[GetOrdinal(name)]; }
  153. }
  154. #endregion
  155. /// <summary>
  156. /// Closes the MySqlDataReader object.
  157. /// </summary>
  158. public override void Close()
  159. {
  160. if (!isOpen) return;
  161. bool shouldCloseConnection = (commandBehavior & CommandBehavior.CloseConnection) != 0;
  162. CommandBehavior originalBehavior = commandBehavior;
  163. // clear all remaining resultsets
  164. try
  165. {
  166. // Temporarily change to Default behavior to allow NextResult to finish properly.
  167. if (!originalBehavior.Equals(CommandBehavior.SchemaOnly))
  168. commandBehavior = CommandBehavior.Default;
  169. while (NextResult()) { }
  170. }
  171. catch (MySqlException ex)
  172. {
  173. // Ignore aborted queries
  174. if (!ex.IsQueryAborted)
  175. {
  176. // ignore IO exceptions.
  177. // We are closing or disposing reader, and do not
  178. // want exception to be propagated to used. If socket is
  179. // is closed on the server side, next query will run into
  180. // IO exception. If reader is closed by GC, we also would
  181. // like to avoid any exception here.
  182. bool isIOException = false;
  183. for (Exception exception = ex; exception != null;
  184. exception = exception.InnerException)
  185. {
  186. if (exception is System.IO.IOException)
  187. {
  188. isIOException = true;
  189. break;
  190. }
  191. }
  192. if (!isIOException)
  193. {
  194. // Ordinary exception (neither IO nor query aborted)
  195. throw;
  196. }
  197. }
  198. }
  199. catch (System.IO.IOException)
  200. {
  201. // eat, on the same reason we eat IO exceptions wrapped into
  202. // MySqlExceptions reasons, described above.
  203. }
  204. finally
  205. {
  206. // always ensure internal reader is null (Bug #55558)
  207. connection.Reader = null;
  208. commandBehavior = originalBehavior;
  209. }
  210. // we now give the command a chance to terminate. In the case of
  211. // stored procedures it needs to update out and inout parameters
  212. command.Close(this);
  213. commandBehavior = CommandBehavior.Default;
  214. if (this.command.Canceled && connection.driver.Version.isAtLeast(5, 1, 0))
  215. {
  216. // Issue dummy command to clear kill flag
  217. ClearKillFlag();
  218. }
  219. if (shouldCloseConnection)
  220. connection.Close();
  221. command = null;
  222. connection.IsInUse = false;
  223. connection = null;
  224. isOpen = false;
  225. }
  226. #region TypeSafe Accessors
  227. /// <summary>
  228. /// Gets the value of the specified column as a Boolean.
  229. /// </summary>
  230. /// <param name="name"></param>
  231. /// <returns></returns>
  232. public bool GetBoolean(string name)
  233. {
  234. return GetBoolean(GetOrdinal(name));
  235. }
  236. /// <summary>
  237. /// Gets the value of the specified column as a Boolean.
  238. /// </summary>
  239. /// <param name="i"></param>
  240. /// <returns></returns>
  241. public override bool GetBoolean(int i)
  242. {
  243. return Convert.ToBoolean(GetValue(i));
  244. }
  245. /// <summary>
  246. /// Gets the value of the specified column as a byte.
  247. /// </summary>
  248. /// <param name="name"></param>
  249. /// <returns></returns>
  250. public byte GetByte(string name)
  251. {
  252. return GetByte(GetOrdinal(name));
  253. }
  254. /// <summary>
  255. /// Gets the value of the specified column as a byte.
  256. /// </summary>
  257. /// <param name="i"></param>
  258. /// <returns></returns>
  259. public override byte GetByte(int i)
  260. {
  261. IMySqlValue v = GetFieldValue(i, false);
  262. if (v is MySqlUByte)
  263. return ((MySqlUByte)v).Value;
  264. else
  265. return (byte)((MySqlByte)v).Value;
  266. }
  267. /// <summary>
  268. /// Gets the value of the specified column as a sbyte.
  269. /// </summary>
  270. /// <param name="name"></param>
  271. /// <returns></returns>
  272. public sbyte GetSByte(string name)
  273. {
  274. return GetSByte(GetOrdinal(name));
  275. }
  276. /// <summary>
  277. /// Gets the value of the specified column as a sbyte.
  278. /// </summary>
  279. /// <param name="i"></param>
  280. /// <returns></returns>
  281. public sbyte GetSByte(int i)
  282. {
  283. IMySqlValue v = GetFieldValue(i, false);
  284. if (v is MySqlByte)
  285. return ((MySqlByte)v).Value;
  286. else
  287. return (sbyte)((MySqlByte)v).Value;
  288. }
  289. /// <summary>
  290. /// Reads a stream of bytes from the specified column offset into the buffer an array starting at the given buffer offset.
  291. /// </summary>
  292. /// <param name="i">The zero-based column ordinal. </param>
  293. /// <param name="fieldOffset">The index within the field from which to begin the read operation. </param>
  294. /// <param name="buffer">The buffer into which to read the stream of bytes. </param>
  295. /// <param name="bufferoffset">The index for buffer to begin the read operation. </param>
  296. /// <param name="length">The maximum length to copy into the buffer. </param>
  297. /// <returns>The actual number of bytes read.</returns>
  298. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='MyDocs/MyMembers[@name="GetBytes"]/*'/>
  299. public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
  300. {
  301. if (i >= FieldCount)
  302. Throw(new IndexOutOfRangeException());
  303. IMySqlValue val = GetFieldValue(i, false);
  304. if (!(val is MySqlBinary) && !(val is MySqlGuid))
  305. Throw(new MySqlException("GetBytes can only be called on binary or guid columns"));
  306. byte[] bytes = null;
  307. if (val is MySqlBinary)
  308. bytes = ((MySqlBinary)val).Value;
  309. else
  310. bytes = ((MySqlGuid)val).Bytes;
  311. if (buffer == null)
  312. return bytes.Length;
  313. if (bufferoffset >= buffer.Length || bufferoffset < 0)
  314. Throw(new IndexOutOfRangeException("Buffer index must be a valid index in buffer"));
  315. if (buffer.Length < (bufferoffset + length))
  316. Throw(new ArgumentException("Buffer is not large enough to hold the requested data"));
  317. if (fieldOffset < 0 ||
  318. ((ulong)fieldOffset >= (ulong)bytes.Length && (ulong)bytes.Length > 0))
  319. Throw(new IndexOutOfRangeException("Data index must be a valid index in the field"));
  320. // adjust the length so we don't run off the end
  321. if ((ulong)bytes.Length < (ulong)(fieldOffset + length))
  322. {
  323. length = (int)((ulong)bytes.Length - (ulong)fieldOffset);
  324. }
  325. Buffer.BlockCopy(bytes, (int)fieldOffset, buffer, (int)bufferoffset, (int)length);
  326. return length;
  327. }
  328. private object ChangeType(IMySqlValue value, int fieldIndex, Type newType)
  329. {
  330. resultSet.Fields[fieldIndex].AddTypeConversion(newType);
  331. return Convert.ChangeType(value.Value, newType, CultureInfo.InvariantCulture);
  332. }
  333. /// <summary>
  334. /// Gets the value of the specified column as a single character.
  335. /// </summary>
  336. /// <param name="name"></param>
  337. /// <returns></returns>
  338. public char GetChar(string name)
  339. {
  340. return GetChar(GetOrdinal(name));
  341. }
  342. /// <summary>
  343. /// Gets the value of the specified column as a single character.
  344. /// </summary>
  345. /// <param name="i"></param>
  346. /// <returns></returns>
  347. public override char GetChar(int i)
  348. {
  349. string s = GetString(i);
  350. return s[0];
  351. }
  352. /// <summary>
  353. /// Reads a stream of characters from the specified column offset into the buffer as an array starting at the given buffer offset.
  354. /// </summary>
  355. /// <param name="i"></param>
  356. /// <param name="fieldoffset"></param>
  357. /// <param name="buffer"></param>
  358. /// <param name="bufferoffset"></param>
  359. /// <param name="length"></param>
  360. /// <returns></returns>
  361. public override long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
  362. {
  363. if (i >= FieldCount)
  364. Throw(new IndexOutOfRangeException());
  365. string valAsString = GetString(i);
  366. if (buffer == null) return valAsString.Length;
  367. if (bufferoffset >= buffer.Length || bufferoffset < 0)
  368. Throw(new IndexOutOfRangeException("Buffer index must be a valid index in buffer"));
  369. if (buffer.Length < (bufferoffset + length))
  370. Throw(new ArgumentException("Buffer is not large enough to hold the requested data"));
  371. if (fieldoffset < 0 || fieldoffset >= valAsString.Length)
  372. Throw(new IndexOutOfRangeException("Field offset must be a valid index in the field"));
  373. if (valAsString.Length < length)
  374. length = valAsString.Length;
  375. valAsString.CopyTo((int)fieldoffset, buffer, bufferoffset, length);
  376. return length;
  377. }
  378. /// <summary>
  379. /// Gets the name of the source data type.
  380. /// </summary>
  381. /// <param name="i"></param>
  382. /// <returns></returns>
  383. public override String GetDataTypeName(int i)
  384. {
  385. if (!isOpen)
  386. Throw(new Exception("No current query in data reader"));
  387. if (i >= FieldCount)
  388. Throw(new IndexOutOfRangeException());
  389. // return the name of the type used on the backend
  390. IMySqlValue v = resultSet.Values[i];
  391. return v.MySqlTypeName;
  392. }
  393. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetMySqlDateTime/*'/>
  394. public MySqlDateTime GetMySqlDateTime(string column)
  395. {
  396. return GetMySqlDateTime(GetOrdinal(column));
  397. }
  398. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetMySqlDateTime/*'/>
  399. public MySqlDateTime GetMySqlDateTime(int column)
  400. {
  401. return (MySqlDateTime)GetFieldValue(column, true);
  402. }
  403. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetDateTimeS/*'/>
  404. public DateTime GetDateTime(string column)
  405. {
  406. return GetDateTime(GetOrdinal(column));
  407. }
  408. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetDateTime/*'/>
  409. public override DateTime GetDateTime(int i)
  410. {
  411. IMySqlValue val = GetFieldValue(i, true);
  412. MySqlDateTime dt;
  413. if (val is MySqlDateTime)
  414. dt = (MySqlDateTime)val;
  415. else
  416. {
  417. // we need to do this because functions like date_add return string
  418. string s = GetString(i);
  419. dt = MySqlDateTime.Parse(s);
  420. }
  421. dt.TimezoneOffset = driver.timeZoneOffset;
  422. if (connection.Settings.ConvertZeroDateTime && !dt.IsValidDateTime)
  423. return DateTime.MinValue;
  424. else
  425. return dt.GetDateTime();
  426. }
  427. public MySqlDecimal GetMySqlDecimal(string column)
  428. {
  429. return GetMySqlDecimal(GetOrdinal(column));
  430. }
  431. public MySqlDecimal GetMySqlDecimal(int i)
  432. {
  433. return (MySqlDecimal)GetFieldValue(i, false);
  434. }
  435. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetDecimalS/*'/>
  436. public Decimal GetDecimal(string column)
  437. {
  438. return GetDecimal(GetOrdinal(column));
  439. }
  440. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetDecimal/*'/>
  441. public override Decimal GetDecimal(int i)
  442. {
  443. IMySqlValue v = GetFieldValue(i, true);
  444. if (v is MySqlDecimal)
  445. return ((MySqlDecimal)v).Value;
  446. return Convert.ToDecimal(v.Value);
  447. }
  448. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetDoubleS/*'/>
  449. public double GetDouble(string column)
  450. {
  451. return GetDouble(GetOrdinal(column));
  452. }
  453. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetDouble/*'/>
  454. public override double GetDouble(int i)
  455. {
  456. IMySqlValue v = GetFieldValue(i, true);
  457. if (v is MySqlDouble)
  458. return ((MySqlDouble)v).Value;
  459. return Convert.ToDouble(v.Value);
  460. }
  461. public Type GetFieldType(string column)
  462. {
  463. return GetFieldType(GetOrdinal(column));
  464. }
  465. /// <summary>
  466. /// Gets the Type that is the data type of the object.
  467. /// </summary>
  468. /// <param name="i"></param>
  469. /// <returns></returns>
  470. public override Type GetFieldType(int i)
  471. {
  472. if (!isOpen)
  473. Throw(new Exception("No current query in data reader"));
  474. if (i >= FieldCount)
  475. Throw(new IndexOutOfRangeException());
  476. // we have to use the values array directly because we can't go through
  477. // GetValue
  478. IMySqlValue v = resultSet.Values[i];
  479. if (v is MySqlDateTime)
  480. {
  481. if (!connection.Settings.AllowZeroDateTime)
  482. return typeof(DateTime);
  483. return typeof(MySqlDateTime);
  484. }
  485. return v.SystemType;
  486. }
  487. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetFloatS/*'/>
  488. public float GetFloat(string column)
  489. {
  490. return GetFloat(GetOrdinal(column));
  491. }
  492. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetFloat/*'/>
  493. public override float GetFloat(int i)
  494. {
  495. IMySqlValue v = GetFieldValue(i, true);
  496. if (v is MySqlSingle)
  497. return ((MySqlSingle)v).Value;
  498. return Convert.ToSingle(v.Value);
  499. }
  500. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetGuidS/*'/>
  501. public Guid GetGuid(string column)
  502. {
  503. return GetGuid(GetOrdinal(column));
  504. }
  505. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetGuid/*'/>
  506. public override Guid GetGuid(int i)
  507. {
  508. object v = GetValue(i);
  509. if (v is Guid)
  510. return (Guid)v;
  511. if (v is string)
  512. return new Guid(v as string);
  513. if (v is byte[])
  514. {
  515. byte[] bytes = (byte[])v;
  516. if (bytes.Length == 16)
  517. return new Guid(bytes);
  518. }
  519. Throw(new MySqlException(Resources.ValueNotSupportedForGuid));
  520. return Guid.Empty; // just to silence compiler
  521. }
  522. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetInt16S/*'/>
  523. public Int16 GetInt16(string column)
  524. {
  525. return GetInt16(GetOrdinal(column));
  526. }
  527. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetInt16/*'/>
  528. public override Int16 GetInt16(int i)
  529. {
  530. IMySqlValue v = GetFieldValue(i, true);
  531. if (v is MySqlInt16)
  532. return ((MySqlInt16)v).Value;
  533. return (short)ChangeType(v, i, typeof(short));
  534. }
  535. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetInt32S/*'/>
  536. public Int32 GetInt32(string column)
  537. {
  538. return GetInt32(GetOrdinal(column));
  539. }
  540. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetInt32/*'/>
  541. public override Int32 GetInt32(int i)
  542. {
  543. IMySqlValue v = GetFieldValue(i, true);
  544. if (v is MySqlInt32)
  545. return ((MySqlInt32)v).Value;
  546. return (Int32)ChangeType(v, i, typeof(Int32));
  547. }
  548. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetInt64S/*'/>
  549. public Int64 GetInt64(string column)
  550. {
  551. return GetInt64(GetOrdinal(column));
  552. }
  553. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetInt64/*'/>
  554. public override Int64 GetInt64(int i)
  555. {
  556. IMySqlValue v = GetFieldValue(i, true);
  557. if (v is MySqlInt64)
  558. return ((MySqlInt64)v).Value;
  559. return (Int64)ChangeType(v, i, typeof(Int64));
  560. }
  561. /// <summary>
  562. /// Gets the name of the specified column.
  563. /// </summary>
  564. /// <param name="i"></param>
  565. /// <returns></returns>
  566. public override String GetName(int i)
  567. {
  568. if (!isOpen)
  569. Throw(new Exception("No current query in data reader"));
  570. if (i >= FieldCount)
  571. Throw(new IndexOutOfRangeException());
  572. return resultSet.Fields[i].ColumnName;
  573. }
  574. /// <summary>
  575. /// Gets the column ordinal, given the name of the column.
  576. /// </summary>
  577. /// <param name="name"></param>
  578. /// <returns></returns>
  579. public override int GetOrdinal(string name)
  580. {
  581. if (!isOpen || resultSet == null)
  582. Throw(new Exception("No current query in data reader"));
  583. return resultSet.GetOrdinal(name);
  584. }
  585. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetStringS/*'/>
  586. public string GetString(string column)
  587. {
  588. return GetString(GetOrdinal(column));
  589. }
  590. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetString/*'/>
  591. public override String GetString(int i)
  592. {
  593. IMySqlValue val = GetFieldValue(i, true);
  594. if (val is MySqlBinary)
  595. {
  596. byte[] v = ((MySqlBinary)val).Value;
  597. return resultSet.Fields[i].Encoding.GetString(v, 0, v.Length);
  598. }
  599. return val.Value.ToString();
  600. }
  601. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetTimeSpan/*'/>
  602. public TimeSpan GetTimeSpan(string column)
  603. {
  604. return GetTimeSpan(GetOrdinal(column));
  605. }
  606. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetTimeSpan/*'/>
  607. public TimeSpan GetTimeSpan(int column)
  608. {
  609. IMySqlValue val = GetFieldValue(column, true);
  610. MySqlTimeSpan ts = (MySqlTimeSpan)val;
  611. return ts.Value;
  612. }
  613. /// <summary>
  614. /// Gets the value of the specified column in its native format.
  615. /// </summary>
  616. /// <param name="i"></param>
  617. /// <returns></returns>
  618. public override object GetValue(int i)
  619. {
  620. if (!isOpen)
  621. Throw(new Exception("No current query in data reader"));
  622. if (i >= FieldCount)
  623. Throw(new IndexOutOfRangeException());
  624. IMySqlValue val = GetFieldValue(i, false);
  625. if (val.IsNull)
  626. return DBNull.Value;
  627. // if the column is a date/time, then we return a MySqlDateTime
  628. // so .ToString() will print '0000-00-00' correctly
  629. if (val is MySqlDateTime)
  630. {
  631. MySqlDateTime dt = (MySqlDateTime)val;
  632. if (!dt.IsValidDateTime && connection.Settings.ConvertZeroDateTime)
  633. return DateTime.MinValue;
  634. else if (connection.Settings.AllowZeroDateTime)
  635. return val;
  636. else
  637. return dt.GetDateTime();
  638. }
  639. return val.Value;
  640. }
  641. /// <summary>
  642. /// Gets all attribute columns in the collection for the current row.
  643. /// </summary>
  644. /// <param name="values"></param>
  645. /// <returns></returns>
  646. public override int GetValues(object[] values)
  647. {
  648. int numCols = Math.Min(values.Length, FieldCount);
  649. for (int i = 0; i < numCols; i++)
  650. values[i] = GetValue(i);
  651. return numCols;
  652. }
  653. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetUInt16/*'/>
  654. public UInt16 GetUInt16(string column)
  655. {
  656. return GetUInt16(GetOrdinal(column));
  657. }
  658. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetUInt16/*'/>
  659. public UInt16 GetUInt16(int column)
  660. {
  661. IMySqlValue v = GetFieldValue(column, true);
  662. if (v is MySqlUInt16)
  663. return ((MySqlUInt16)v).Value;
  664. return (UInt16)ChangeType(v, column, typeof(UInt16));
  665. }
  666. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetUInt32/*'/>
  667. public UInt32 GetUInt32(string column)
  668. {
  669. return GetUInt32(GetOrdinal(column));
  670. }
  671. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetUInt32/*'/>
  672. public UInt32 GetUInt32(int column)
  673. {
  674. IMySqlValue v = GetFieldValue(column, true);
  675. if (v is MySqlUInt32)
  676. return ((MySqlUInt32)v).Value;
  677. return (uint)ChangeType(v, column, typeof(UInt32));
  678. }
  679. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetUInt64/*'/>
  680. public UInt64 GetUInt64(string column)
  681. {
  682. return GetUInt64(GetOrdinal(column));
  683. }
  684. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlDataReader.xml' path='docs/GetUInt64/*'/>
  685. public UInt64 GetUInt64(int column)
  686. {
  687. IMySqlValue v = GetFieldValue(column, true);
  688. if (v is MySqlUInt64)
  689. return ((MySqlUInt64)v).Value;
  690. return (UInt64)ChangeType(v, column, typeof(UInt64));
  691. }
  692. #endregion
  693. IDataReader IDataRecord.GetData(int i)
  694. {
  695. return base.GetData(i);
  696. }
  697. /// <summary>
  698. /// Gets a value indicating whether the column contains non-existent or missing values.
  699. /// </summary>
  700. /// <param name="i"></param>
  701. /// <returns></returns>
  702. public override bool IsDBNull(int i)
  703. {
  704. return DBNull.Value == GetValue(i);
  705. }
  706. /// <summary>
  707. /// Advances the data reader to the next result, when reading the results of batch SQL statements.
  708. /// </summary>
  709. /// <returns></returns>
  710. public override bool NextResult()
  711. {
  712. if (!isOpen)
  713. Throw(new MySqlException(Resources.NextResultIsClosed));
  714. bool isCaching = command.CommandType == CommandType.TableDirect && command.EnableCaching &&
  715. (commandBehavior & CommandBehavior.SequentialAccess) == 0;
  716. // this will clear out any unread data
  717. if (resultSet != null)
  718. {
  719. resultSet.Close();
  720. if (isCaching)
  721. TableCache.AddToCache(command.CommandText, resultSet);
  722. }
  723. // single result means we only return a single resultset. If we have already
  724. // returned one, then we return false
  725. // TableDirect is basically a select * from a single table so it will generate
  726. // a single result also
  727. if (resultSet != null &&
  728. ((commandBehavior & CommandBehavior.SingleResult) != 0 || isCaching))
  729. return false;
  730. // next load up the next resultset if any
  731. try
  732. {
  733. do
  734. {
  735. resultSet = null;
  736. // if we are table caching, then try to retrieve the resultSet from the cache
  737. if (isCaching)
  738. resultSet = TableCache.RetrieveFromCache(command.CommandText,
  739. command.CacheAge);
  740. if (resultSet == null)
  741. {
  742. resultSet = driver.NextResult(Statement.StatementId, false);
  743. if (resultSet == null) return false;
  744. if (resultSet.IsOutputParameters && command.CommandType == CommandType.StoredProcedure)
  745. {
  746. StoredProcedure sp = statement as StoredProcedure;
  747. sp.ProcessOutputParameters(this);
  748. resultSet.Close();
  749. if (!sp.ServerProvidingOutputParameters) return false;
  750. // if we are using server side output parameters then we will get our ok packet
  751. // *after* the output parameters resultset
  752. resultSet = driver.NextResult(Statement.StatementId, true);
  753. }
  754. resultSet.Cached = isCaching;
  755. }
  756. if (resultSet.Size == 0)
  757. {
  758. Command.lastInsertedId = resultSet.InsertedId;
  759. if (affectedRows == -1)
  760. affectedRows = resultSet.AffectedRows;
  761. else
  762. affectedRows += resultSet.AffectedRows;
  763. }
  764. } while (resultSet.Size == 0);
  765. return true;
  766. }
  767. catch (MySqlException ex)
  768. {
  769. if (ex.IsFatal)
  770. connection.Abort();
  771. if (ex.Number == 0)
  772. throw new MySqlException(Resources.FatalErrorReadingResult, ex);
  773. if ((commandBehavior & CommandBehavior.CloseConnection) != 0)
  774. Close();
  775. throw;
  776. }
  777. }
  778. /// <summary>
  779. /// Advances the MySqlDataReader to the next record.
  780. /// </summary>
  781. /// <returns></returns>
  782. public override bool Read()
  783. {
  784. if (!isOpen)
  785. Throw(new MySqlException("Invalid attempt to Read when reader is closed."));
  786. if (resultSet == null)
  787. return false;
  788. try
  789. {
  790. return resultSet.NextRow(commandBehavior);
  791. }
  792. catch (TimeoutException tex)
  793. {
  794. connection.HandleTimeoutOrThreadAbort(tex);
  795. throw; // unreached
  796. }
  797. catch (ThreadAbortException taex)
  798. {
  799. connection.HandleTimeoutOrThreadAbort(taex);
  800. throw;
  801. }
  802. catch (MySqlException ex)
  803. {
  804. if (ex.IsFatal)
  805. connection.Abort();
  806. if (ex.IsQueryAborted)
  807. {
  808. throw;
  809. }
  810. throw new MySqlException(Resources.FatalErrorDuringRead, ex);
  811. }
  812. }
  813. private IMySqlValue GetFieldValue(int index, bool checkNull)
  814. {
  815. if (index < 0 || index >= FieldCount)
  816. Throw(new ArgumentException(Resources.InvalidColumnOrdinal));
  817. IMySqlValue v = resultSet[index];
  818. if (checkNull && v.IsNull)
  819. throw new System.Data.SqlTypes.SqlNullValueException();
  820. return v;
  821. }
  822. private void ClearKillFlag()
  823. {
  824. // This query will silently crash because of the Kill call that happened before.
  825. string dummyStatement = "SELECT * FROM bogus_table LIMIT 0"; /* dummy query used to clear kill flag */
  826. MySqlCommand dummyCommand = new MySqlCommand(dummyStatement, connection);
  827. dummyCommand.InternallyCreated = true;
  828. try
  829. {
  830. dummyCommand.ExecuteReader(); // ExecuteReader catches the exception and returns null, which is expected.
  831. }
  832. catch (MySqlException ex)
  833. {
  834. if (ex.Number != (int)MySqlErrorCode.NoSuchTable) throw;
  835. }
  836. }
  837. private void ProcessOutputParameters()
  838. {
  839. // if we are not 5.5 or later or we are not prepared then we are simulating output parameters
  840. // with user variables and they are also string so we have to work some magic with out
  841. // column types before we read the data
  842. if (!driver.SupportsOutputParameters || !command.IsPrepared)
  843. AdjustOutputTypes();
  844. // now read the output parameters data row
  845. if ((commandBehavior & CommandBehavior.SchemaOnly) != 0) return;
  846. resultSet.NextRow(commandBehavior);
  847. string prefix = "@" + StoredProcedure.ParameterPrefix;
  848. for (int i = 0; i < FieldCount; i++)
  849. {
  850. string fieldName = GetName(i);
  851. if (fieldName.StartsWith(prefix))
  852. fieldName = fieldName.Remove(0, prefix.Length);
  853. MySqlParameter parameter = command.Parameters.GetParameterFlexible(fieldName, true);
  854. parameter.Value = GetValue(i);
  855. }
  856. }
  857. private void AdjustOutputTypes()
  858. {
  859. // since MySQL likes to return user variables as strings
  860. // we reset the types of the readers internal value objects
  861. // this will allow those value objects to parse the string based
  862. // return values
  863. for (int i = 0; i < FieldCount; i++)
  864. {
  865. string fieldName = GetName(i);
  866. fieldName = fieldName.Remove(0, StoredProcedure.ParameterPrefix.Length + 1);
  867. MySqlParameter parameter = command.Parameters.GetParameterFlexible(fieldName, true);
  868. IMySqlValue v = MySqlField.GetIMySqlValue(parameter.MySqlDbType);
  869. if (v is MySqlBit)
  870. {
  871. MySqlBit bit = (MySqlBit)v;
  872. bit.ReadAsString = true;
  873. resultSet.SetValueObject(i, bit);
  874. }
  875. else
  876. resultSet.SetValueObject(i, v);
  877. }
  878. }
  879. private void Throw(Exception ex)
  880. {
  881. if (connection != null)
  882. connection.Throw(ex);
  883. throw ex;
  884. }
  885. public new void Dispose()
  886. {
  887. Dispose(true);
  888. GC.SuppressFinalize(this);
  889. }
  890. protected override void Dispose(bool disposing)
  891. {
  892. if (disposed)
  893. return;
  894. Close();
  895. base.Dispose(disposing);
  896. disposed = true;
  897. }
  898. #region Destructor
  899. ~MySqlDataReader()
  900. {
  901. Dispose(false);
  902. }
  903. #endregion
  904. }
  905. }
  906. #endif