Пример #1
0
        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 not null)
                        {
                            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(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 = ExceptionDispatchInfo.Capture(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 = ExceptionDispatchInfo.Capture(ex);
            }
            finally
            {
                BufferState = State;
            }
        }
Пример #2
0
 internal Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
Пример #3
0
 public ValueTask <int> WriteBytesAsync(ArraySegment <byte> data, IOBehavior ioBehavior) => throw new NotSupportedException();
Пример #4
0
 private ValueTask <int> ScanResultSetAsync(IOBehavior ioBehavior, ResultSet resultSet, CancellationToken cancellationToken)
 {
     if (!m_hasMoreResults)
     {
         return(default);
Пример #5
0
        internal async Task <bool> NextResultAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            VerifyNotDisposed();
            try
            {
                do
                {
                    while (true)
                    {
                        await m_resultSet !.ReadEntireAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
                        await ScanResultSetAsync(ioBehavior, m_resultSet, cancellationToken).ConfigureAwait(false);

                        if (m_hasMoreResults && m_resultSet.ContainsCommandParameters)
                        {
                            await ReadOutParametersAsync(Command !, m_resultSet, ioBehavior, cancellationToken).ConfigureAwait(false);
                        }
                        else
                        {
                            break;
                        }
                    }

                    if (!m_hasMoreResults)
                    {
                        if (m_commandListPosition.CommandIndex < m_commandListPosition.Commands.Count)
                        {
                            Command = m_commandListPosition.Commands[m_commandListPosition.CommandIndex];
                            using (Command.CancellableCommand.RegisterCancel(cancellationToken))
                            {
                                var writer = new ByteBufferWriter();
                                if (!Command.Connection !.Session.IsCancelingQuery && m_payloadCreator.WriteQueryCommand(ref m_commandListPosition, m_cachedProcedures !, writer))
                                {
                                    using var payload = writer.ToPayloadData();
                                    await Command.Connection.Session.SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);

                                    await m_resultSet.ReadResultSetHeaderAsync(ioBehavior).ConfigureAwait(false);

                                    ActivateResultSet();
                                    m_hasMoreResults = true;
                                }
                            }
                        }
                    }
                    else
                    {
                        ActivateResultSet();
                    }
                }while (m_hasMoreResults && (Command !.CommandBehavior & (CommandBehavior.SingleResult | CommandBehavior.SingleRow)) != 0);

                if (!m_hasMoreResults)
                {
                    m_resultSet.Reset();
                }
#if !NETSTANDARD1_3
                m_schemaTable = null;
#endif
                return(m_hasMoreResults);
            }
            catch (MySqlException)
            {
                m_resultSet !.Reset();
                m_hasMoreResults = false;
#if !NETSTANDARD1_3
                m_schemaTable = null;
#endif
                throw;
            }
        }
Пример #6
0
        private async ValueTask <MySqlTransaction> BeginTransactionAsync(IsolationLevel isolationLevel, bool?isReadOnly, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            if (State != ConnectionState.Open)
            {
                throw new InvalidOperationException("Connection is not open.");
            }
            if (CurrentTransaction is object)
            {
                throw new InvalidOperationException("Transactions may not be nested.");
            }
#if !NETSTANDARD1_3
            if (m_enlistedTransaction is object)
            {
                throw new InvalidOperationException("Cannot begin a transaction when already enlisted in a transaction.");
            }
#endif

            var isolationLevelValue = isolationLevel switch
            {
                IsolationLevel.ReadUncommitted => "read uncommitted",
                IsolationLevel.ReadCommitted => "read committed",
                IsolationLevel.RepeatableRead => "repeatable read",
                IsolationLevel.Serializable => "serializable",
                IsolationLevel.Snapshot => "repeatable read",

                // "In terms of the SQL:1992 transaction isolation levels, the default InnoDB level is REPEATABLE READ." - http://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-model.html
                IsolationLevel.Unspecified => "repeatable read",

                _ => throw new NotSupportedException("IsolationLevel.{0} is not supported.".FormatInvariant(isolationLevel))
            };

            using (var cmd = new MySqlCommand($"set session transaction isolation level {isolationLevelValue};", this))
            {
                await cmd.ExecuteNonQueryAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

                var consistentSnapshotText = isolationLevel == IsolationLevel.Snapshot ? " with consistent snapshot" : "";
                var readOnlyText           = isReadOnly switch
                {
                    true => " read only",
                    false => " read write",
                    null => "",
                };
                var separatorText = (consistentSnapshotText.Length == 0 || readOnlyText.Length == 0) ? "" : ",";
                cmd.CommandText = $"start transaction{consistentSnapshotText}{separatorText}{readOnlyText};";
                await cmd.ExecuteNonQueryAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
            }

            var transaction = new MySqlTransaction(this, isolationLevel);
            CurrentTransaction = transaction;
            return(transaction);
        }
Пример #7
0
        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);
                    }
                }

                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_);
            }
        }
