sqlx_core/mssql/protocol/packet.rs
1use bitflags::bitflags;
2use bytes::{Buf, Bytes};
3
4use crate::error::Error;
5use crate::io::{Decode, Encode};
6
7#[derive(Debug)]
8pub(crate) struct PacketHeader {
9 // Type defines the type of message. Type is a 1-byte unsigned char.
10 pub(crate) r#type: PacketType,
11
12 // Status is a bit field used to indicate the message state. Status is a 1-byte unsigned char.
13 pub(crate) status: Status,
14
15 // Length is the size of the packet including the 8 bytes in the packet header.
16 pub(crate) length: u16,
17
18 // The process ID on the server, corresponding to the current connection.
19 pub(crate) server_process_id: u16,
20
21 // Packet ID is used for numbering message packets that contain data in addition to the packet
22 // header. Packet ID is a 1-byte, unsigned char. Each time packet data is sent, the value of
23 // PacketID is incremented by 1, modulo 256. This allows the receiver to track the sequence
24 // of TDS packets for a given message. This value is currently ignored.
25 pub(crate) packet_id: u8,
26}
27
28impl<'s> Encode<'s, &'s mut usize> for PacketHeader {
29 fn encode_with(&self, buf: &mut Vec<u8>, offset: &'s mut usize) {
30 buf.push(self.r#type as u8);
31 buf.push(self.status.bits());
32
33 *offset = buf.len();
34 buf.extend(&self.length.to_be_bytes());
35
36 buf.extend(&self.server_process_id.to_be_bytes());
37 buf.push(self.packet_id);
38
39 // window, unused
40 buf.push(0);
41 }
42}
43
44impl Decode<'_> for PacketHeader {
45 fn decode_with(mut buf: Bytes, _: ()) -> Result<Self, Error> {
46 Ok(Self {
47 r#type: PacketType::get(buf.get_u8())?,
48 status: Status::from_bits_truncate(buf.get_u8()),
49 length: buf.get_u16(),
50 server_process_id: buf.get_u16(),
51 packet_id: buf.get_u8(),
52 })
53 }
54}
55
56#[derive(Debug, Copy, PartialEq, Clone)]
57pub(crate) enum PacketType {
58 // Pre-login. Should always be #18 unless we decide to try and support pre 7.0 TDS
59 PreTds7Login = 2,
60 PreLogin = 18,
61
62 SqlBatch = 1,
63 Rpc = 3,
64 AttentionSignal = 6,
65 BulkLoadData = 7,
66 FederatedAuthToken = 8,
67 TransactionManagerRequest = 14,
68 Tds7Login = 16,
69 Sspi = 17,
70
71 TabularResult = 4,
72}
73
74impl PacketType {
75 pub fn get(value: u8) -> Result<Self, Error> {
76 Ok(match value {
77 1 => PacketType::SqlBatch,
78 2 => PacketType::PreTds7Login,
79 3 => PacketType::Rpc,
80 4 => PacketType::TabularResult,
81 6 => PacketType::AttentionSignal,
82 7 => PacketType::BulkLoadData,
83 8 => PacketType::FederatedAuthToken,
84 14 => PacketType::TransactionManagerRequest,
85 16 => PacketType::Tds7Login,
86 17 => PacketType::Sspi,
87 18 => PacketType::PreLogin,
88
89 ty => {
90 return Err(err_protocol!("unknown packet type: {}", ty));
91 }
92 })
93 }
94}
95
96// Status is a bit field used to indicate the message state. Status is a 1-byte unsigned char.
97// The following Status bit flags are defined.
98bitflags! {
99 pub(crate) struct Status: u8 {
100 // "Normal" message.
101 const NORMAL = 0x00;
102
103 // End of message (EOM). The packet is the last packet in the whole request.
104 const END_OF_MESSAGE = 0x01;
105
106 // (From client to server) Ignore this event (0x01 MUST also be set).
107 const IGNORE_EVENT = 0x02;
108
109 // RESETCONNECTION
110 //
111 // (Introduced in TDS 7.1)
112 //
113 // (From client to server) Reset this connection
114 // before processing event. Only set for event types Batch, RPC, or Transaction Manager
115 // request. If clients want to set this bit, it MUST be part of the first packet of the
116 // message. This signals the server to clean up the environment state of the connection
117 // back to the default environment setting, effectively simulating a logout and a
118 // subsequent login, and provides server support for connection pooling. This bit SHOULD
119 // be ignored if it is set in a packet that is not the first packet of the message.
120 //
121 // This status bit MUST NOT be set in conjunction with the RESETCONNECTIONSKIPTRAN bit.
122 // Distributed transactions and isolation levels will not be reset.
123 const RESET_CONN = 0x08;
124
125 // RESETCONNECTIONSKIPTRAN
126 //
127 // (Introduced in TDS 7.3)
128 //
129 // (From client to server) Reset the
130 // connection before processing event but do not modify the transaction state (the
131 // state will remain the same before and after the reset). The transaction in the
132 // session can be a local transaction that is started from the session or it can
133 // be a distributed transaction in which the session is enlisted. This status bit
134 // MUST NOT be set in conjunction with the RESETCONNECTION bit.
135 // Otherwise identical to RESETCONNECTION.
136 const RESET_CONN_SKIP_TRAN = 0x10;
137 }
138}