public override async Task OpenAsync(CancellationToken cancellationToken) { VerifyNotDisposed(); if (State != ConnectionState.Closed) { throw new InvalidOperationException("Cannot Open when State is {0}.".FormatInvariant(State)); } #if !NETSTANDARD1_3 if (System.Transactions.Transaction.Current != null) { throw new NotSupportedException("Ambient transactions are not supported. Use BeginTransaction instead."); } #endif if (m_connectionStringBuilder.UseCompression) { throw new NotSupportedException("Compression not supported."); } SetState(ConnectionState.Connecting); bool success = false; try { var pool = ConnectionPool.GetPool(m_connectionStringBuilder); m_session = pool?.TryGetSession(); if (m_session == null) { m_session = new MySqlSession(pool); var connected = await m_session.ConnectAsync(m_connectionStringBuilder.Server.Split(','), (int)m_connectionStringBuilder.Port).ConfigureAwait(false); if (!connected) { SetState(ConnectionState.Closed); throw new MySqlException("Unable to connect to any of the specified MySQL hosts."); } var payload = await m_session.ReceiveAsync(cancellationToken).ConfigureAwait(false); var reader = new ByteArrayReader(payload.ArraySegment.Array, payload.ArraySegment.Offset, payload.ArraySegment.Count); var initialHandshake = new InitialHandshakePacket(reader); if (initialHandshake.AuthPluginName != "mysql_native_password") { throw new NotSupportedException("Only 'mysql_native_password' authentication method is supported."); } m_session.ServerVersion = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion)); var response = HandshakeResponse41Packet.Create(initialHandshake, m_connectionStringBuilder.UserID, m_connectionStringBuilder.Password, m_database); payload = new PayloadData(new ArraySegment <byte>(response)); await m_session.SendReplyAsync(payload, cancellationToken).ConfigureAwait(false); await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); // TODO: Check success } else if (m_connectionStringBuilder.ConnectionReset) { if (m_session.ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0) { await m_session.SendAsync(ResetConnectionPayload.Create(), cancellationToken).ConfigureAwait(false); var payload = await m_session.ReceiveReplyAsync(cancellationToken); OkPayload.Create(payload); } else { // MySQL doesn't appear to accept a replayed hashed password (using the challenge from the initial handshake), so just send zeroes // and expect to get a new challenge var payload = ChangeUserPayload.Create(m_connectionStringBuilder.UserID, new byte[20], m_database); await m_session.SendAsync(payload, cancellationToken).ConfigureAwait(false); payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload); if (switchRequest.Name != "mysql_native_password") { throw new NotSupportedException("Only 'mysql_native_password' authentication method is supported."); } var hashedPassword = AuthenticationUtility.HashPassword(switchRequest.Data, 0, m_connectionStringBuilder.Password); payload = new PayloadData(new ArraySegment <byte>(hashedPassword)); await m_session.SendReplyAsync(payload, cancellationToken).ConfigureAwait(false); payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); OkPayload.Create(payload); } } m_hasBeenOpened = true; SetState(ConnectionState.Open); success = true; } catch (MySqlException) { SetState(ConnectionState.Closed); throw; } catch (SocketException ex) { SetState(ConnectionState.Closed); throw new MySqlException("Unable to connect to any of the specified MySQL hosts.", ex); } finally { if (!success) { Utility.Dispose(ref m_session); } } }
static Row?ScanRowAsyncRemainder(ResultSet this_, PayloadData payload, Row?row_) { if (payload.HeaderByte == EofPayload.Signature) { var span = payload.Span; if (this_.Session.SupportsDeprecateEof && OkPayload.IsOk(span, this_.Session.SupportsDeprecateEof)) { var ok = OkPayload.Create(span, this_.Session.SupportsDeprecateEof, this_.Session.SupportsSessionTrack); this_.BufferState = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData; return(null); } if (!this_.Session.SupportsDeprecateEof && EofPayload.IsEof(payload)) { var eof = EofPayload.Create(span); this_.BufferState = (eof.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData; return(null); } } if (row_ is null) { bool isBinaryRow = false; if (payload.HeaderByte == 0 && !this_.Connection.IgnorePrepare) { // this might be a binary row, but it might also be a text row whose first column is zero bytes long; try reading // the row as a series of length-encoded values (the text format) to see if this might plausibly be a text row var isTextRow = false; var reader = new ByteArrayReader(payload.Span); var columnCount = 0; while (reader.BytesRemaining > 0) { int length; var firstByte = reader.ReadByte(); if (firstByte == 0xFB) { // NULL length = 0; } else if (firstByte == 0xFC) { // two-byte length-encoded integer if (reader.BytesRemaining < 2) { break; } length = unchecked ((int)reader.ReadFixedLengthUInt32(2)); } else if (firstByte == 0xFD) { // three-byte length-encoded integer if (reader.BytesRemaining < 3) { break; } length = unchecked ((int)reader.ReadFixedLengthUInt32(3)); } else if (firstByte == 0xFE) { // eight-byte length-encoded integer if (reader.BytesRemaining < 8) { break; } length = checked ((int)reader.ReadFixedLengthUInt64(8)); } else if (firstByte == 0xFF) { // invalid length prefix break; } else { // single-byte length length = firstByte; } if (reader.BytesRemaining < length) { break; } reader.Offset += length; columnCount++; if (columnCount == this_.ColumnDefinitions !.Length) { // if we used up all the bytes reading exactly 'ColumnDefinitions' length-encoded columns, then assume this is a text row if (reader.BytesRemaining == 0) { isTextRow = true; } break; } } isBinaryRow = !isTextRow; } row_ = isBinaryRow ? (Row) new BinaryRow(this_) : new TextRow(this_); } row_.SetData(payload.Memory); this_.m_hasRows = true; this_.BufferState = ResultSetState.ReadingRows; return(row_); }
public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior) { Reset(); try { while (true) { var payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false); var firstByte = payload.HeaderByte; if (firstByte == OkPayload.Signature) { var ok = OkPayload.Create(payload.Span, Session.SupportsDeprecateEof, Session.SupportsSessionTrack); RecordsAffected = (RecordsAffected ?? 0) + ok.AffectedRowCount; LastInsertId = unchecked ((long)ok.LastInsertId); WarningCount = ok.WarningCount; if (ok.NewSchema is object) { Connection.Session.DatabaseOverride = ok.NewSchema; } ColumnDefinitions = null; ColumnTypes = null; State = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData; if (State == ResultSetState.NoMoreData) { break; } } else if (firstByte == LocalInfilePayload.Signature) { try { if (!Connection.AllowLoadLocalInfile) { throw new NotSupportedException("To use LOAD DATA LOCAL INFILE, set AllowLoadLocalInfile=true in the connection string. See https://fl.vu/mysql-load-data"); } var localInfile = LocalInfilePayload.Create(payload.Span); var hasSourcePrefix = localInfile.FileName.StartsWith(MySqlBulkLoader.SourcePrefix, StringComparison.Ordinal); if (!IsHostVerified(Connection) && !hasSourcePrefix) { throw new NotSupportedException("Use SourceStream or SslMode >= VerifyCA for LOAD DATA LOCAL INFILE. See https://fl.vu/mysql-load-data"); } var source = hasSourcePrefix ? MySqlBulkLoader.GetAndRemoveSource(localInfile.FileName) : File.OpenRead(localInfile.FileName); switch (source) { case Stream stream: var buffer = ArrayPool <byte> .Shared.Rent(1048576); try { int byteCount; while ((byteCount = await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false)) > 0) { payload = new PayloadData(new ArraySegment <byte>(buffer, 0, byteCount)); await Session.SendReplyAsync(payload, ioBehavior, CancellationToken.None).ConfigureAwait(false); } } finally { ArrayPool <byte> .Shared.Return(buffer); stream.Dispose(); } break; case MySqlBulkCopy bulkCopy: await bulkCopy.SendDataReaderAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false); break; default: throw new InvalidOperationException("Unsupported Source type: {0}".FormatInvariant(source.GetType().Name)); } } catch (Exception ex) { // store the exception, to be thrown after reading the response packet from the server ReadResultSetHeaderException = new MySqlException("Error during LOAD DATA LOCAL INFILE", ex); } await Session.SendReplyAsync(EmptyPayload.Instance, ioBehavior, CancellationToken.None).ConfigureAwait(false); } else { int ReadColumnCount(ReadOnlySpan <byte> span) { var reader = new ByteArrayReader(span); var columnCount_ = (int)reader.ReadLengthEncodedInteger(); if (reader.BytesRemaining != 0) { throw new MySqlException("Unexpected data at end of column_count packet; see https://github.com/mysql-net/MySqlConnector/issues/324"); } return(columnCount_); } var columnCount = ReadColumnCount(payload.Span); // reserve adequate space to hold a copy of all column definitions (but note that this can be resized below if we guess too small) Utility.Resize(ref m_columnDefinitionPayloads, columnCount * 96); ColumnDefinitions = new ColumnDefinitionPayload[columnCount]; ColumnTypes = new MySqlDbType[columnCount]; for (var column = 0; column < ColumnDefinitions.Length; column++) { payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false); var payloadLength = payload.Span.Length; // 'Session.ReceiveReplyAsync' reuses a shared buffer; make a copy so that the column definitions can always be safely read at any future point if (m_columnDefinitionPayloadUsedBytes + payloadLength > m_columnDefinitionPayloads.Count) { Utility.Resize(ref m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes + payloadLength); } payload.Span.CopyTo(m_columnDefinitionPayloads.Array.AsSpan().Slice(m_columnDefinitionPayloadUsedBytes)); var columnDefinition = ColumnDefinitionPayload.Create(new ResizableArraySegment <byte>(m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes, payloadLength)); ColumnDefinitions[column] = columnDefinition; ColumnTypes[column] = TypeMapper.ConvertToMySqlDbType(columnDefinition, treatTinyAsBoolean: Connection.TreatTinyAsBoolean, guidFormat: Connection.GuidFormat); m_columnDefinitionPayloadUsedBytes += payloadLength; } if (!Session.SupportsDeprecateEof) { payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false); EofPayload.Create(payload.Span); } if (ColumnDefinitions.Length == (Command?.OutParameters?.Count + 1) && ColumnDefinitions[0].Name == SingleCommandPayloadCreator.OutParameterSentinelColumnName) { ContainsCommandParameters = true; } LastInsertId = -1; WarningCount = 0; State = ResultSetState.ReadResultSetHeader; break; } } } catch (Exception ex) { ReadResultSetHeaderException = ex; } finally { BufferState = State; } }
private ValueTask <Row> ScanRowAsync(IOBehavior ioBehavior, Row row, CancellationToken cancellationToken) { // if we've already read past the end of this resultset, Read returns false if (BufferState == ResultSetState.HasMoreData || BufferState == ResultSetState.NoMoreData || BufferState == ResultSetState.None) { return(new ValueTask <Row>((Row)null)); } using (Command.RegisterCancel(cancellationToken)) { var payloadValueTask = Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None); return(payloadValueTask.IsCompletedSuccessfully ? new ValueTask <Row>(ScanRowAsyncRemainder(payloadValueTask.Result, row)) : new ValueTask <Row>(ScanRowAsyncAwaited(payloadValueTask.AsTask(), row, cancellationToken))); } async Task <Row> ScanRowAsyncAwaited(Task <PayloadData> payloadTask, Row row_, CancellationToken token) { PayloadData payloadData; try { payloadData = await payloadTask.ConfigureAwait(false); } catch (MySqlException ex) { BufferState = State = ResultSetState.NoMoreData; if (ex.Number == (int)MySqlErrorCode.QueryInterrupted) { token.ThrowIfCancellationRequested(); } throw; } return(ScanRowAsyncRemainder(payloadData, row_)); } Row ScanRowAsyncRemainder(PayloadData payload, Row row_) { if (payload.HeaderByte == EofPayload.Signature) { if (Session.SupportsDeprecateEof && OkPayload.IsOk(payload, Session.SupportsDeprecateEof)) { var ok = OkPayload.Create(payload, Session.SupportsDeprecateEof); BufferState = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData; m_rowBuffered = null; return(null); } if (!Session.SupportsDeprecateEof && EofPayload.IsEof(payload)) { var eof = EofPayload.Create(payload); BufferState = (eof.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData; m_rowBuffered = null; return(null); } } var reader = new ByteArrayReader(payload.ArraySegment); for (var column = 0; column < m_dataOffsets.Length; column++) { var length = reader.ReadLengthEncodedIntegerOrNull(); m_dataLengths[column] = length == -1 ? 0 : length; m_dataOffsets[column] = length == -1 ? -1 : reader.Offset; reader.Offset += m_dataLengths[column]; } if (row_ == null) { row_ = new Row(this); } row_.SetData(m_dataLengths, m_dataOffsets, payload.ArraySegment); m_rowBuffered = row_; m_hasRows = true; BufferState = ResultSetState.ReadingRows; return(row_); } }
public async Task <ResultSet> ReadResultSetHeaderAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) { // ResultSet can be re-used, so initialize everything BufferState = ResultSetState.None; ColumnDefinitions = null; LastInsertId = 0; RecordsAffected = 0; State = ResultSetState.None; m_dataLengths = null; m_dataOffsets = null; m_readBuffer.Clear(); m_row = null; m_rowBuffered = null; while (true) { var payload = await Session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); var firstByte = payload.HeaderByte; if (firstByte == OkPayload.Signature) { var ok = OkPayload.Create(payload); RecordsAffected = ok.AffectedRowCount; LastInsertId = ok.LastInsertId; ColumnDefinitions = null; State = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData; if (State == ResultSetState.NoMoreData) { break; } } else if (firstByte == 0xFB) { throw new NotSupportedException("Don't support LOCAL_INFILE_Request"); } else { var reader = new ByteArrayReader(payload.ArraySegment); var columnCount = (int)reader.ReadLengthEncodedInteger(); ColumnDefinitions = new ColumnDefinitionPayload[columnCount]; m_dataOffsets = new int[columnCount]; m_dataLengths = new int[columnCount]; for (var column = 0; column < ColumnDefinitions.Length; column++) { payload = await Session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); ColumnDefinitions[column] = ColumnDefinitionPayload.Create(payload); } payload = await Session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); EofPayload.Create(payload); LastInsertId = -1; State = ResultSetState.ReadResultSetHeader; break; } } BufferState = State; return(this); }
public async Task <ResultSet> ReadResultSetHeaderAsync(IOBehavior ioBehavior) { // ResultSet can be re-used, so initialize everything BufferState = ResultSetState.None; ColumnDefinitions = null; LastInsertId = 0; RecordsAffected = 0; State = ResultSetState.None; m_columnDefinitionPayloadUsedBytes = 0; m_dataLengths = null; m_dataOffsets = null; m_readBuffer.Clear(); m_row = null; m_rowBuffered = null; m_hasRows = false; try { while (true) { var payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false); var firstByte = payload.HeaderByte; if (firstByte == OkPayload.Signature) { var ok = OkPayload.Create(payload); RecordsAffected += ok.AffectedRowCount; LastInsertId = ok.LastInsertId; ColumnDefinitions = null; State = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData; if (State == ResultSetState.NoMoreData) { break; } } else if (firstByte == LocalInfilePayload.Signature) { try { var localInfile = LocalInfilePayload.Create(payload); if (!IsHostVerified(Connection) && !localInfile.FileName.StartsWith(MySqlBulkLoader.StreamPrefix, StringComparison.Ordinal)) { throw new NotSupportedException("Use SourceStream or SslMode >= VerifyCA for LOAD DATA LOCAL INFILE"); } using (var stream = localInfile.FileName.StartsWith(MySqlBulkLoader.StreamPrefix, StringComparison.Ordinal) ? MySqlBulkLoader.GetAndRemoveStream(localInfile.FileName) : File.OpenRead(localInfile.FileName)) { byte[] readBuffer = new byte[65536]; int byteCount; while ((byteCount = await stream.ReadAsync(readBuffer, 0, readBuffer.Length).ConfigureAwait(false)) > 0) { payload = new PayloadData(new ArraySegment <byte>(readBuffer, 0, byteCount)); await Session.SendReplyAsync(payload, ioBehavior, CancellationToken.None).ConfigureAwait(false); } } } catch (Exception ex) { // store the exception, to be thrown after reading the response packet from the server ReadResultSetHeaderException = new MySqlException("Error during LOAD DATA LOCAL INFILE", ex); } await Session.SendReplyAsync(EmptyPayload.Create(), ioBehavior, CancellationToken.None).ConfigureAwait(false); } else { var reader = new ByteArrayReader(payload.ArraySegment); var columnCount = (int)reader.ReadLengthEncodedInteger(); if (reader.BytesRemaining != 0) { throw new MySqlException("Unexpected data at end of column_count packet; see https://github.com/mysql-net/MySqlConnector/issues/324"); } // reserve adequate space to hold a copy of all column definitions (but note that this can be resized below if we guess too small) Array.Resize(ref m_columnDefinitionPayloads, columnCount * 96); ColumnDefinitions = new ColumnDefinitionPayload[columnCount]; m_dataOffsets = new int[columnCount]; m_dataLengths = new int[columnCount]; for (var column = 0; column < ColumnDefinitions.Length; column++) { payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false); var arraySegment = payload.ArraySegment; // 'Session.ReceiveReplyAsync' reuses a shared buffer; make a copy so that the column definitions can always be safely read at any future point if (m_columnDefinitionPayloadUsedBytes + arraySegment.Count > m_columnDefinitionPayloads.Length) { Array.Resize(ref m_columnDefinitionPayloads, Math.Max(m_columnDefinitionPayloadUsedBytes + arraySegment.Count, m_columnDefinitionPayloadUsedBytes * 2)); } Buffer.BlockCopy(arraySegment.Array, arraySegment.Offset, m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes, arraySegment.Count); ColumnDefinitions[column] = ColumnDefinitionPayload.Create(new ArraySegment <byte>(m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes, arraySegment.Count)); m_columnDefinitionPayloadUsedBytes += arraySegment.Count; } if (!Session.SupportsDeprecateEof) { payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false); EofPayload.Create(payload); } LastInsertId = -1; State = ResultSetState.ReadResultSetHeader; break; } } } catch (Exception ex) { ReadResultSetHeaderException = ex; } finally { BufferState = State; } return(this); }
public async Task <ResultSet> ReadResultSetHeaderAsync(IOBehavior ioBehavior) { // ResultSet can be re-used, so initialize everything BufferState = ResultSetState.None; ColumnDefinitions = null; LastInsertId = 0; RecordsAffected = 0; State = ResultSetState.None; m_dataLengths = null; m_dataOffsets = null; m_readBuffer.Clear(); m_row = null; m_rowBuffered = null; m_hasRows = false; try { while (true) { var payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false); var firstByte = payload.HeaderByte; if (firstByte == OkPayload.Signature) { var ok = OkPayload.Create(payload); RecordsAffected += ok.AffectedRowCount; LastInsertId = ok.LastInsertId; ColumnDefinitions = null; State = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData; if (State == ResultSetState.NoMoreData) { break; } } else if (firstByte == LocalInfilePayload.Signature) { try { var localInfile = LocalInfilePayload.Create(payload); using (var stream = localInfile.FileName.StartsWith(MySqlBulkLoader.StreamPrefix, StringComparison.Ordinal) ? MySqlBulkLoader.GetAndRemoveStream(localInfile.FileName) : File.OpenRead(localInfile.FileName)) { byte[] readBuffer = new byte[65536]; int byteCount; while ((byteCount = await stream.ReadAsync(readBuffer, 0, readBuffer.Length).ConfigureAwait(false)) > 0) { payload = new PayloadData(new ArraySegment <byte>(readBuffer, 0, byteCount)); await Session.SendReplyAsync(payload, ioBehavior, CancellationToken.None).ConfigureAwait(false); } } } catch (Exception ex) { // store the exception, to be thrown after reading the response packet from the server ReadResultSetHeaderException = new MySqlException("Error during LOAD DATA LOCAL INFILE", ex); } await Session.SendReplyAsync(EmptyPayload.Create(), ioBehavior, CancellationToken.None).ConfigureAwait(false); } else { var reader = new ByteArrayReader(payload.ArraySegment); var columnCount = (int)reader.ReadLengthEncodedInteger(); if (reader.BytesRemaining != 0) { throw new MySqlException("Unexpected data at end of column_count packet; see https://github.com/mysql-net/MySqlConnector/issues/324"); } ColumnDefinitions = new ColumnDefinitionPayload[columnCount]; m_dataOffsets = new int[columnCount]; m_dataLengths = new int[columnCount]; for (var column = 0; column < ColumnDefinitions.Length; column++) { payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false); ColumnDefinitions[column] = ColumnDefinitionPayload.Create(payload); } if (!Session.SupportsDeprecateEof) { payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false); EofPayload.Create(payload); } LastInsertId = -1; State = ResultSetState.ReadResultSetHeader; break; } } } catch (Exception ex) { ReadResultSetHeaderException = ex; } finally { BufferState = State; } return(this); }
private ValueTask <Row> ScanRowAsync(IOBehavior ioBehavior, Row row, CancellationToken cancellationToken) { // if we've already read past the end of this resultset, Read returns false if (BufferState == ResultSetState.HasMoreData || BufferState == ResultSetState.NoMoreData || BufferState == ResultSetState.None) { return(new ValueTask <Row>((Row)null)); } using (Command.RegisterCancel(cancellationToken)) { var payloadValueTask = Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None); return(payloadValueTask.IsCompletedSuccessfully ? new ValueTask <Row>(ScanRowAsyncRemainder(payloadValueTask.Result, row)) : new ValueTask <Row>(ScanRowAsyncAwaited(payloadValueTask.AsTask(), row, cancellationToken))); } async Task <Row> ScanRowAsyncAwaited(Task <PayloadData> payloadTask, Row row_, CancellationToken token) { PayloadData payloadData; try { payloadData = await payloadTask.ConfigureAwait(false); } catch (MySqlException ex) { BufferState = State = ResultSetState.NoMoreData; if (ex.Number == (int)MySqlErrorCode.QueryInterrupted) { token.ThrowIfCancellationRequested(); } throw; } return(ScanRowAsyncRemainder(payloadData, row_)); } Row ScanRowAsyncRemainder(PayloadData payload, Row row_) { if (payload.HeaderByte == EofPayload.Signature) { var span = payload.AsSpan(); if (Session.SupportsDeprecateEof && OkPayload.IsOk(span, Session.SupportsDeprecateEof)) { var ok = OkPayload.Create(span, Session.SupportsDeprecateEof, Session.SupportsSessionTrack); BufferState = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData; m_rowBuffered = null; return(null); } if (!Session.SupportsDeprecateEof && EofPayload.IsEof(payload)) { var eof = EofPayload.Create(span); BufferState = (eof.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData; m_rowBuffered = null; return(null); } } if (row_ == null) { row_ = DataReader.ResultSetProtocol == ResultSetProtocol.Binary ? (Row) new BinaryRow(this) : new TextRow(this); } row_.SetData(payload.ArraySegment); m_rowBuffered = row_; m_hasRows = true; BufferState = ResultSetState.ReadingRows; return(row_); } }
public async Task ConnectAsync(ConnectionSettings cs, ILoadBalancer loadBalancer, IOBehavior ioBehavior, CancellationToken cancellationToken) { try { lock (m_lock) { VerifyState(State.Created); m_state = State.Connecting; } var connected = false; if (cs.ConnectionType == ConnectionType.Tcp) { connected = await OpenTcpSocketAsync(cs, loadBalancer, ioBehavior, cancellationToken).ConfigureAwait(false); } else if (cs.ConnectionType == ConnectionType.Unix) { connected = await OpenUnixSocketAsync(cs, ioBehavior, cancellationToken).ConfigureAwait(false); } if (!connected) { lock (m_lock) m_state = State.Failed; throw new MySqlException("Unable to connect to any of the specified MySQL hosts."); } var byteHandler = new SocketByteHandler(m_socket); m_payloadHandler = new StandardPayloadHandler(byteHandler); var payload = await ReceiveAsync(ioBehavior, cancellationToken).ConfigureAwait(false); var initialHandshake = InitialHandshakePayload.Create(payload); // if PluginAuth is supported, then use the specified auth plugin; else, fall back to protocol capabilities to determine the auth type to use string authPluginName; if ((initialHandshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0) { authPluginName = initialHandshake.AuthPluginName; } else { authPluginName = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SecureConnection) == 0 ? "mysql_old_password" : "mysql_native_password"; } if (authPluginName != "mysql_native_password" && authPluginName != "sha256_password" && authPluginName != "caching_sha2_password") { throw new NotSupportedException("Authentication method '{0}' is not supported.".FormatInvariant(initialHandshake.AuthPluginName)); } ServerVersion = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion)); ConnectionId = initialHandshake.ConnectionId; AuthPluginData = initialHandshake.AuthPluginData; m_useCompression = cs.UseCompression && (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Compress) != 0; var serverSupportsSsl = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Ssl) != 0; if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl)) { if (!serverSupportsSsl) { throw new MySqlException("Server does not support SSL"); } await InitSslAsync(initialHandshake.ProtocolCapabilities, cs, ioBehavior, cancellationToken).ConfigureAwait(false); } m_supportsConnectionAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ConnectionAttributes) != 0; if (m_supportsConnectionAttributes && s_connectionAttributes == null) { s_connectionAttributes = CreateConnectionAttributes(); } m_supportsDeprecateEof = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0; payload = HandshakeResponse41Payload.Create(initialHandshake, cs, m_useCompression, m_supportsConnectionAttributes ? s_connectionAttributes : null); await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); // if server doesn't support the authentication fast path, it will send a new challenge if (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature) { payload = await SwitchAuthenticationAsync(cs, payload, ioBehavior, cancellationToken).ConfigureAwait(false); } OkPayload.Create(payload); if (m_useCompression) { m_payloadHandler = new CompressedPayloadHandler(m_payloadHandler.ByteHandler); } } catch (IOException ex) { throw new MySqlException("Couldn't connect to server", ex); } }
private ValueTask <Row> ScanRowAsync(IOBehavior ioBehavior, Row row, CancellationToken cancellationToken) { // if we've already read past the end of this resultset, Read returns false if (BufferState == ResultSetState.HasMoreData || BufferState == ResultSetState.NoMoreData || BufferState == ResultSetState.None) { return(new ValueTask <Row>((Row)null)); } using (Command.CancellableCommand.RegisterCancel(cancellationToken)) { var payloadValueTask = Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None); return(payloadValueTask.IsCompletedSuccessfully ? new ValueTask <Row>(ScanRowAsyncRemainder(payloadValueTask.Result, row)) : new ValueTask <Row>(ScanRowAsyncAwaited(payloadValueTask.AsTask(), row, cancellationToken))); } async Task <Row> ScanRowAsyncAwaited(Task <PayloadData> payloadTask, Row row_, CancellationToken token) { PayloadData payloadData; try { payloadData = await payloadTask.ConfigureAwait(false); } catch (MySqlException ex) { BufferState = State = ResultSetState.NoMoreData; if (ex.Number == (int)MySqlErrorCode.QueryInterrupted) { token.ThrowIfCancellationRequested(); } throw; } return(ScanRowAsyncRemainder(payloadData, row_)); } Row ScanRowAsyncRemainder(PayloadData payload, Row row_) { if (payload.HeaderByte == EofPayload.Signature) { var span = payload.AsSpan(); if (Session.SupportsDeprecateEof && OkPayload.IsOk(span, Session.SupportsDeprecateEof)) { var ok = OkPayload.Create(span, Session.SupportsDeprecateEof, Session.SupportsSessionTrack); BufferState = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData; m_rowBuffered = null; return(null); } if (!Session.SupportsDeprecateEof && EofPayload.IsEof(payload)) { var eof = EofPayload.Create(span); BufferState = (eof.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData; m_rowBuffered = null; return(null); } } if (row_ is null) { bool isBinaryRow = false; if (payload.HeaderByte == 0 && !Connection.IgnorePrepare) { // this might be a binary row, but it might also be a text row whose first column is zero bytes long; try reading // the row as a series of length-encoded values (the text format) to see if this might plausibly be a text row var isTextRow = false; var reader = new ByteArrayReader(payload.AsSpan()); var columnCount = 0; while (reader.BytesRemaining > 0) { int length; var firstByte = reader.ReadByte(); if (firstByte == 0xFB) { // NULL length = 0; } else if (firstByte == 0xFC) { // two-byte length-encoded integer if (reader.BytesRemaining < 2) { break; } length = unchecked ((int)reader.ReadFixedLengthUInt32(2)); } else if (firstByte == 0xFD) { // three-byte length-encoded integer if (reader.BytesRemaining < 3) { break; } length = unchecked ((int)reader.ReadFixedLengthUInt32(3)); } else if (firstByte == 0xFE) { // eight-byte length-encoded integer if (reader.BytesRemaining < 8) { break; } length = checked ((int)reader.ReadFixedLengthUInt64(8)); } else if (firstByte == 0xFF) { // invalid length prefix break; } else { // single-byte length length = firstByte; } if (reader.BytesRemaining < length) { break; } reader.Offset += length; columnCount++; if (columnCount == ColumnDefinitions.Length) { // if we used up all the bytes reading exactly 'ColumnDefinitions' length-encoded columns, then assume this is a text row if (reader.BytesRemaining == 0) { isTextRow = true; } break; } } isBinaryRow = !isTextRow; } row_ = isBinaryRow ? (Row) new BinaryRow(this) : new TextRow(this); } row_.SetData(payload.ArraySegment); m_rowBuffered = row_; m_hasRows = true; BufferState = ResultSetState.ReadingRows; return(row_); } }