Пример #8
0
        private async Task <bool> OpenSocketAsync(IEnumerable <string> hostnames, int port, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            foreach (var hostname in hostnames)
            {
                IPAddress[] ipAddresses;
                try
                {
#if NETSTANDARD1_3
                    // Dns.GetHostAddresses isn't available until netstandard 2.0: https://github.com/dotnet/corefx/pull/11950
                    ipAddresses = await Dns.GetHostAddressesAsync(hostname).ConfigureAwait(false);
#else
                    if (ioBehavior == IOBehavior.Asynchronous)
                    {
                        ipAddresses = await Dns.GetHostAddressesAsync(hostname).ConfigureAwait(false);
                    }
                    else
                    {
                        ipAddresses = Dns.GetHostAddresses(hostname);
                    }
#endif
                }
                catch (SocketException)
                {
                    // name couldn't be resolved
                    continue;
                }

                // need to try IP Addresses one at a time: https://github.com/dotnet/corefx/issues/5829
                foreach (var ipAddress in ipAddresses)
                {
                    Socket socket = null;
                    try
                    {
                        socket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                        using (cancellationToken.Register(() => socket.Dispose()))
                        {
                            try
                            {
                                if (ioBehavior == IOBehavior.Asynchronous)
                                {
#if NETSTANDARD1_3
                                    await socket.ConnectAsync(ipAddress, port).ConfigureAwait(false);
#else
                                    await Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, ipAddress, port, null).ConfigureAwait(false);
#endif
                                }
                                else
                                {
#if NETSTANDARD1_3
                                    await socket.ConnectAsync(ipAddress, port).ConfigureAwait(false);
#else
                                    socket.Connect(ipAddress, port);
#endif
                                }
                            }
                            catch (ObjectDisposedException ex) when(cancellationToken.IsCancellationRequested)
                            {
                                throw new MySqlException("Connect Timeout expired.", ex);
                            }
                        }
                    }
                    catch (SocketException)
                    {
                        Utility.Dispose(ref socket);
                        continue;
                    }

                    m_socket      = socket;
                    m_transmitter = new PacketTransmitter(m_socket);
                    m_state       = State.Connected;
                    return(true);
                }
            }
            return(false);
        }
Пример #9
0
        private Task TryAsync <TArg>(Func <TArg, IOBehavior, CancellationToken, Task> func, TArg arg, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            VerifyConnected();
            var task = func(arg, ioBehavior, cancellationToken);

            if (task.Status == TaskStatus.RanToCompletion)
            {
                return(task);
            }

            return(task.ContinueWith(TryAsyncContinuation, cancellationToken, TaskContinuationOptions.LazyCancellation | TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default));
        }
Пример #10
0
 // Continues a conversation with the server by receiving a response to a packet sent with 'Send' or 'SendReply'.
 public ValueTask <PayloadData> ReceiveReplyAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
 => TryAsync(m_transmitter.ReceiveReplyAsync, ioBehavior, cancellationToken);
