예제 #1
0
        public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDictionary <string, CachedProcedure?> cachedProcedures, ByteBufferWriter writer)
        {
            writer.Write((byte)CommandKind.Multi);
            bool?firstResult = default;
            bool wroteCommand;

            do
            {
                // save room for command length
                var position = writer.Position;
                writer.Write(Padding);

                wroteCommand = SingleCommandPayloadCreator.Instance.WriteQueryCommand(ref commandListPosition, cachedProcedures, writer);
                if (firstResult is null)
                {
                    firstResult = wroteCommand;
                }

                // write command length
                var commandLength = writer.Position - position - Padding.Length;
                var span          = writer.ArraySegment.AsSpan().Slice(position);
                span[0] = 0xFE;
                BinaryPrimitives.WriteUInt64LittleEndian(span.Slice(1), (ulong)commandLength);
            } while (wroteCommand);

            // remove the padding that was saved for the final command (which wasn't written)
            writer.TrimEnd(Padding.Length);
            return(firstResult.Value);
        }
예제 #2
0
        public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDictionary <string, CachedProcedure> cachedProcedures, ByteBufferWriter writer)
        {
            if (commandListPosition.CommandIndex == commandListPosition.Commands.Count)
            {
                return(false);
            }

            var command            = commandListPosition.Commands[commandListPosition.CommandIndex];
            var preparedStatements = command.TryGetPreparedStatements();

            if (preparedStatements is null)
            {
                if (Log.IsDebugEnabled())
                {
                    Log.Debug("Session{0} Preparing command payload; CommandText: {1}", command.Connection.Session.Id, command.CommandText);
                }

                if (command.CommandType == CommandType.StoredProcedure)
                {
                    WriteStoredProcedure(command, cachedProcedures, writer);
                }
                else
                {
                    WriteCommand(command, writer);
                }

                commandListPosition.CommandIndex++;
            }
            else
            {
                WritePreparedStatement(command, preparedStatements.Statements[commandListPosition.PreparedStatementIndex], writer);

                // advance to next prepared statement or next command
                if (++commandListPosition.PreparedStatementIndex == preparedStatements.Statements.Count)
                {
                    commandListPosition.CommandIndex++;
                    commandListPosition.PreparedStatementIndex = 0;
                }
            }

            return(true);
        }
        public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDictionary <string, CachedProcedure?> cachedProcedures, ByteBufferWriter writer)
        {
            if (commandListPosition.CommandIndex == commandListPosition.Commands.Count)
            {
                return(false);
            }

            writer.Write((byte)CommandKind.Query);
            bool isComplete;

            do
            {
                var command = commandListPosition.Commands[commandListPosition.CommandIndex];
                if (Log.IsTraceEnabled())
                {
                    Log.Trace("Session{0} Preparing command payload; CommandText: {1}", command.Connection !.Session.Id, command.CommandText);
                }

                isComplete = SingleCommandPayloadCreator.WriteQueryPayload(command, cachedProcedures, writer);
                commandListPosition.CommandIndex++;
            }while (commandListPosition.CommandIndex < commandListPosition.Commands.Count && isComplete);

            return(true);
        }
예제 #4
0
        public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDictionary <string, CachedProcedure?> cachedProcedures, ByteBufferWriter writer)
        {
            if (commandListPosition.CommandIndex == commandListPosition.Commands.Count)
            {
                return(false);
            }

            var command            = commandListPosition.Commands[commandListPosition.CommandIndex];
            var preparedStatements = command.TryGetPreparedStatements();

            if (preparedStatements is null)
            {
                if (Log.IsTraceEnabled())
                {
                    Log.Trace("Session{0} Preparing command payload; CommandText: {1}", command.Connection !.Session.Id, command.CommandText);
                }

                writer.Write((byte)CommandKind.Query);
                WriteQueryPayload(command, cachedProcedures, writer);

                commandListPosition.CommandIndex++;
            }
            else
            {
                writer.Write((byte)CommandKind.StatementExecute);
                WritePreparedStatement(command, preparedStatements.Statements[commandListPosition.PreparedStatementIndex], writer);

                // advance to next prepared statement or next command
                if (++commandListPosition.PreparedStatementIndex == preparedStatements.Statements.Count)
                {
                    commandListPosition.CommandIndex++;
                    commandListPosition.PreparedStatementIndex = 0;
                }
            }
            return(true);
        }
