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.

842 lines
29 KiB

4 years ago
  1. #if MYSQL_6_10
  2. // Copyright ?2004, 2019, 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 Externals.MySql.Data.Common;
  24. using Externals.MySql.Data.MySqlClient.Authentication;
  25. using Externals.MySql.Data.Types;
  26. using System;
  27. using System.Collections;
  28. using System.ComponentModel;
  29. using System.IO;
  30. using System.Linq;
  31. using System.Reflection;
  32. using System.Text;
  33. namespace Externals.MySql.Data.MySqlClient
  34. {
  35. /// <summary>
  36. /// Summary description for Driver.
  37. /// </summary>
  38. internal class NativeDriver : IDriver
  39. {
  40. private DBVersion version;
  41. private int threadId;
  42. protected byte[] encryptionSeed;
  43. protected ServerStatusFlags serverStatus;
  44. protected MySqlStream stream;
  45. protected Stream baseStream;
  46. private BitArray nullMap;
  47. private MySqlPacket packet;
  48. private ClientFlags connectionFlags;
  49. private Driver owner;
  50. private int warnings;
  51. private MySqlAuthenticationPlugin authPlugin;
  52. // Windows authentication method string, used by the protocol.
  53. // Also known as "client plugin name".
  54. const string AuthenticationWindowsPlugin = "authentication_windows_client";
  55. // Predefined username for IntegratedSecurity
  56. const string AuthenticationWindowsUser = "auth_windows";
  57. public NativeDriver(Driver owner)
  58. {
  59. this.owner = owner;
  60. threadId = -1;
  61. }
  62. public ClientFlags Flags
  63. {
  64. get { return connectionFlags; }
  65. }
  66. public int ThreadId
  67. {
  68. get { return threadId; }
  69. }
  70. public DBVersion Version
  71. {
  72. get { return version; }
  73. }
  74. public ServerStatusFlags ServerStatus
  75. {
  76. get { return serverStatus; }
  77. }
  78. public int WarningCount
  79. {
  80. get { return warnings; }
  81. }
  82. public MySqlPacket Packet
  83. {
  84. get { return packet; }
  85. }
  86. internal MySqlConnectionStringBuilder Settings
  87. {
  88. get { return owner.Settings; }
  89. }
  90. internal Encoding Encoding
  91. {
  92. get { return owner.Encoding; }
  93. }
  94. private void HandleException(MySqlException ex)
  95. {
  96. if (ex.IsFatal)
  97. owner.Close();
  98. }
  99. internal void SendPacket(MySqlPacket p)
  100. {
  101. stream.SendPacket(p);
  102. }
  103. internal void SendEmptyPacket()
  104. {
  105. byte[] buffer = new byte[4];
  106. stream.SendEntirePacketDirectly(buffer, 0);
  107. }
  108. internal MySqlPacket ReadPacket()
  109. {
  110. return packet = stream.ReadPacket();
  111. }
  112. internal void ReadOk(bool read)
  113. {
  114. try
  115. {
  116. if (read)
  117. packet = stream.ReadPacket();
  118. byte marker = (byte)packet.ReadByte();
  119. if (marker != 0)
  120. {
  121. throw new MySqlException("Out of sync with server", true, null);
  122. }
  123. packet.ReadFieldLength(); /* affected rows */
  124. packet.ReadFieldLength(); /* last insert id */
  125. if (packet.HasMoreData)
  126. {
  127. serverStatus = (ServerStatusFlags)packet.ReadInteger(2);
  128. packet.ReadInteger(2); /* warning count */
  129. if (packet.HasMoreData)
  130. {
  131. packet.ReadLenString(); /* message */
  132. }
  133. }
  134. }
  135. catch (MySqlException ex)
  136. {
  137. HandleException(ex);
  138. throw;
  139. }
  140. }
  141. /// <summary>
  142. /// Sets the current database for the this connection
  143. /// </summary>
  144. /// <param name="dbName"></param>
  145. public void SetDatabase(string dbName)
  146. {
  147. byte[] dbNameBytes = Encoding.GetBytes(dbName);
  148. packet.Clear();
  149. packet.WriteByte((byte)DBCmd.INIT_DB);
  150. packet.Write(dbNameBytes);
  151. ExecutePacket(packet);
  152. ReadOk(true);
  153. }
  154. public void Configure()
  155. {
  156. stream.MaxPacketSize = (ulong)owner.MaxPacketSize;
  157. stream.Encoding = Encoding;
  158. }
  159. public void Open()
  160. {
  161. // connect to one of our specified hosts
  162. try
  163. {
  164. baseStream = StreamCreator.GetStream(Settings);
  165. #if !NETSTANDARD1_3
  166. if (Settings.IncludeSecurityAsserts)
  167. MySqlSecurityPermission.CreatePermissionSet(false).Assert();
  168. #endif
  169. }
  170. catch (System.Security.SecurityException)
  171. {
  172. throw;
  173. }
  174. catch (Exception ex)
  175. {
  176. throw new MySqlException(Resources.UnableToConnectToHost,
  177. (int)MySqlErrorCode.UnableToConnectToHost, ex);
  178. }
  179. if (baseStream == null)
  180. throw new MySqlException(Resources.UnableToConnectToHost,
  181. (int)MySqlErrorCode.UnableToConnectToHost);
  182. int maxSinglePacket = 255 * 255 * 255;
  183. stream = new MySqlStream(baseStream, Encoding, false);
  184. stream.ResetTimeout((int)Settings.ConnectionTimeout * 1000);
  185. // read off the welcome packet and parse out it's values
  186. packet = stream.ReadPacket();
  187. int protocol = packet.ReadByte();
  188. string versionString = packet.ReadString();
  189. version = DBVersion.Parse(versionString);
  190. threadId = packet.ReadInteger(4);
  191. byte[] seedPart1 = packet.ReadStringAsBytes();
  192. maxSinglePacket = (256 * 256 * 256) - 1;
  193. // read in Server capabilities if they are provided
  194. ClientFlags serverCaps = 0;
  195. if (packet.HasMoreData)
  196. serverCaps = (ClientFlags)packet.ReadInteger(2);
  197. /* New protocol with 16 bytes to describe server characteristics */
  198. owner.ConnectionCharSetIndex = (int)packet.ReadByte();
  199. serverStatus = (ServerStatusFlags)packet.ReadInteger(2);
  200. // Since 5.5, high bits of server caps are stored after status.
  201. // Previously, it was part of reserved always 0x00 13-byte filler.
  202. uint serverCapsHigh = (uint)packet.ReadInteger(2);
  203. serverCaps |= (ClientFlags)(serverCapsHigh << 16);
  204. packet.Position += 11;
  205. byte[] seedPart2 = packet.ReadStringAsBytes();
  206. encryptionSeed = new byte[seedPart1.Length + seedPart2.Length];
  207. seedPart1.CopyTo(encryptionSeed, 0);
  208. seedPart2.CopyTo(encryptionSeed, seedPart1.Length);
  209. string authenticationMethod = "";
  210. if ((serverCaps & ClientFlags.PLUGIN_AUTH) != 0)
  211. {
  212. authenticationMethod = packet.ReadString();
  213. }
  214. else
  215. {
  216. // Some MySql versions like 5.1, don't give name of plugin, default to native password.
  217. authenticationMethod = "mysql_native_password";
  218. }
  219. // based on our settings, set our connection flags
  220. SetConnectionFlags(serverCaps);
  221. packet.Clear();
  222. packet.WriteInteger((int)connectionFlags, 4);
  223. packet.WriteInteger(maxSinglePacket, 4);
  224. packet.WriteByte(33); //character set utf-8
  225. packet.Write(new byte[23]);
  226. if ((serverCaps & ClientFlags.SSL) == 0)
  227. {
  228. if ((Settings.SslMode != MySqlSslMode.None)
  229. && (Settings.SslMode != MySqlSslMode.Preferred))
  230. {
  231. // Client requires SSL connections.
  232. string message = String.Format(Resources.NoServerSSLSupport,
  233. Settings.Server);
  234. throw new MySqlException(message);
  235. }
  236. }
  237. else if (Settings.SslMode != MySqlSslMode.None)
  238. {
  239. stream.SendPacket(packet);
  240. stream = new Ssl(Settings).StartSSL(ref baseStream, Encoding, Settings.ToString());
  241. packet.Clear();
  242. packet.WriteInteger((int)connectionFlags, 4);
  243. packet.WriteInteger(maxSinglePacket, 4);
  244. packet.WriteByte(33); //character set utf-8
  245. packet.Write(new byte[23]);
  246. }
  247. Authenticate(authenticationMethod, false);
  248. // if we are using compression, then we use our CompressedStream class
  249. // to hide the ugliness of managing the compression
  250. if ((connectionFlags & ClientFlags.COMPRESS) != 0)
  251. stream = new MySqlStream(baseStream, Encoding, true);
  252. // give our stream the server version we are connected to.
  253. // We may have some fields that are read differently based
  254. // on the version of the server we are connected to.
  255. packet.Version = version;
  256. stream.MaxBlockSize = maxSinglePacket;
  257. }
  258. #region Authentication
  259. /// <summary>
  260. /// Return the appropriate set of connection flags for our
  261. /// server capabilities and our user requested options.
  262. /// </summary>
  263. private void SetConnectionFlags(ClientFlags serverCaps)
  264. {
  265. // We always allow multiple result sets
  266. ClientFlags flags = ClientFlags.MULTI_RESULTS;
  267. // allow load data local infile
  268. if (Settings.AllowLoadLocalInfile)
  269. flags |= ClientFlags.LOCAL_FILES;
  270. if (!Settings.UseAffectedRows)
  271. flags |= ClientFlags.FOUND_ROWS;
  272. flags |= ClientFlags.PROTOCOL_41;
  273. // Need this to get server status values
  274. flags |= ClientFlags.TRANSACTIONS;
  275. // user allows/disallows batch statements
  276. if (Settings.AllowBatch)
  277. flags |= ClientFlags.MULTI_STATEMENTS;
  278. // if the server allows it, tell it that we want long column info
  279. if ((serverCaps & ClientFlags.LONG_FLAG) != 0)
  280. flags |= ClientFlags.LONG_FLAG;
  281. // if the server supports it and it was requested, then turn on compression
  282. if ((serverCaps & ClientFlags.COMPRESS) != 0 && Settings.UseCompression)
  283. flags |= ClientFlags.COMPRESS;
  284. flags |= ClientFlags.LONG_PASSWORD; // for long passwords
  285. // did the user request an interactive session?
  286. if (Settings.InteractiveSession)
  287. flags |= ClientFlags.INTERACTIVE;
  288. // if the server allows it and a database was specified, then indicate
  289. // that we will connect with a database name
  290. if ((serverCaps & ClientFlags.CONNECT_WITH_DB) != 0 &&
  291. Settings.Database != null && Settings.Database.Length > 0)
  292. flags |= ClientFlags.CONNECT_WITH_DB;
  293. // if the server is requesting a secure connection, then we oblige
  294. if ((serverCaps & ClientFlags.SECURE_CONNECTION) != 0)
  295. flags |= ClientFlags.SECURE_CONNECTION;
  296. // if the server is capable of SSL and the user is requesting SSL
  297. if ((serverCaps & ClientFlags.SSL) != 0 && Settings.SslMode != MySqlSslMode.None)
  298. flags |= ClientFlags.SSL;
  299. // if the server supports output parameters, then we do too
  300. if ((serverCaps & ClientFlags.PS_MULTI_RESULTS) != 0)
  301. flags |= ClientFlags.PS_MULTI_RESULTS;
  302. if ((serverCaps & ClientFlags.PLUGIN_AUTH) != 0)
  303. flags |= ClientFlags.PLUGIN_AUTH;
  304. // if the server supports connection attributes
  305. if ((serverCaps & ClientFlags.CONNECT_ATTRS) != 0)
  306. flags |= ClientFlags.CONNECT_ATTRS;
  307. if ((serverCaps & ClientFlags.CAN_HANDLE_EXPIRED_PASSWORD) != 0)
  308. flags |= ClientFlags.CAN_HANDLE_EXPIRED_PASSWORD;
  309. connectionFlags = flags;
  310. }
  311. public void Authenticate(string authMethod, bool reset)
  312. {
  313. if (authMethod != null)
  314. {
  315. // Integrated security is a shortcut for windows auth
  316. if (Settings.IntegratedSecurity)
  317. authMethod = "authentication_windows_client";
  318. authPlugin = MySqlAuthenticationPlugin.GetPlugin(authMethod, this, encryptionSeed);
  319. }
  320. authPlugin.Authenticate(reset);
  321. }
  322. #endregion
  323. public void Reset()
  324. {
  325. warnings = 0;
  326. stream.Encoding = this.Encoding;
  327. stream.SequenceByte = 0;
  328. packet.Clear();
  329. packet.WriteByte((byte)DBCmd.CHANGE_USER);
  330. Authenticate(null, true);
  331. }
  332. /// <summary>
  333. /// Query is the method that is called to send all queries to the server
  334. /// </summary>
  335. public void SendQuery(MySqlPacket queryPacket)
  336. {
  337. warnings = 0;
  338. queryPacket.SetByte(4, (byte)DBCmd.QUERY);
  339. ExecutePacket(queryPacket);
  340. // the server will respond in one of several ways with the first byte indicating
  341. // the type of response.
  342. // 0 == ok packet. This indicates non-select queries
  343. // 0xff == error packet. This is handled in stream.OpenPacket
  344. // > 0 = number of columns in select query
  345. // We don't actually read the result here since a single query can generate
  346. // multiple resultsets and we don't want to duplicate code. See ReadResult
  347. // Instead we set our internal server status flag to indicate that we have a query waiting.
  348. // This flag will be maintained by ReadResult
  349. serverStatus |= ServerStatusFlags.AnotherQuery;
  350. }
  351. public void Close(bool isOpen)
  352. {
  353. try
  354. {
  355. if (isOpen)
  356. {
  357. try
  358. {
  359. packet.Clear();
  360. packet.WriteByte((byte)DBCmd.QUIT);
  361. ExecutePacket(packet);
  362. }
  363. catch (Exception ex)
  364. {
  365. MySqlTrace.LogError(ThreadId, ex.ToString());
  366. // Eat exception here. We should try to closing
  367. // the stream anyway.
  368. }
  369. }
  370. if (stream != null)
  371. stream.Close();
  372. stream = null;
  373. }
  374. catch (Exception)
  375. {
  376. // we are just going to eat any exceptions
  377. // generated here
  378. }
  379. }
  380. public bool Ping()
  381. {
  382. try
  383. {
  384. packet.Clear();
  385. packet.WriteByte((byte)DBCmd.PING);
  386. ExecutePacket(packet);
  387. ReadOk(true);
  388. return true;
  389. }
  390. catch (Exception)
  391. {
  392. owner.Close();
  393. return false;
  394. }
  395. }
  396. public int GetResult(ref int affectedRow, ref long insertedId)
  397. {
  398. try
  399. {
  400. packet = stream.ReadPacket();
  401. }
  402. catch (TimeoutException)
  403. {
  404. // Do not reset serverStatus, allow to reenter, e.g when
  405. // ResultSet is closed.
  406. throw;
  407. }
  408. catch (Exception)
  409. {
  410. serverStatus &= ~(ServerStatusFlags.AnotherQuery |
  411. ServerStatusFlags.MoreResults);
  412. throw;
  413. }
  414. int fieldCount = (int)packet.ReadFieldLength();
  415. if (-1 == fieldCount)
  416. {
  417. if (this.Settings.AllowLoadLocalInfile)
  418. {
  419. string filename = packet.ReadString();
  420. SendFileToServer(filename);
  421. return GetResult(ref affectedRow, ref insertedId);
  422. }
  423. else
  424. {
  425. stream.Close();
  426. throw new MySqlException(Resources.LocalInfileDisabled);
  427. }
  428. }
  429. else if (fieldCount == 0)
  430. {
  431. // the code to read last packet will set these server status vars
  432. // again if necessary.
  433. serverStatus &= ~(ServerStatusFlags.AnotherQuery |
  434. ServerStatusFlags.MoreResults);
  435. affectedRow = (int)packet.ReadFieldLength();
  436. insertedId = (long)packet.ReadFieldLength();
  437. serverStatus = (ServerStatusFlags)packet.ReadInteger(2);
  438. warnings += packet.ReadInteger(2);
  439. if (packet.HasMoreData)
  440. {
  441. packet.ReadLenString(); //TODO: server message
  442. }
  443. }
  444. return fieldCount;
  445. }
  446. /// <summary>
  447. /// Sends the specified file to the server.
  448. /// This supports the LOAD DATA LOCAL INFILE
  449. /// </summary>
  450. /// <param name="filename"></param>
  451. private void SendFileToServer(string filename)
  452. {
  453. byte[] buffer = new byte[8196];
  454. long len = 0;
  455. try
  456. {
  457. using (FileStream fs = new FileStream(filename, FileMode.Open,
  458. FileAccess.Read))
  459. {
  460. len = fs.Length;
  461. while (len > 0)
  462. {
  463. int count = fs.Read(buffer, 4, (int)(len > 8192 ? 8192 : len));
  464. stream.SendEntirePacketDirectly(buffer, count);
  465. len -= count;
  466. }
  467. stream.SendEntirePacketDirectly(buffer, 0);
  468. }
  469. }
  470. catch (Exception ex)
  471. {
  472. throw new MySqlException("Error during LOAD DATA LOCAL INFILE", ex);
  473. }
  474. }
  475. private void ReadNullMap(int fieldCount)
  476. {
  477. // if we are binary, then we need to load in our null bitmap
  478. nullMap = null;
  479. byte[] nullMapBytes = new byte[(fieldCount + 9) / 8];
  480. packet.ReadByte();
  481. packet.Read(nullMapBytes, 0, nullMapBytes.Length);
  482. nullMap = new BitArray(nullMapBytes);
  483. }
  484. public IMySqlValue ReadColumnValue(int index, MySqlField field, IMySqlValue valObject)
  485. {
  486. long length = -1;
  487. bool isNull;
  488. if (nullMap != null)
  489. isNull = nullMap[index + 2];
  490. else
  491. {
  492. length = packet.ReadFieldLength();
  493. isNull = length == -1;
  494. }
  495. packet.Encoding = field.Encoding;
  496. packet.Version = version;
  497. var val = valObject.ReadValue(packet, length, isNull);
  498. if (val is MySqlDateTime d)
  499. {
  500. d.TimezoneOffset = field.driver.timeZoneOffset;
  501. return d;
  502. }
  503. return val;
  504. }
  505. public void SkipColumnValue(IMySqlValue valObject)
  506. {
  507. int length = -1;
  508. if (nullMap == null)
  509. {
  510. length = (int)packet.ReadFieldLength();
  511. if (length == -1) return;
  512. }
  513. if (length > -1)
  514. packet.Position += length;
  515. else
  516. valObject.SkipValue(packet);
  517. }
  518. public void GetColumnsData(MySqlField[] columns)
  519. {
  520. for (int i = 0; i < columns.Length; i++)
  521. GetColumnData(columns[i]);
  522. ReadEOF();
  523. }
  524. private void GetColumnData(MySqlField field)
  525. {
  526. stream.Encoding = Encoding;
  527. packet = stream.ReadPacket();
  528. field.Encoding = Encoding;
  529. field.CatalogName = packet.ReadLenString();
  530. field.DatabaseName = packet.ReadLenString();
  531. field.TableName = packet.ReadLenString();
  532. field.RealTableName = packet.ReadLenString();
  533. field.ColumnName = packet.ReadLenString();
  534. field.OriginalColumnName = packet.ReadLenString();
  535. packet.ReadByte();
  536. field.CharacterSetIndex = packet.ReadInteger(2);
  537. field.ColumnLength = packet.ReadInteger(4);
  538. MySqlDbType type = (MySqlDbType)packet.ReadByte();
  539. ColumnFlags colFlags;
  540. if ((connectionFlags & ClientFlags.LONG_FLAG) != 0)
  541. colFlags = (ColumnFlags)packet.ReadInteger(2);
  542. else
  543. colFlags = (ColumnFlags)packet.ReadByte();
  544. field.Scale = (byte)packet.ReadByte();
  545. if (packet.HasMoreData)
  546. {
  547. packet.ReadInteger(2); // reserved
  548. }
  549. if (type == MySqlDbType.Decimal || type == MySqlDbType.NewDecimal)
  550. {
  551. field.Precision = ((colFlags & ColumnFlags.UNSIGNED) != 0) ? (byte)(field.ColumnLength) : (byte)(field.ColumnLength - 1);
  552. if (field.Scale != 0)
  553. field.Precision--;
  554. }
  555. field.SetTypeAndFlags(type, colFlags);
  556. }
  557. private void ExecutePacket(MySqlPacket packetToExecute)
  558. {
  559. try
  560. {
  561. warnings = 0;
  562. stream.SequenceByte = 0;
  563. stream.SendPacket(packetToExecute);
  564. }
  565. catch (MySqlException ex)
  566. {
  567. HandleException(ex);
  568. throw;
  569. }
  570. }
  571. public void ExecuteStatement(MySqlPacket packetToExecute)
  572. {
  573. warnings = 0;
  574. packetToExecute.SetByte(4, (byte)DBCmd.EXECUTE);
  575. ExecutePacket(packetToExecute);
  576. serverStatus |= ServerStatusFlags.AnotherQuery;
  577. }
  578. private void CheckEOF()
  579. {
  580. if (!packet.IsLastPacket)
  581. throw new MySqlException("Expected end of data packet");
  582. packet.ReadByte(); // read off the 254
  583. if (packet.HasMoreData)
  584. {
  585. warnings += packet.ReadInteger(2);
  586. serverStatus = (ServerStatusFlags)packet.ReadInteger(2);
  587. // if we are at the end of this cursor based resultset, then we remove
  588. // the last row sent status flag so our next fetch doesn't abort early
  589. // and we remove this command result from our list of active CommandResult objects.
  590. // if ((serverStatus & ServerStatusFlags.LastRowSent) != 0)
  591. // {
  592. // serverStatus &= ~ServerStatusFlags.LastRowSent;
  593. // commandResults.Remove(lastCommandResult);
  594. // }
  595. }
  596. }
  597. private void ReadEOF()
  598. {
  599. packet = stream.ReadPacket();
  600. CheckEOF();
  601. }
  602. public int PrepareStatement(string sql, ref MySqlField[] parameters)
  603. {
  604. //TODO: check this
  605. //ClearFetchedRow();
  606. packet.Length = sql.Length * 4 + 5;
  607. byte[] buffer = packet.Buffer;
  608. int len = Encoding.GetBytes(sql, 0, sql.Length, packet.Buffer, 5);
  609. packet.Position = len + 5;
  610. buffer[4] = (byte)DBCmd.PREPARE;
  611. ExecutePacket(packet);
  612. packet = stream.ReadPacket();
  613. int marker = packet.ReadByte();
  614. if (marker != 0)
  615. throw new MySqlException("Expected prepared statement marker");
  616. int statementId = packet.ReadInteger(4);
  617. int numCols = packet.ReadInteger(2);
  618. int numParams = packet.ReadInteger(2);
  619. //TODO: find out what this is needed for
  620. packet.ReadInteger(3);
  621. if (numParams > 0)
  622. {
  623. parameters = owner.GetColumns(numParams);
  624. // we set the encoding for each parameter back to our connection encoding
  625. // since we can't trust what is coming back from the server
  626. for (int i = 0; i < parameters.Length; i++)
  627. parameters[i].Encoding = Encoding;
  628. }
  629. if (numCols > 0)
  630. {
  631. while (numCols-- > 0)
  632. {
  633. packet = stream.ReadPacket();
  634. //TODO: handle streaming packets
  635. }
  636. ReadEOF();
  637. }
  638. return statementId;
  639. }
  640. // private void ClearFetchedRow()
  641. // {
  642. // if (lastCommandResult == 0) return;
  643. //TODO
  644. /* CommandResult result = (CommandResult)commandResults[lastCommandResult];
  645. result.ReadRemainingColumns();
  646. stream.OpenPacket();
  647. if (! stream.IsLastPacket)
  648. throw new MySqlException("Cursor reading out of sync");
  649. ReadEOF(false);
  650. lastCommandResult = 0;*/
  651. // }
  652. /// <summary>
  653. /// FetchDataRow is the method that the data reader calls to see if there is another
  654. /// row to fetch. In the non-prepared mode, it will simply read the next data packet.
  655. /// In the prepared mode (statementId > 0), it will
  656. /// </summary>
  657. public bool FetchDataRow(int statementId, int columns)
  658. {
  659. /* ClearFetchedRow();
  660. if (!commandResults.ContainsKey(statementId)) return false;
  661. if ( (serverStatus & ServerStatusFlags.LastRowSent) != 0)
  662. return false;
  663. stream.StartPacket(9, true);
  664. stream.WriteByte((byte)DBCmd.FETCH);
  665. stream.WriteInteger(statementId, 4);
  666. stream.WriteInteger(1, 4);
  667. stream.Flush();
  668. lastCommandResult = statementId;
  669. */
  670. packet = stream.ReadPacket();
  671. if (packet.IsLastPacket)
  672. {
  673. CheckEOF();
  674. return false;
  675. }
  676. nullMap = null;
  677. if (statementId > 0)
  678. ReadNullMap(columns);
  679. return true;
  680. }
  681. public void CloseStatement(int statementId)
  682. {
  683. packet.Clear();
  684. packet.WriteByte((byte)DBCmd.CLOSE_STMT);
  685. packet.WriteInteger((long)statementId, 4);
  686. stream.SequenceByte = 0;
  687. stream.SendPacket(packet);
  688. }
  689. /// <summary>
  690. /// Execution timeout, in milliseconds. When the accumulated time for network IO exceeds this value
  691. /// TimeoutException is thrown. This timeout needs to be reset for every new command
  692. /// </summary>
  693. ///
  694. public void ResetTimeout(int timeout)
  695. {
  696. if (stream != null)
  697. stream.ResetTimeout(timeout);
  698. }
  699. internal void SetConnectAttrs()
  700. {
  701. // Sets connect attributes
  702. if ((connectionFlags & ClientFlags.CONNECT_ATTRS) != 0)
  703. {
  704. string connectAttrs = string.Empty;
  705. MySqlConnectAttrs attrs = new MySqlConnectAttrs();
  706. foreach (PropertyInfo property in attrs.GetType().GetProperties())
  707. {
  708. string name = property.Name;
  709. #if NETSTANDARD1_3
  710. object[] customAttrs = property.GetCustomAttributes(typeof(DisplayNameAttribute), false).ToArray<object>();
  711. #else
  712. object[] customAttrs = property.GetCustomAttributes(typeof(DisplayNameAttribute), false);
  713. #endif
  714. if (customAttrs.Length > 0)
  715. name = (customAttrs[0] as DisplayNameAttribute).DisplayName;
  716. string value = (string)property.GetValue(attrs, null);
  717. connectAttrs += string.Format("{0}{1}", (char)name.Length, name);
  718. connectAttrs += string.Format("{0}{1}", (char)value.Length, value);
  719. }
  720. packet.WriteLenString(connectAttrs);
  721. }
  722. }
  723. }
  724. }
  725. #endif