Пример #11
0
 // Continues a conversation with the server by sending a reply to a packet received with 'Receive' or 'ReceiveReply'.
 public Task SendReplyAsync(PayloadData payload, IOBehavior ioBehavior, CancellationToken cancellationToken)
 => TryAsync(m_transmitter.SendReplyAsync, payload, ioBehavior, cancellationToken);
Пример #12
0
        public async Task ResetConnectionAsync(string userId, string password, string database, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            if (ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0)
            {
                await SendAsync(ResetConnectionPayload.Create(), ioBehavior, cancellationToken).ConfigureAwait(false);

                var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

                OkPayload.Create(payload);

                // the "reset connection" packet also resets the connection charset, so we need to change that back to our default
                payload = new PayloadData(new ArraySegment <byte>(Payload.CreateEofStringPayload(CommandKind.Query, "SET NAMES utf8mb4;")));
                await SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);

                payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

                OkPayload.Create(payload);
            }
            else
            {
                // optimistically hash the password with the challenge from the initial handshake (supported by MariaDB; doesn't appear to be supported by MySQL)
                var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData, 0, password);
                var payload        = ChangeUserPayload.Create(userId, hashedPassword, database);
                await SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);

                payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

                if (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature)
                {
                    // if the server didn't support the hashed password; rehash with the new challenge
                    var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload);
                    if (switchRequest.Name != "mysql_native_password")
                    {
                        throw new NotSupportedException("Authentication method '{0}' is not supported.".FormatInvariant(switchRequest.Name));
                    }
                    hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(switchRequest.Data, 0, password);
                    payload        = new PayloadData(new ArraySegment <byte>(hashedPassword));
                    await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);

                    payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
                }
                OkPayload.Create(payload);
            }
        }
Пример #13
0
        public static async Task <PayloadData> AuthenticateAsync(ConnectionSettings cs, byte[] switchRequestPayloadData,
                                                                 ServerSession session, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            using var innerStream     = new NegotiateToMySqlConverterStream(session, ioBehavior, cancellationToken);
            using var negotiateStream = new NegotiateStream(innerStream);
            var targetName = cs.ServerSPN ?? GetServicePrincipalName(switchRequestPayloadData);

#if NETSTANDARD1_3
            await negotiateStream.AuthenticateAsClientAsync(CredentialCache.DefaultNetworkCredentials, targetName).ConfigureAwait(false);
#else
            if (ioBehavior == IOBehavior.Synchronous)
            {
                negotiateStream.AuthenticateAsClient(CredentialCache.DefaultNetworkCredentials, targetName);
            }
            else
            {
                await negotiateStream.AuthenticateAsClientAsync(CredentialCache.DefaultNetworkCredentials, targetName).ConfigureAwait(false);
            }
#endif
            if (cs.ServerSPN is object && !negotiateStream.IsMutuallyAuthenticated)
            {
                // Negotiate used NTLM fallback, server name cannot be verified.
                throw new AuthenticationException(String.Format(
                                                      "GSSAPI : Unable to verify server principal name using authentication type {0}",
                                                      negotiateStream.RemoteIdentity?.AuthenticationType));
            }
            if (innerStream.MySQLProtocolPayload is PayloadData payload)
            {
                // return already pre-read OK packet.
                return(payload);
            }

            // Read final OK packet from server
            return(await session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false));
        }