예제 #5
0
        public static async Task <DbDataReader> ExecuteReaderAsync(IReadOnlyList <IMySqlCommand> commands, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            var commandListPosition = new CommandListPosition(commands);
            var command             = commands[0];

            // pre-requisite: Connection is non-null must be checked before calling this method
            var connection = command.Connection !;

            if (Log.IsDebugEnabled())
            {
                Log.Debug("Session{0} ExecuteReader {1} CommandCount: {2}", connection.Session.Id, ioBehavior, commands.Count);
            }

            Dictionary <string, CachedProcedure?>?cachedProcedures = null;

            foreach (var command2 in commands)
            {
                if (command2.CommandType == CommandType.StoredProcedure)
                {
                    cachedProcedures ??= new Dictionary <string, CachedProcedure?>();
                    var commandText = command2.CommandText !;
                    if (!cachedProcedures.ContainsKey(commandText))
                    {
                        cachedProcedures.Add(commandText, await connection.GetCachedProcedure(ioBehavior, commandText, cancellationToken).ConfigureAwait(false));

                        // because the connection was used to execute a MySqlDataReader with the connection's DefaultCommandTimeout,
                        // we need to reapply the command's CommandTimeout (even if some of the time has elapsed)
                        command.CancellableCommand.ResetCommandTimeout();
                    }
                }
            }

            var writer = new ByteBufferWriter();

            // cachedProcedures will be non-null if there is a stored procedure, which is also the only time it will be read
            if (!payloadCreator.WriteQueryCommand(ref commandListPosition, cachedProcedures !, writer))
            {
                throw new InvalidOperationException("ICommandPayloadCreator failed to write query payload");
            }

            cancellationToken.ThrowIfCancellationRequested();

            using var payload      = writer.ToPayloadData();
            using var registration = command.CancellableCommand.RegisterCancel(cancellationToken);
            connection.Session.StartQuerying(command.CancellableCommand);
            command.SetLastInsertedId(-1);
            try
            {
                await connection.Session.SendAsync(payload, ioBehavior, CancellationToken.None).ConfigureAwait(false);

                return(await MySqlDataReader.CreateAsync(commandListPosition, payloadCreator, cachedProcedures, command, behavior, ioBehavior, cancellationToken).ConfigureAwait(false));
            }
            catch (MySqlException ex) when(ex.Number == (int)MySqlErrorCode.QueryInterrupted && cancellationToken.IsCancellationRequested)
            {
                Log.Warn("Session{0} query was interrupted", connection.Session.Id);
                throw new OperationCanceledException(cancellationToken);
            }
            catch (Exception ex) when(payload.Span.Length > 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.Span.Length / 1_000_000;

                throw new MySqlException("Error submitting {0}MB packet; ensure 'max_allowed_packet' is greater than {0}MB.".FormatInvariant(megabytes), ex);
            }
        }
예제 #6
0
        public static async Task <DbDataReader> ExecuteReaderAsync(IReadOnlyList <IMySqlCommand> commands, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            var commandListPosition = new CommandListPosition(commands);
            var command             = commands[0];

            if (Log.IsDebugEnabled())
            {
                Log.Debug("Session{0} ExecuteReader {1} CommandCount: {2}", command.Connection.Session.Id, ioBehavior, commands.Count);
            }

            Dictionary <string, CachedProcedure> cachedProcedures = null;

            foreach (var command2 in commands)
            {
                if (command2.CommandType == CommandType.StoredProcedure)
                {
                    if (cachedProcedures is null)
                    {
                        cachedProcedures = new Dictionary <string, CachedProcedure>();
                    }
                    if (!cachedProcedures.ContainsKey(command2.CommandText))
                    {
                        cachedProcedures.Add(command2.CommandText, await command2.Connection.GetCachedProcedure(ioBehavior, command2.CommandText, cancellationToken).ConfigureAwait(false));
                    }
                }
            }

            var writer = new ByteBufferWriter();

            if (!payloadCreator.WriteQueryCommand(ref commandListPosition, cachedProcedures, writer))
            {
                throw new InvalidOperationException("ICommandPayloadCreator failed to write query payload");
            }

            cancellationToken.ThrowIfCancellationRequested();

            using (var payload = writer.ToPayloadData())
                using (command.CancellableCommand.RegisterCancel(cancellationToken))
                {
                    command.Connection.Session.StartQuerying(command.CancellableCommand);
                    command.SetLastInsertedId(-1);
                    try
                    {
                        await command.Connection.Session.SendAsync(payload, ioBehavior, CancellationToken.None).ConfigureAwait(false);

                        return(await MySqlDataReader.CreateAsync(commandListPosition, payloadCreator, cachedProcedures, command, behavior, ioBehavior, cancellationToken).ConfigureAwait(false));
                    }
                    catch (MySqlException ex) when(ex.Number == (int)MySqlErrorCode.QueryInterrupted && cancellationToken.IsCancellationRequested)
                    {
                        Log.Warn("Session{0} query was interrupted", 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);
                    }
                }
        }