sqlx_core_oldapi/mssql/protocol/
packet.rs

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