Пример #14
0
        private async Task <int> LoadAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            if (Connection is null)
            {
                throw new InvalidOperationException("Connection not set");
            }

            if (!string.IsNullOrWhiteSpace(FileName) && SourceStream is object)
            {
                throw new InvalidOperationException("Cannot set both FileName and SourceStream");
            }

            // LOCAL INFILE case
            if (!string.IsNullOrWhiteSpace(FileName) && Local)
            {
                SourceStream = CreateFileStream(FileName !);
                FileName     = null;
            }

            if (string.IsNullOrWhiteSpace(FileName) && SourceStream is object)
            {
                if (!Local)
                {
                    throw new InvalidOperationException("Cannot use SourceStream when Local is not true.");
                }

                FileName = GenerateSourceStreamName();
                lock (s_lock)
                    s_streams.Add(FileName, SourceStream);
            }

            if (string.IsNullOrWhiteSpace(FileName) || string.IsNullOrWhiteSpace(TableName))
            {
                // This is intentionally a different exception to what is thrown by MySql.Data because
                // it does not handle null or empty FileName and TableName.
                // The baseline client simply tries to use the given values, resulting in a NullReferenceException for
                // a null FileName, a MySqlException with an inner FileStream exception for an empty FileName,
                // and a MySqlException with a syntax error if the TableName is null or empty.
                throw new InvalidOperationException("FileName or SourceStream, and TableName are required.");
            }

            bool closeConnection = false;

            if (Connection.State != ConnectionState.Open)
            {
                closeConnection = true;
                Connection.Open();
            }

            bool closeStream = SourceStream is object;

            try
            {
                if (Local && !Connection.AllowLoadLocalInfile)
                {
                    throw new NotSupportedException("To use MySqlBulkLoader.Local=true, set AllowLoadLocalInfile=true in the connection string. See https://fl.vu/mysql-load-data");
                }

                var commandString = BuildSqlCommand();
                var cmd           = new MySqlCommand(commandString, Connection, Connection.CurrentTransaction)
                {
                    CommandTimeout = Timeout,
                };
                var result = await cmd.ExecuteNonQueryAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

                closeStream = false;
                return(result);
            }
            finally
            {
                if (closeStream)
                {
                    using (GetAndRemoveStream(FileName !))
                    {
                        // close the stream
                    }
                }

                if (closeConnection)
                {
                    Connection.Close();
                }
            }
        }
Пример #15
0
 private async ValueTask <int> WriteToServerAsync(IDataReader dataReader, IOBehavior ioBehavior, CancellationToken cancellationToken)
Пример #16
0
        private ValueTask <PayloadData> TryAsync(Func <IOBehavior, CancellationToken, ValueTask <PayloadData> > func, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            VerifyConnected();
            var task = func(ioBehavior, cancellationToken);

            if (task.IsCompletedSuccessfully)
            {
                if (task.Result.HeaderByte != ErrorPayload.Signature)
                {
                    return(task);
                }

                var exception = ErrorPayload.Create(task.Result).ToException();
#if NETSTANDARD1_3
                return(new ValueTask <PayloadData>(Task.FromException <PayloadData>(exception)));
#else
                var tcs = new TaskCompletionSource <PayloadData>();
                tcs.SetException(exception);
                return(new ValueTask <PayloadData>(tcs.Task));
#endif
            }

            return(new ValueTask <PayloadData>(task.AsTask()
                                               .ContinueWith(TryAsyncContinuation, cancellationToken, TaskContinuationOptions.LazyCancellation | TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)));
        }
Пример #17
0
        private async ValueTask WriteToServerAsync(IDataReader dataReader, IOBehavior ioBehavior, CancellationToken cancellationToken)
