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); }
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); }
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); }
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); } }
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); } } }