#endif
        {
            var tableName = DestinationTableName ?? throw new InvalidOperationException("DestinationTableName must be set before calling WriteToServer");

            var bulkLoader = new MySqlBulkLoader(m_connection)
            {
                CharacterSet            = "utf8mb4",
                EscapeCharacter         = '\\',
                FieldQuotationCharacter = '\0',
                FieldTerminator         = "\t",
                LinePrefix                                 = null,
                LineTerminator                             = "\n",
                Local                                      = true,
                NumberOfLinesToSkip                        = 0,
                Source                                     = dataReader ?? throw new ArgumentNullException(nameof(dataReader)),
                                                 TableName = tableName,
                                                 Timeout   = BulkCopyTimeout,
            };

            var closeConnection = false;

            if (m_connection.State != ConnectionState.Open)
            {
                m_connection.Open();
                closeConnection = true;
            }

            using (var cmd = new MySqlCommand("select * from " + QuoteIdentifier(tableName) + ";", m_connection, m_transaction))
                using (var reader = (MySqlDataReader)await cmd.ExecuteReaderAsync(CommandBehavior.SchemaOnly, ioBehavior, cancellationToken).ConfigureAwait(false))
                {
                    var schema = reader.GetColumnSchema();
                    for (var i = 0; i < schema.Count; i++)
                    {
                        if (schema[i].DataTypeName == "BIT")
                        {
                            bulkLoader.Columns.Add($"@col{i}");
                            bulkLoader.Expressions.Add($"`{reader.GetName(i)}` = CAST(@col{i} AS UNSIGNED)");
                        }
                        else if (schema[i].DataTypeName == "YEAR")
                        {
                            // the current code can't distinguish between 0 = 0000 and 0 = 2000
                            throw new NotSupportedException("'YEAR' columns are not supported by MySqlBulkLoader.");
                        }
                        else
                        {
                            var type = schema[i].DataType;
                            if (type == typeof(byte[]) || (type == typeof(Guid) && (m_connection.GuidFormat == MySqlGuidFormat.Binary16 || m_connection.GuidFormat == MySqlGuidFormat.LittleEndianBinary16 || m_connection.GuidFormat == MySqlGuidFormat.TimeSwapBinary16)))
                            {
                                bulkLoader.Columns.Add($"@col{i}");
                                bulkLoader.Expressions.Add($"`{reader.GetName(i)}` = UNHEX(@col{i})");
                            }
                            else
                            {
                                bulkLoader.Columns.Add(QuoteIdentifier(reader.GetName(i)));
                            }
                        }
                    }
                }

            await bulkLoader.LoadAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

            if (closeConnection)
            {
                m_connection.Close();
            }

#if !NETSTANDARD2_1 && !NETCOREAPP3_0
            return(default);
Пример #18
0
        public async Task ConnectAsync(IEnumerable <string> hosts, int port, string userId, string password, string database, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            var connected = await OpenSocketAsync(hosts, port, ioBehavior, cancellationToken).ConfigureAwait(false);

            if (!connected)
            {
                throw new MySqlException("Unable to connect to any of the specified MySQL hosts.");
            }

            var payload = await ReceiveAsync(ioBehavior, 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("Authentication method '{0}' is not supported.".FormatInvariant(initialHandshake.AuthPluginName));
            }
            ServerVersion  = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion));
            ConnectionId   = initialHandshake.ConnectionId;
            AuthPluginData = initialHandshake.AuthPluginData;

            var response = HandshakeResponse41Packet.Create(initialHandshake, userId, password, database);

            payload = new PayloadData(new ArraySegment <byte>(response));
            await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
            await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
        }
Пример #19
0
 internal Task CloseAsync(IOBehavior ioBehavior) => CloseAsync(changeState: true, ioBehavior);
Пример #20
0
        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);
        }
Пример #21
0
        public async Task <ResultSet> ReadResultSetHeaderAsync(IOBehavior ioBehavior)
        {
            // ResultSet can be re-used, so initialize everything
            BufferState       = ResultSetState.None;
            ColumnDefinitions = null;
            ColumnTypes       = null;
            LastInsertId      = 0;
            RecordsAffected   = null;
            State             = ResultSetState.None;
            m_columnDefinitionPayloadUsedBytes = 0;
            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 = (RecordsAffected ?? 0) + ok.AffectedRowCount;
                        LastInsertId    = unchecked ((long)ok.LastInsertId);
                        if (ok.NewSchema != null)
                        {
                            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
                        {
                            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.Instance, ioBehavior, CancellationToken.None).ConfigureAwait(false);
                    }
                    else
                    {
                        int ReadColumnCount(ArraySegment <byte> arraySegment)
                        {
                            var reader       = new ByteArrayReader(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");
                            }
                            return(columnCount_);
                        }
                        var columnCount = ReadColumnCount(payload.ArraySegment);

                        // 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 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.Count)
                            {
                                Utility.Resize(ref m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes + arraySegment.Count);
                            }
                            Buffer.BlockCopy(arraySegment.Array, arraySegment.Offset, m_columnDefinitionPayloads.Array, m_columnDefinitionPayloadUsedBytes, arraySegment.Count);

                            var columnDefinition = ColumnDefinitionPayload.Create(new ResizableArraySegment <byte>(m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes, arraySegment.Count));
                            ColumnDefinitions[column]           = columnDefinition;
                            ColumnTypes[column]                 = TypeMapper.ConvertToMySqlDbType(columnDefinition, treatTinyAsBoolean: Connection.TreatTinyAsBoolean, guidFormat: Connection.GuidFormat);
                            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);
        }
Пример #22
0
        public static async Task <CachedProcedure?> FillAsync(IOBehavior ioBehavior, MySqlConnection connection, string schema, string component, CancellationToken cancellationToken)
        {
            // try to use mysql.proc first, as it is much faster
            if (connection.Session.ServerVersion.Version < ServerVersions.RemovesMySqlProcTable && !connection.Session.ProcAccessDenied)
            {
                try
                {
                    using var cmd   = connection.CreateCommand();
                    cmd.Transaction = connection.CurrentTransaction;
                    cmd.CommandText = @"SELECT param_list, returns FROM mysql.proc WHERE db = @schema AND name = @component";
                    cmd.Parameters.AddWithValue("@schema", schema);
                    cmd.Parameters.AddWithValue("@component", component);

                    using var reader = await cmd.ExecuteReaderNoResetTimeoutAsync(CommandBehavior.Default, ioBehavior, cancellationToken).ConfigureAwait(false);

                    var exists = await reader.ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

                    if (!exists)
                    {
                        return(null);
                    }

                    var parametersSqlBytes = (byte[])reader.GetValue(0);
                    var returnsSqlBytes    = (byte[])reader.GetValue(1);

                    // ASSUME this is UTF-8 encoded; it's possible that the `character_set_client` column would need to be used?
                    var parametersSql = Encoding.UTF8.GetString(parametersSqlBytes);
                    var returnsSql    = Encoding.UTF8.GetString(returnsSqlBytes);

                    var parsedParameters = ParseParameters(parametersSql);
                    if (returnsSql.Length != 0)
                    {
                        var returnDataType = ParseDataType(returnsSql, out var unsigned, out var length);
                        parsedParameters.Insert(0, CreateCachedParameter(0, null, "", returnDataType, unsigned, length, returnsSql));
                    }

                    return(new CachedProcedure(schema, component, parsedParameters));
                }
                catch (MySqlException ex)
                {
                    Log.Warn("Session{0} failed to retrieve metadata for Schema={1} Component={2}; falling back to INFORMATION_SCHEMA. Error: {3}", connection.Session.Id, schema, component, ex.Message);
                    if (ex.ErrorCode == MySqlErrorCode.TableAccessDenied)
                    {
                        connection.Session.ProcAccessDenied = true;
                    }
                }
            }

            if (connection.Session.ServerVersion.Version < ServerVersions.SupportsProcedureCache)
            {
                Log.Warn("Session{0} ServerVersion={1} does not support cached procedures", connection.Session.Id, connection.Session.ServerVersion.OriginalString);
                return(null);
            }

            var parameters = new List <CachedParameter>();
            int routineCount;

            using (var cmd = connection.CreateCommand())
            {
                cmd.Transaction = connection.CurrentTransaction;
                cmd.CommandText = @"SELECT COUNT(*)
					FROM information_schema.routines
					WHERE ROUTINE_SCHEMA = @schema AND ROUTINE_NAME = @component;
					SELECT ORDINAL_POSITION, PARAMETER_MODE, PARAMETER_NAME, DTD_IDENTIFIER
					FROM information_schema.parameters
					WHERE SPECIFIC_SCHEMA = @schema AND SPECIFIC_NAME = @component
					ORDER BY ORDINAL_POSITION"                    ;
                cmd.Parameters.AddWithValue("@schema", schema);
                cmd.Parameters.AddWithValue("@component", component);

                using var reader = await cmd.ExecuteReaderNoResetTimeoutAsync(CommandBehavior.Default, ioBehavior, cancellationToken).ConfigureAwait(false);

                await reader.ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

                routineCount = reader.GetInt32(0);
                await reader.NextResultAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

                while (await reader.ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false))
                {
                    var dataType = ParseDataType(reader.GetString(3), out var unsigned, out var length);
                    parameters.Add(new(
                                       reader.GetInt32(0),
                                       !reader.IsDBNull(1) ? reader.GetString(1) : null,
                                       !reader.IsDBNull(2) ? reader.GetString(2) : "",
                                       dataType,
                                       unsigned,
                                       length
                                       ));
                }
            }

            if (Log.IsInfoEnabled())
            {
                Log.Info("Procedure for Schema={0} Component={1} has RoutineCount={2}, ParameterCount={3}", schema, component, routineCount, parameters.Count);
            }
            return(routineCount == 0 ? null : new CachedProcedure(schema, component, parameters));
        }
Пример #23
0
 internal Task <bool> ReadAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) =>
 m_resultSet !.ReadAsync(ioBehavior, cancellationToken);
Пример #24
0
        private async Task DoPrepareAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            var statementPreparer = new StatementPreparer(CommandText, m_parameterCollection, CreateStatementPreparerOptions());
            var parsedStatements  = statementPreparer.SplitStatements();

            if (parsedStatements.Statements.Count > 1)
            {
                throw new NotSupportedException("Multiple semicolon-delimited SQL statements are not supported by MySqlCommand.Prepare");
            }

            var columnsAndParameters     = new ResizableArray <byte>();
            var columnsAndParametersSize = 0;

            var preparedStatements = new List <PreparedStatement>(parsedStatements.Statements.Count);

            foreach (var statement in parsedStatements.Statements)
            {
                await Connection.Session.SendAsync(new PayloadData(statement.StatementBytes), ioBehavior, cancellationToken).ConfigureAwait(false);

                var payload = await Connection.Session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

                var response = StatementPrepareResponsePayload.Create(payload.AsSpan());

                ColumnDefinitionPayload[] parameters = null;
                if (response.ParameterCount > 0)
                {
                    parameters = new ColumnDefinitionPayload[response.ParameterCount];
                    for (var i = 0; i < response.ParameterCount; i++)
                    {
                        payload = await Connection.Session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

                        Utility.Resize(ref columnsAndParameters, columnsAndParametersSize + payload.ArraySegment.Count);
                        Buffer.BlockCopy(payload.ArraySegment.Array, payload.ArraySegment.Offset, columnsAndParameters.Array, columnsAndParametersSize, payload.ArraySegment.Count);
                        parameters[i]             = ColumnDefinitionPayload.Create(new ResizableArraySegment <byte>(columnsAndParameters, columnsAndParametersSize, payload.ArraySegment.Count));
                        columnsAndParametersSize += payload.ArraySegment.Count;
                    }
                    if (!Connection.Session.SupportsDeprecateEof)
                    {
                        payload = await Connection.Session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

                        EofPayload.Create(payload.AsSpan());
                    }
                }

                ColumnDefinitionPayload[] columns = null;
                if (response.ColumnCount > 0)
                {
                    columns = new ColumnDefinitionPayload[response.ColumnCount];
                    for (var i = 0; i < response.ColumnCount; i++)
                    {
                        payload = await Connection.Session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

                        Utility.Resize(ref columnsAndParameters, columnsAndParametersSize + payload.ArraySegment.Count);
                        Buffer.BlockCopy(payload.ArraySegment.Array, payload.ArraySegment.Offset, columnsAndParameters.Array, columnsAndParametersSize, payload.ArraySegment.Count);
                        columns[i] = ColumnDefinitionPayload.Create(new ResizableArraySegment <byte>(columnsAndParameters, columnsAndParametersSize, payload.ArraySegment.Count));
                        columnsAndParametersSize += payload.ArraySegment.Count;
                    }
                    if (!Connection.Session.SupportsDeprecateEof)
                    {
                        payload = await Connection.Session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

                        EofPayload.Create(payload.AsSpan());
                    }
                }

                preparedStatements.Add(new PreparedStatement(response.StatementId, statement, columns, parameters));
            }

            Connection.Session.AddPreparedStatement(CommandText, new PreparedStatements(preparedStatements, parsedStatements));
        }
Пример #25
0
        private async Task ExecuteSavepointAsync(string command, string savepointName, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            VerifyValid();

            if (savepointName is null)
            {
                throw new ArgumentNullException(nameof(savepointName));
            }
            if (savepointName.Length == 0)
            {
                throw new ArgumentException("savepointName must not be empty", nameof(savepointName));
            }

            using var cmd = new MySqlCommand(command + "savepoint " + QuoteIdentifier(savepointName), Connection, this);
            await cmd.ExecuteNonQueryAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
        }
        public virtual async Task <DbDataReader> ExecuteReaderAsync(string commandText, MySqlParameterCollection parameterCollection,
                                                                    CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (Log.IsDebugEnabled())
            {
                Log.Debug("Session{0} ExecuteBehavior {1} CommandText: {2}", m_command.Connection.Session.Id, ioBehavior, commandText);
            }
            using (var payload = CreateQueryPayload(commandText, parameterCollection))
                using (m_command.RegisterCancel(cancellationToken))
                {
                    m_command.Connection.Session.StartQuerying(m_command);
                    m_command.LastInsertedId = -1;
                    try
                    {
                        await m_command.Connection.Session.SendAsync(payload, ioBehavior, CancellationToken.None).ConfigureAwait(false);

                        return(await MySqlDataReader.CreateAsync(m_command, behavior, ResultSetProtocol.Text, ioBehavior).ConfigureAwait(false));
                    }
                    catch (MySqlException ex) when(ex.Number == (int)MySqlErrorCode.QueryInterrupted && cancellationToken.IsCancellationRequested)
                    {
                        Log.Warn("Session{0} query was interrupted", m_command.Connection.Session.Id);
                        throw new OperationCanceledException(cancellationToken);
                    }
                    catch (Exception ex) when(payload.ArraySegment.Count > 4_194_304 && (ex is SocketException || ex is IOException || ex is MySqlProtocolException))
                    {
                        // the default MySQL Server value for max_allowed_packet (in MySQL 5.7) is 4MiB: https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_max_allowed_packet
                        // use "decimal megabytes" (to round up) when creating the exception message
                        int megabytes = payload.ArraySegment.Count / 1_000_000;

                        throw new MySqlException("Error submitting {0}MB packet; ensure 'max_allowed_packet' is greater than {0}MB.".FormatInvariant(megabytes), ex);
                    }
                }
        }
Пример #27
0
 public ValueTask <int> ReadBytesAsync(ArraySegment <byte> buffer, IOBehavior ioBehavior) =>
 m_compressedPayloadHandler.ReadBytesAsync(buffer, m_protocolErrorBehavior, ioBehavior);
Пример #28
0
 private async ValueTask <int> WriteToServerAsync(DataTable dataTable, IOBehavior ioBehavior, CancellationToken cancellationToken)
Пример #29
0
        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_);
            }
        }
Пример #30
0
		public async Task ReadEntireAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
		{
			while (State is ResultSetState.ReadingRows or ResultSetState.ReadResultSetHeader)
				await ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
		}