tds_protocol/
token.rs

1//! TDS token stream definitions.
2//!
3//! Tokens are the fundamental units of TDS response data. The server sends
4//! a stream of tokens that describe metadata, rows, errors, and other information.
5//!
6//! ## Token Structure
7//!
8//! Each token begins with a 1-byte token type identifier, followed by
9//! token-specific data. Some tokens have fixed lengths, while others
10//! have length prefixes.
11//!
12//! ## Usage
13//!
14//! ```rust,ignore
15//! use tds_protocol::token::{Token, TokenParser};
16//! use bytes::Bytes;
17//!
18//! let data: Bytes = /* received from server */;
19//! let mut parser = TokenParser::new(data);
20//!
21//! while let Some(token) = parser.next_token()? {
22//!     match token {
23//!         Token::Done(done) => println!("Rows affected: {}", done.row_count),
24//!         Token::Error(err) => eprintln!("Error {}: {}", err.number, err.message),
25//!         _ => {}
26//!     }
27//! }
28//! ```
29
30use bytes::{Buf, BufMut, Bytes};
31
32use crate::codec::{read_b_varchar, read_us_varchar};
33use crate::error::ProtocolError;
34use crate::types::TypeId;
35
36/// Token type identifier.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38#[repr(u8)]
39pub enum TokenType {
40    /// Column metadata (COLMETADATA).
41    ColMetaData = 0x81,
42    /// Error message (ERROR).
43    Error = 0xAA,
44    /// Informational message (INFO).
45    Info = 0xAB,
46    /// Login acknowledgment (LOGINACK).
47    LoginAck = 0xAD,
48    /// Row data (ROW).
49    Row = 0xD1,
50    /// Null bitmap compressed row (NBCROW).
51    NbcRow = 0xD2,
52    /// Environment change (ENVCHANGE).
53    EnvChange = 0xE3,
54    /// SSPI authentication (SSPI).
55    Sspi = 0xED,
56    /// Done (DONE).
57    Done = 0xFD,
58    /// Done in procedure (DONEINPROC).
59    DoneInProc = 0xFF,
60    /// Done procedure (DONEPROC).
61    DoneProc = 0xFE,
62    /// Return status (RETURNSTATUS).
63    ReturnStatus = 0x79,
64    /// Return value (RETURNVALUE).
65    ReturnValue = 0xAC,
66    /// Order (ORDER).
67    Order = 0xA9,
68    /// Feature extension acknowledgment (FEATUREEXTACK).
69    FeatureExtAck = 0xAE,
70    /// Session state (SESSIONSTATE).
71    SessionState = 0xE4,
72    /// Federated authentication info (FEDAUTHINFO).
73    FedAuthInfo = 0xEE,
74    /// Column info (COLINFO).
75    ColInfo = 0xA5,
76    /// Table name (TABNAME).
77    TabName = 0xA4,
78    /// Offset (OFFSET).
79    Offset = 0x78,
80}
81
82impl TokenType {
83    /// Create a token type from a raw byte.
84    pub fn from_u8(value: u8) -> Option<Self> {
85        match value {
86            0x81 => Some(Self::ColMetaData),
87            0xAA => Some(Self::Error),
88            0xAB => Some(Self::Info),
89            0xAD => Some(Self::LoginAck),
90            0xD1 => Some(Self::Row),
91            0xD2 => Some(Self::NbcRow),
92            0xE3 => Some(Self::EnvChange),
93            0xED => Some(Self::Sspi),
94            0xFD => Some(Self::Done),
95            0xFF => Some(Self::DoneInProc),
96            0xFE => Some(Self::DoneProc),
97            0x79 => Some(Self::ReturnStatus),
98            0xAC => Some(Self::ReturnValue),
99            0xA9 => Some(Self::Order),
100            0xAE => Some(Self::FeatureExtAck),
101            0xE4 => Some(Self::SessionState),
102            0xEE => Some(Self::FedAuthInfo),
103            0xA5 => Some(Self::ColInfo),
104            0xA4 => Some(Self::TabName),
105            0x78 => Some(Self::Offset),
106            _ => None,
107        }
108    }
109}
110
111/// Parsed TDS token.
112///
113/// This enum represents all possible tokens that can be received from SQL Server.
114/// Each variant contains the parsed token data.
115#[derive(Debug, Clone)]
116pub enum Token {
117    /// Column metadata describing result set structure.
118    ColMetaData(ColMetaData),
119    /// Row data.
120    Row(RawRow),
121    /// Null bitmap compressed row.
122    NbcRow(NbcRow),
123    /// Completion of a SQL statement.
124    Done(Done),
125    /// Completion of a stored procedure.
126    DoneProc(DoneProc),
127    /// Completion within a stored procedure.
128    DoneInProc(DoneInProc),
129    /// Return status from stored procedure.
130    ReturnStatus(i32),
131    /// Return value from stored procedure.
132    ReturnValue(ReturnValue),
133    /// Error message from server.
134    Error(ServerError),
135    /// Informational message from server.
136    Info(ServerInfo),
137    /// Login acknowledgment.
138    LoginAck(LoginAck),
139    /// Environment change notification.
140    EnvChange(EnvChange),
141    /// Column ordering information.
142    Order(Order),
143    /// Feature extension acknowledgment.
144    FeatureExtAck(FeatureExtAck),
145    /// SSPI authentication data.
146    Sspi(SspiToken),
147    /// Session state information.
148    SessionState(SessionState),
149    /// Federated authentication info.
150    FedAuthInfo(FedAuthInfo),
151}
152
153/// Column metadata token.
154#[derive(Debug, Clone, Default)]
155pub struct ColMetaData {
156    /// Column definitions.
157    pub columns: Vec<ColumnData>,
158}
159
160/// Column definition within metadata.
161#[derive(Debug, Clone)]
162pub struct ColumnData {
163    /// Column name.
164    pub name: String,
165    /// Column data type ID.
166    pub type_id: TypeId,
167    /// Column data type raw byte (for unknown types).
168    pub col_type: u8,
169    /// Column flags.
170    pub flags: u16,
171    /// User type ID.
172    pub user_type: u32,
173    /// Type-specific metadata.
174    pub type_info: TypeInfo,
175}
176
177/// Type-specific metadata.
178#[derive(Debug, Clone, Default)]
179pub struct TypeInfo {
180    /// Maximum length for variable-length types.
181    pub max_length: Option<u32>,
182    /// Precision for numeric types.
183    pub precision: Option<u8>,
184    /// Scale for numeric types.
185    pub scale: Option<u8>,
186    /// Collation for string types.
187    pub collation: Option<Collation>,
188}
189
190/// SQL Server collation.
191///
192/// Collations in SQL Server define the character encoding and sorting rules
193/// for string data. For `VARCHAR` columns, the collation determines which
194/// code page (character encoding) is used to store the data.
195///
196/// # Encoding Support
197///
198/// When the `encoding` feature is enabled, the [`Collation::encoding()`] method
199/// returns the appropriate [`encoding_rs::Encoding`] for decoding `VARCHAR` data.
200///
201/// # Example
202///
203/// ```rust,ignore
204/// use tds_protocol::token::Collation;
205///
206/// let collation = Collation { lcid: 0x0804, sort_id: 0 }; // Chinese (PRC)
207/// if let Some(encoding) = collation.encoding() {
208///     let (decoded, _, _) = encoding.decode(raw_bytes);
209///     // decoded is now proper Chinese text
210/// }
211/// ```
212#[derive(Debug, Clone, Copy, Default)]
213pub struct Collation {
214    /// Locale ID (LCID).
215    ///
216    /// The LCID encodes both the language and region. The lower 16 bits
217    /// contain the primary language ID, and bits 16-19 contain the sort ID
218    /// for some collations.
219    ///
220    /// For UTF-8 collations (SQL Server 2019+), bit 27 (0x0800_0000) is set.
221    pub lcid: u32,
222    /// Sort ID.
223    ///
224    /// Used with certain collations to specify sorting behavior.
225    pub sort_id: u8,
226}
227
228impl Collation {
229    /// Returns the character encoding for this collation.
230    ///
231    /// This method maps the collation's LCID to the appropriate character
232    /// encoding from the `encoding_rs` crate.
233    ///
234    /// # Returns
235    ///
236    /// - `Some(&Encoding)` - The encoding to use for decoding `VARCHAR` data
237    /// - `None` - If the collation uses UTF-8 (no transcoding needed) or
238    ///   the LCID is not recognized (caller should use Windows-1252 fallback)
239    ///
240    /// # UTF-8 Collations
241    ///
242    /// SQL Server 2019+ supports UTF-8 collations (identified by the `_UTF8`
243    /// suffix). These return `None` because no transcoding is needed.
244    ///
245    /// # Example
246    ///
247    /// ```rust,ignore
248    /// let collation = Collation { lcid: 0x0419, sort_id: 0 }; // Russian
249    /// if let Some(encoding) = collation.encoding() {
250    ///     // encoding is Windows-1251 for Cyrillic
251    ///     let (text, _, had_errors) = encoding.decode(&raw_bytes);
252    /// }
253    /// ```
254    #[cfg(feature = "encoding")]
255    pub fn encoding(&self) -> Option<&'static encoding_rs::Encoding> {
256        crate::collation::encoding_for_lcid(self.lcid)
257    }
258
259    /// Returns whether this collation uses UTF-8 encoding.
260    ///
261    /// UTF-8 collations were introduced in SQL Server 2019 and are
262    /// identified by the `_UTF8` suffix in the collation name.
263    #[cfg(feature = "encoding")]
264    pub fn is_utf8(&self) -> bool {
265        crate::collation::is_utf8_collation(self.lcid)
266    }
267
268    /// Returns the Windows code page number for this collation.
269    ///
270    /// Useful for error messages and debugging.
271    ///
272    /// # Returns
273    ///
274    /// The code page number (e.g., 1252 for Western European, 932 for Japanese).
275    #[cfg(feature = "encoding")]
276    pub fn code_page(&self) -> Option<u16> {
277        crate::collation::code_page_for_lcid(self.lcid)
278    }
279
280    /// Returns the encoding name for this collation.
281    ///
282    /// Useful for error messages and debugging.
283    #[cfg(feature = "encoding")]
284    pub fn encoding_name(&self) -> &'static str {
285        crate::collation::encoding_name_for_lcid(self.lcid)
286    }
287}
288
289/// Raw row data (not yet decoded).
290#[derive(Debug, Clone)]
291pub struct RawRow {
292    /// Raw column values.
293    pub data: bytes::Bytes,
294}
295
296/// Null bitmap compressed row.
297#[derive(Debug, Clone)]
298pub struct NbcRow {
299    /// Null bitmap.
300    pub null_bitmap: Vec<u8>,
301    /// Raw non-null column values.
302    pub data: bytes::Bytes,
303}
304
305/// Done token indicating statement completion.
306#[derive(Debug, Clone, Copy)]
307pub struct Done {
308    /// Status flags.
309    pub status: DoneStatus,
310    /// Current command.
311    pub cur_cmd: u16,
312    /// Row count (if applicable).
313    pub row_count: u64,
314}
315
316/// Done status flags.
317#[derive(Debug, Clone, Copy, Default)]
318pub struct DoneStatus {
319    /// More results follow.
320    pub more: bool,
321    /// Error occurred.
322    pub error: bool,
323    /// Transaction in progress.
324    pub in_xact: bool,
325    /// Row count is valid.
326    pub count: bool,
327    /// Attention acknowledgment.
328    pub attn: bool,
329    /// Server error caused statement termination.
330    pub srverror: bool,
331}
332
333/// Done in procedure token.
334#[derive(Debug, Clone, Copy)]
335pub struct DoneInProc {
336    /// Status flags.
337    pub status: DoneStatus,
338    /// Current command.
339    pub cur_cmd: u16,
340    /// Row count.
341    pub row_count: u64,
342}
343
344/// Done procedure token.
345#[derive(Debug, Clone, Copy)]
346pub struct DoneProc {
347    /// Status flags.
348    pub status: DoneStatus,
349    /// Current command.
350    pub cur_cmd: u16,
351    /// Row count.
352    pub row_count: u64,
353}
354
355/// Return value from stored procedure.
356#[derive(Debug, Clone)]
357pub struct ReturnValue {
358    /// Parameter ordinal.
359    pub param_ordinal: u16,
360    /// Parameter name.
361    pub param_name: String,
362    /// Status flags.
363    pub status: u8,
364    /// User type.
365    pub user_type: u32,
366    /// Type flags.
367    pub flags: u16,
368    /// Type info.
369    pub type_info: TypeInfo,
370    /// Value data.
371    pub value: bytes::Bytes,
372}
373
374/// Server error message.
375#[derive(Debug, Clone)]
376pub struct ServerError {
377    /// Error number.
378    pub number: i32,
379    /// Error state.
380    pub state: u8,
381    /// Error severity class.
382    pub class: u8,
383    /// Error message text.
384    pub message: String,
385    /// Server name.
386    pub server: String,
387    /// Procedure name.
388    pub procedure: String,
389    /// Line number.
390    pub line: i32,
391}
392
393/// Server informational message.
394#[derive(Debug, Clone)]
395pub struct ServerInfo {
396    /// Info number.
397    pub number: i32,
398    /// Info state.
399    pub state: u8,
400    /// Info class (severity).
401    pub class: u8,
402    /// Info message text.
403    pub message: String,
404    /// Server name.
405    pub server: String,
406    /// Procedure name.
407    pub procedure: String,
408    /// Line number.
409    pub line: i32,
410}
411
412/// Login acknowledgment token.
413#[derive(Debug, Clone)]
414pub struct LoginAck {
415    /// Interface type.
416    pub interface: u8,
417    /// TDS version.
418    pub tds_version: u32,
419    /// Program name.
420    pub prog_name: String,
421    /// Program version.
422    pub prog_version: u32,
423}
424
425/// Environment change token.
426#[derive(Debug, Clone)]
427pub struct EnvChange {
428    /// Type of environment change.
429    pub env_type: EnvChangeType,
430    /// New value.
431    pub new_value: EnvChangeValue,
432    /// Old value.
433    pub old_value: EnvChangeValue,
434}
435
436/// Environment change type.
437#[derive(Debug, Clone, Copy, PartialEq, Eq)]
438#[repr(u8)]
439pub enum EnvChangeType {
440    /// Database changed.
441    Database = 1,
442    /// Language changed.
443    Language = 2,
444    /// Character set changed.
445    CharacterSet = 3,
446    /// Packet size changed.
447    PacketSize = 4,
448    /// Unicode data sorting locale ID.
449    UnicodeSortingLocalId = 5,
450    /// Unicode comparison flags.
451    UnicodeComparisonFlags = 6,
452    /// SQL collation.
453    SqlCollation = 7,
454    /// Begin transaction.
455    BeginTransaction = 8,
456    /// Commit transaction.
457    CommitTransaction = 9,
458    /// Rollback transaction.
459    RollbackTransaction = 10,
460    /// Enlist DTC transaction.
461    EnlistDtcTransaction = 11,
462    /// Defect DTC transaction.
463    DefectTransaction = 12,
464    /// Real-time log shipping.
465    RealTimeLogShipping = 13,
466    /// Promote transaction.
467    PromoteTransaction = 15,
468    /// Transaction manager address.
469    TransactionManagerAddress = 16,
470    /// Transaction ended.
471    TransactionEnded = 17,
472    /// Reset connection completion acknowledgment.
473    ResetConnectionCompletionAck = 18,
474    /// User instance started.
475    UserInstanceStarted = 19,
476    /// Routing information.
477    Routing = 20,
478}
479
480/// Environment change value.
481#[derive(Debug, Clone)]
482pub enum EnvChangeValue {
483    /// String value.
484    String(String),
485    /// Binary value.
486    Binary(bytes::Bytes),
487    /// Routing information.
488    Routing {
489        /// Host name.
490        host: String,
491        /// Port number.
492        port: u16,
493    },
494}
495
496/// Column ordering information.
497#[derive(Debug, Clone)]
498pub struct Order {
499    /// Ordered column indices.
500    pub columns: Vec<u16>,
501}
502
503/// Feature extension acknowledgment.
504#[derive(Debug, Clone)]
505pub struct FeatureExtAck {
506    /// Acknowledged features.
507    pub features: Vec<FeatureAck>,
508}
509
510/// Individual feature acknowledgment.
511#[derive(Debug, Clone)]
512pub struct FeatureAck {
513    /// Feature ID.
514    pub feature_id: u8,
515    /// Feature data.
516    pub data: bytes::Bytes,
517}
518
519/// SSPI authentication token.
520#[derive(Debug, Clone)]
521pub struct SspiToken {
522    /// SSPI data.
523    pub data: bytes::Bytes,
524}
525
526/// Session state token.
527#[derive(Debug, Clone)]
528pub struct SessionState {
529    /// Session state data.
530    pub data: bytes::Bytes,
531}
532
533/// Federated authentication info.
534#[derive(Debug, Clone)]
535pub struct FedAuthInfo {
536    /// STS URL.
537    pub sts_url: String,
538    /// Service principal name.
539    pub spn: String,
540}
541
542// =============================================================================
543// ColMetaData and Row Parsing Implementation
544// =============================================================================
545
546impl ColMetaData {
547    /// Special value indicating no metadata.
548    pub const NO_METADATA: u16 = 0xFFFF;
549
550    /// Decode a COLMETADATA token from bytes.
551    pub fn decode(src: &mut impl Buf) -> Result<Self, ProtocolError> {
552        if src.remaining() < 2 {
553            return Err(ProtocolError::UnexpectedEof);
554        }
555
556        let column_count = src.get_u16_le();
557
558        // 0xFFFF means no metadata present
559        if column_count == Self::NO_METADATA {
560            return Ok(Self {
561                columns: Vec::new(),
562            });
563        }
564
565        let mut columns = Vec::with_capacity(column_count as usize);
566
567        for _ in 0..column_count {
568            let column = Self::decode_column(src)?;
569            columns.push(column);
570        }
571
572        Ok(Self { columns })
573    }
574
575    /// Decode a single column from the metadata.
576    fn decode_column(src: &mut impl Buf) -> Result<ColumnData, ProtocolError> {
577        // UserType (4 bytes) + Flags (2 bytes) + TypeId (1 byte)
578        if src.remaining() < 7 {
579            return Err(ProtocolError::UnexpectedEof);
580        }
581
582        let user_type = src.get_u32_le();
583        let flags = src.get_u16_le();
584        let col_type = src.get_u8();
585
586        let type_id = TypeId::from_u8(col_type).unwrap_or(TypeId::Null); // Default to Null for unknown types
587
588        // Parse type-specific metadata
589        let type_info = Self::decode_type_info(src, type_id, col_type)?;
590
591        // Read column name (B_VARCHAR format - 1 byte length in characters)
592        let name = read_b_varchar(src).ok_or(ProtocolError::UnexpectedEof)?;
593
594        Ok(ColumnData {
595            name,
596            type_id,
597            col_type,
598            flags,
599            user_type,
600            type_info,
601        })
602    }
603
604    /// Decode type-specific metadata based on the type ID.
605    fn decode_type_info(
606        src: &mut impl Buf,
607        type_id: TypeId,
608        col_type: u8,
609    ) -> Result<TypeInfo, ProtocolError> {
610        match type_id {
611            // Fixed-length types have no additional metadata
612            TypeId::Null => Ok(TypeInfo::default()),
613            TypeId::Int1 | TypeId::Bit => Ok(TypeInfo::default()),
614            TypeId::Int2 => Ok(TypeInfo::default()),
615            TypeId::Int4 => Ok(TypeInfo::default()),
616            TypeId::Int8 => Ok(TypeInfo::default()),
617            TypeId::Float4 => Ok(TypeInfo::default()),
618            TypeId::Float8 => Ok(TypeInfo::default()),
619            TypeId::Money => Ok(TypeInfo::default()),
620            TypeId::Money4 => Ok(TypeInfo::default()),
621            TypeId::DateTime => Ok(TypeInfo::default()),
622            TypeId::DateTime4 => Ok(TypeInfo::default()),
623
624            // Variable length integer/float/money (1-byte max length)
625            TypeId::IntN | TypeId::BitN | TypeId::FloatN | TypeId::MoneyN | TypeId::DateTimeN => {
626                if src.remaining() < 1 {
627                    return Err(ProtocolError::UnexpectedEof);
628                }
629                let max_length = src.get_u8() as u32;
630                Ok(TypeInfo {
631                    max_length: Some(max_length),
632                    ..Default::default()
633                })
634            }
635
636            // GUID has 1-byte length
637            TypeId::Guid => {
638                if src.remaining() < 1 {
639                    return Err(ProtocolError::UnexpectedEof);
640                }
641                let max_length = src.get_u8() as u32;
642                Ok(TypeInfo {
643                    max_length: Some(max_length),
644                    ..Default::default()
645                })
646            }
647
648            // Decimal/Numeric types (1-byte length + precision + scale)
649            TypeId::Decimal | TypeId::Numeric | TypeId::DecimalN | TypeId::NumericN => {
650                if src.remaining() < 3 {
651                    return Err(ProtocolError::UnexpectedEof);
652                }
653                let max_length = src.get_u8() as u32;
654                let precision = src.get_u8();
655                let scale = src.get_u8();
656                Ok(TypeInfo {
657                    max_length: Some(max_length),
658                    precision: Some(precision),
659                    scale: Some(scale),
660                    ..Default::default()
661                })
662            }
663
664            // Old-style byte-length strings (Char, VarChar, Binary, VarBinary)
665            TypeId::Char | TypeId::VarChar | TypeId::Binary | TypeId::VarBinary => {
666                if src.remaining() < 1 {
667                    return Err(ProtocolError::UnexpectedEof);
668                }
669                let max_length = src.get_u8() as u32;
670                Ok(TypeInfo {
671                    max_length: Some(max_length),
672                    ..Default::default()
673                })
674            }
675
676            // Big varchar/binary with 2-byte length + collation for strings
677            TypeId::BigVarChar | TypeId::BigChar => {
678                if src.remaining() < 7 {
679                    // 2 (length) + 5 (collation)
680                    return Err(ProtocolError::UnexpectedEof);
681                }
682                let max_length = src.get_u16_le() as u32;
683                let collation = Self::decode_collation(src)?;
684                Ok(TypeInfo {
685                    max_length: Some(max_length),
686                    collation: Some(collation),
687                    ..Default::default()
688                })
689            }
690
691            // Big binary (2-byte length, no collation)
692            TypeId::BigVarBinary | TypeId::BigBinary => {
693                if src.remaining() < 2 {
694                    return Err(ProtocolError::UnexpectedEof);
695                }
696                let max_length = src.get_u16_le() as u32;
697                Ok(TypeInfo {
698                    max_length: Some(max_length),
699                    ..Default::default()
700                })
701            }
702
703            // Unicode strings (NChar, NVarChar) - 2-byte length + collation
704            TypeId::NChar | TypeId::NVarChar => {
705                if src.remaining() < 7 {
706                    // 2 (length) + 5 (collation)
707                    return Err(ProtocolError::UnexpectedEof);
708                }
709                let max_length = src.get_u16_le() as u32;
710                let collation = Self::decode_collation(src)?;
711                Ok(TypeInfo {
712                    max_length: Some(max_length),
713                    collation: Some(collation),
714                    ..Default::default()
715                })
716            }
717
718            // Date type (no additional metadata)
719            TypeId::Date => Ok(TypeInfo::default()),
720
721            // Time, DateTime2, DateTimeOffset have scale
722            TypeId::Time | TypeId::DateTime2 | TypeId::DateTimeOffset => {
723                if src.remaining() < 1 {
724                    return Err(ProtocolError::UnexpectedEof);
725                }
726                let scale = src.get_u8();
727                Ok(TypeInfo {
728                    scale: Some(scale),
729                    ..Default::default()
730                })
731            }
732
733            // Text/NText/Image (deprecated LOB types)
734            TypeId::Text | TypeId::NText | TypeId::Image => {
735                // These have complex metadata: length (4) + collation (5) + table name parts
736                if src.remaining() < 4 {
737                    return Err(ProtocolError::UnexpectedEof);
738                }
739                let max_length = src.get_u32_le();
740
741                // For Text/NText, read collation
742                let collation = if type_id == TypeId::Text || type_id == TypeId::NText {
743                    if src.remaining() < 5 {
744                        return Err(ProtocolError::UnexpectedEof);
745                    }
746                    Some(Self::decode_collation(src)?)
747                } else {
748                    None
749                };
750
751                // Skip table name parts (variable length)
752                // Format: numParts (1 byte) followed by us_varchar for each part
753                if src.remaining() < 1 {
754                    return Err(ProtocolError::UnexpectedEof);
755                }
756                let num_parts = src.get_u8();
757                for _ in 0..num_parts {
758                    // Read and discard table name part
759                    let _ = read_us_varchar(src).ok_or(ProtocolError::UnexpectedEof)?;
760                }
761
762                Ok(TypeInfo {
763                    max_length: Some(max_length),
764                    collation,
765                    ..Default::default()
766                })
767            }
768
769            // XML type
770            TypeId::Xml => {
771                if src.remaining() < 1 {
772                    return Err(ProtocolError::UnexpectedEof);
773                }
774                let schema_present = src.get_u8();
775
776                if schema_present != 0 {
777                    // Read schema info (3 us_varchar strings)
778                    let _ = read_us_varchar(src).ok_or(ProtocolError::UnexpectedEof)?; // db name
779                    let _ = read_us_varchar(src).ok_or(ProtocolError::UnexpectedEof)?; // owning schema
780                    let _ = read_us_varchar(src).ok_or(ProtocolError::UnexpectedEof)?; // xml schema collection
781                }
782
783                Ok(TypeInfo::default())
784            }
785
786            // UDT (User-defined type) - complex metadata
787            TypeId::Udt => {
788                // Max length (2 bytes)
789                if src.remaining() < 2 {
790                    return Err(ProtocolError::UnexpectedEof);
791                }
792                let max_length = src.get_u16_le() as u32;
793
794                // UDT metadata: db name, schema name, type name, assembly qualified name
795                let _ = read_us_varchar(src).ok_or(ProtocolError::UnexpectedEof)?; // db name
796                let _ = read_us_varchar(src).ok_or(ProtocolError::UnexpectedEof)?; // schema name
797                let _ = read_us_varchar(src).ok_or(ProtocolError::UnexpectedEof)?; // type name
798                let _ = read_us_varchar(src).ok_or(ProtocolError::UnexpectedEof)?; // assembly qualified name
799
800                Ok(TypeInfo {
801                    max_length: Some(max_length),
802                    ..Default::default()
803                })
804            }
805
806            // Table-valued parameter - complex metadata (skip for now)
807            TypeId::Tvp => {
808                // TVP has very complex metadata, not commonly used
809                // For now, we can't properly parse this
810                Err(ProtocolError::InvalidTokenType(col_type))
811            }
812
813            // SQL Variant - 4-byte length
814            TypeId::Variant => {
815                if src.remaining() < 4 {
816                    return Err(ProtocolError::UnexpectedEof);
817                }
818                let max_length = src.get_u32_le();
819                Ok(TypeInfo {
820                    max_length: Some(max_length),
821                    ..Default::default()
822                })
823            }
824        }
825    }
826
827    /// Decode collation information (5 bytes).
828    fn decode_collation(src: &mut impl Buf) -> Result<Collation, ProtocolError> {
829        if src.remaining() < 5 {
830            return Err(ProtocolError::UnexpectedEof);
831        }
832        // Collation: LCID (4 bytes) + Sort ID (1 byte)
833        let lcid = src.get_u32_le();
834        let sort_id = src.get_u8();
835        Ok(Collation { lcid, sort_id })
836    }
837
838    /// Get the number of columns.
839    #[must_use]
840    pub fn column_count(&self) -> usize {
841        self.columns.len()
842    }
843
844    /// Check if this represents no metadata.
845    #[must_use]
846    pub fn is_empty(&self) -> bool {
847        self.columns.is_empty()
848    }
849}
850
851impl ColumnData {
852    /// Check if this column is nullable.
853    #[must_use]
854    pub fn is_nullable(&self) -> bool {
855        (self.flags & 0x0001) != 0
856    }
857
858    /// Get the fixed size in bytes for this column, if applicable.
859    ///
860    /// Returns `None` for variable-length types.
861    #[must_use]
862    pub fn fixed_size(&self) -> Option<usize> {
863        match self.type_id {
864            TypeId::Null => Some(0),
865            TypeId::Int1 | TypeId::Bit => Some(1),
866            TypeId::Int2 => Some(2),
867            TypeId::Int4 => Some(4),
868            TypeId::Int8 => Some(8),
869            TypeId::Float4 => Some(4),
870            TypeId::Float8 => Some(8),
871            TypeId::Money => Some(8),
872            TypeId::Money4 => Some(4),
873            TypeId::DateTime => Some(8),
874            TypeId::DateTime4 => Some(4),
875            TypeId::Date => Some(3),
876            _ => None,
877        }
878    }
879}
880
881// =============================================================================
882// Row Parsing Implementation
883// =============================================================================
884
885impl RawRow {
886    /// Decode a ROW token from bytes.
887    ///
888    /// This function requires the column metadata to know how to parse the row.
889    /// The row data is stored as raw bytes for later parsing.
890    pub fn decode(src: &mut impl Buf, metadata: &ColMetaData) -> Result<Self, ProtocolError> {
891        let mut data = bytes::BytesMut::new();
892
893        for col in &metadata.columns {
894            Self::decode_column_value(src, col, &mut data)?;
895        }
896
897        Ok(Self {
898            data: data.freeze(),
899        })
900    }
901
902    /// Decode a single column value and append to the output buffer.
903    fn decode_column_value(
904        src: &mut impl Buf,
905        col: &ColumnData,
906        dst: &mut bytes::BytesMut,
907    ) -> Result<(), ProtocolError> {
908        match col.type_id {
909            // Fixed-length types
910            TypeId::Null => {
911                // No data
912            }
913            TypeId::Int1 | TypeId::Bit => {
914                if src.remaining() < 1 {
915                    return Err(ProtocolError::UnexpectedEof);
916                }
917                dst.extend_from_slice(&[src.get_u8()]);
918            }
919            TypeId::Int2 => {
920                if src.remaining() < 2 {
921                    return Err(ProtocolError::UnexpectedEof);
922                }
923                dst.extend_from_slice(&src.get_u16_le().to_le_bytes());
924            }
925            TypeId::Int4 => {
926                if src.remaining() < 4 {
927                    return Err(ProtocolError::UnexpectedEof);
928                }
929                dst.extend_from_slice(&src.get_u32_le().to_le_bytes());
930            }
931            TypeId::Int8 => {
932                if src.remaining() < 8 {
933                    return Err(ProtocolError::UnexpectedEof);
934                }
935                dst.extend_from_slice(&src.get_u64_le().to_le_bytes());
936            }
937            TypeId::Float4 => {
938                if src.remaining() < 4 {
939                    return Err(ProtocolError::UnexpectedEof);
940                }
941                dst.extend_from_slice(&src.get_u32_le().to_le_bytes());
942            }
943            TypeId::Float8 => {
944                if src.remaining() < 8 {
945                    return Err(ProtocolError::UnexpectedEof);
946                }
947                dst.extend_from_slice(&src.get_u64_le().to_le_bytes());
948            }
949            TypeId::Money => {
950                if src.remaining() < 8 {
951                    return Err(ProtocolError::UnexpectedEof);
952                }
953                let hi = src.get_u32_le();
954                let lo = src.get_u32_le();
955                dst.extend_from_slice(&hi.to_le_bytes());
956                dst.extend_from_slice(&lo.to_le_bytes());
957            }
958            TypeId::Money4 => {
959                if src.remaining() < 4 {
960                    return Err(ProtocolError::UnexpectedEof);
961                }
962                dst.extend_from_slice(&src.get_u32_le().to_le_bytes());
963            }
964            TypeId::DateTime => {
965                if src.remaining() < 8 {
966                    return Err(ProtocolError::UnexpectedEof);
967                }
968                let days = src.get_u32_le();
969                let time = src.get_u32_le();
970                dst.extend_from_slice(&days.to_le_bytes());
971                dst.extend_from_slice(&time.to_le_bytes());
972            }
973            TypeId::DateTime4 => {
974                if src.remaining() < 4 {
975                    return Err(ProtocolError::UnexpectedEof);
976                }
977                dst.extend_from_slice(&src.get_u32_le().to_le_bytes());
978            }
979            // DATE type uses 1-byte length prefix (can be NULL)
980            TypeId::Date => {
981                Self::decode_bytelen_type(src, dst)?;
982            }
983
984            // Variable-length nullable types (length-prefixed)
985            TypeId::IntN | TypeId::BitN | TypeId::FloatN | TypeId::MoneyN | TypeId::DateTimeN => {
986                Self::decode_bytelen_type(src, dst)?;
987            }
988
989            TypeId::Guid => {
990                Self::decode_bytelen_type(src, dst)?;
991            }
992
993            TypeId::Decimal | TypeId::Numeric | TypeId::DecimalN | TypeId::NumericN => {
994                Self::decode_bytelen_type(src, dst)?;
995            }
996
997            // Old-style byte-length strings
998            TypeId::Char | TypeId::VarChar | TypeId::Binary | TypeId::VarBinary => {
999                Self::decode_bytelen_type(src, dst)?;
1000            }
1001
1002            // 2-byte length strings (or PLP for MAX types)
1003            TypeId::BigVarChar | TypeId::BigVarBinary => {
1004                // max_length == 0xFFFF indicates VARCHAR(MAX) or VARBINARY(MAX), which uses PLP
1005                if col.type_info.max_length == Some(0xFFFF) {
1006                    Self::decode_plp_type(src, dst)?;
1007                } else {
1008                    Self::decode_ushortlen_type(src, dst)?;
1009                }
1010            }
1011
1012            // Fixed-length types that don't have MAX variants
1013            TypeId::BigChar | TypeId::BigBinary => {
1014                Self::decode_ushortlen_type(src, dst)?;
1015            }
1016
1017            // Unicode strings (2-byte length in bytes, or PLP for NVARCHAR(MAX))
1018            TypeId::NVarChar => {
1019                // max_length == 0xFFFF indicates NVARCHAR(MAX), which uses PLP
1020                if col.type_info.max_length == Some(0xFFFF) {
1021                    Self::decode_plp_type(src, dst)?;
1022                } else {
1023                    Self::decode_ushortlen_type(src, dst)?;
1024                }
1025            }
1026
1027            // Fixed-length NCHAR doesn't have MAX variant
1028            TypeId::NChar => {
1029                Self::decode_ushortlen_type(src, dst)?;
1030            }
1031
1032            // Time types with scale
1033            TypeId::Time | TypeId::DateTime2 | TypeId::DateTimeOffset => {
1034                Self::decode_bytelen_type(src, dst)?;
1035            }
1036
1037            // TEXT/NTEXT/IMAGE - deprecated LOB types using textptr format
1038            TypeId::Text | TypeId::NText | TypeId::Image => {
1039                Self::decode_textptr_type(src, dst)?;
1040            }
1041
1042            // XML - uses actual PLP format
1043            TypeId::Xml => {
1044                Self::decode_plp_type(src, dst)?;
1045            }
1046
1047            // Complex types
1048            TypeId::Variant => {
1049                Self::decode_intlen_type(src, dst)?;
1050            }
1051
1052            TypeId::Udt => {
1053                // UDT uses PLP encoding
1054                Self::decode_plp_type(src, dst)?;
1055            }
1056
1057            TypeId::Tvp => {
1058                // TVP not supported in row data
1059                return Err(ProtocolError::InvalidTokenType(col.col_type));
1060            }
1061        }
1062
1063        Ok(())
1064    }
1065
1066    /// Decode a 1-byte length-prefixed value.
1067    fn decode_bytelen_type(
1068        src: &mut impl Buf,
1069        dst: &mut bytes::BytesMut,
1070    ) -> Result<(), ProtocolError> {
1071        if src.remaining() < 1 {
1072            return Err(ProtocolError::UnexpectedEof);
1073        }
1074        let len = src.get_u8() as usize;
1075        if len == 0xFF {
1076            // NULL value - store as zero-length with NULL marker
1077            dst.extend_from_slice(&[0xFF]);
1078        } else if len == 0 {
1079            // Empty value
1080            dst.extend_from_slice(&[0x00]);
1081        } else {
1082            if src.remaining() < len {
1083                return Err(ProtocolError::UnexpectedEof);
1084            }
1085            dst.extend_from_slice(&[len as u8]);
1086            for _ in 0..len {
1087                dst.extend_from_slice(&[src.get_u8()]);
1088            }
1089        }
1090        Ok(())
1091    }
1092
1093    /// Decode a 2-byte length-prefixed value.
1094    fn decode_ushortlen_type(
1095        src: &mut impl Buf,
1096        dst: &mut bytes::BytesMut,
1097    ) -> Result<(), ProtocolError> {
1098        if src.remaining() < 2 {
1099            return Err(ProtocolError::UnexpectedEof);
1100        }
1101        let len = src.get_u16_le() as usize;
1102        if len == 0xFFFF {
1103            // NULL value
1104            dst.extend_from_slice(&0xFFFFu16.to_le_bytes());
1105        } else if len == 0 {
1106            // Empty value
1107            dst.extend_from_slice(&0u16.to_le_bytes());
1108        } else {
1109            if src.remaining() < len {
1110                return Err(ProtocolError::UnexpectedEof);
1111            }
1112            dst.extend_from_slice(&(len as u16).to_le_bytes());
1113            for _ in 0..len {
1114                dst.extend_from_slice(&[src.get_u8()]);
1115            }
1116        }
1117        Ok(())
1118    }
1119
1120    /// Decode a 4-byte length-prefixed value.
1121    fn decode_intlen_type(
1122        src: &mut impl Buf,
1123        dst: &mut bytes::BytesMut,
1124    ) -> Result<(), ProtocolError> {
1125        if src.remaining() < 4 {
1126            return Err(ProtocolError::UnexpectedEof);
1127        }
1128        let len = src.get_u32_le() as usize;
1129        if len == 0xFFFFFFFF {
1130            // NULL value
1131            dst.extend_from_slice(&0xFFFFFFFFu32.to_le_bytes());
1132        } else if len == 0 {
1133            // Empty value
1134            dst.extend_from_slice(&0u32.to_le_bytes());
1135        } else {
1136            if src.remaining() < len {
1137                return Err(ProtocolError::UnexpectedEof);
1138            }
1139            dst.extend_from_slice(&(len as u32).to_le_bytes());
1140            for _ in 0..len {
1141                dst.extend_from_slice(&[src.get_u8()]);
1142            }
1143        }
1144        Ok(())
1145    }
1146
1147    /// Decode a TEXT/NTEXT/IMAGE type (textptr format).
1148    ///
1149    /// These deprecated LOB types use a special format:
1150    /// - 1 byte: textptr_len (0 = NULL)
1151    /// - textptr_len bytes: textptr (if not NULL)
1152    /// - 8 bytes: timestamp (if not NULL)
1153    /// - 4 bytes: data length (if not NULL)
1154    /// - data_len bytes: the actual data (if not NULL)
1155    ///
1156    /// We convert this to PLP format for the client to parse:
1157    /// - 8 bytes: total length (0xFFFFFFFFFFFFFFFF = NULL)
1158    /// - 4 bytes: chunk length (= data length)
1159    /// - chunk data
1160    /// - 4 bytes: 0 (terminator)
1161    fn decode_textptr_type(
1162        src: &mut impl Buf,
1163        dst: &mut bytes::BytesMut,
1164    ) -> Result<(), ProtocolError> {
1165        if src.remaining() < 1 {
1166            return Err(ProtocolError::UnexpectedEof);
1167        }
1168
1169        let textptr_len = src.get_u8() as usize;
1170
1171        if textptr_len == 0 {
1172            // NULL value - write PLP NULL marker
1173            dst.extend_from_slice(&0xFFFFFFFFFFFFFFFFu64.to_le_bytes());
1174            return Ok(());
1175        }
1176
1177        // Skip textptr bytes
1178        if src.remaining() < textptr_len {
1179            return Err(ProtocolError::UnexpectedEof);
1180        }
1181        src.advance(textptr_len);
1182
1183        // Skip 8-byte timestamp
1184        if src.remaining() < 8 {
1185            return Err(ProtocolError::UnexpectedEof);
1186        }
1187        src.advance(8);
1188
1189        // Read data length
1190        if src.remaining() < 4 {
1191            return Err(ProtocolError::UnexpectedEof);
1192        }
1193        let data_len = src.get_u32_le() as usize;
1194
1195        if src.remaining() < data_len {
1196            return Err(ProtocolError::UnexpectedEof);
1197        }
1198
1199        // Write in PLP format for client parsing:
1200        // - 8 bytes: total length
1201        // - 4 bytes: chunk length
1202        // - chunk data
1203        // - 4 bytes: 0 (terminator)
1204        dst.extend_from_slice(&(data_len as u64).to_le_bytes());
1205        dst.extend_from_slice(&(data_len as u32).to_le_bytes());
1206        for _ in 0..data_len {
1207            dst.extend_from_slice(&[src.get_u8()]);
1208        }
1209        dst.extend_from_slice(&0u32.to_le_bytes()); // PLP terminator
1210
1211        Ok(())
1212    }
1213
1214    /// Decode a PLP (Partially Length-Prefixed) value.
1215    ///
1216    /// PLP format:
1217    /// - 8 bytes: total length (0xFFFFFFFFFFFFFFFE = unknown, 0xFFFFFFFFFFFFFFFF = NULL)
1218    /// - If not NULL: chunks of (4 byte chunk length + data) until chunk length = 0
1219    fn decode_plp_type(src: &mut impl Buf, dst: &mut bytes::BytesMut) -> Result<(), ProtocolError> {
1220        if src.remaining() < 8 {
1221            return Err(ProtocolError::UnexpectedEof);
1222        }
1223
1224        let total_len = src.get_u64_le();
1225
1226        // Store the total length marker
1227        dst.extend_from_slice(&total_len.to_le_bytes());
1228
1229        if total_len == 0xFFFFFFFFFFFFFFFF {
1230            // NULL value - no more data
1231            return Ok(());
1232        }
1233
1234        // Read chunks until terminator
1235        loop {
1236            if src.remaining() < 4 {
1237                return Err(ProtocolError::UnexpectedEof);
1238            }
1239            let chunk_len = src.get_u32_le() as usize;
1240            dst.extend_from_slice(&(chunk_len as u32).to_le_bytes());
1241
1242            if chunk_len == 0 {
1243                // End of PLP data
1244                break;
1245            }
1246
1247            if src.remaining() < chunk_len {
1248                return Err(ProtocolError::UnexpectedEof);
1249            }
1250
1251            for _ in 0..chunk_len {
1252                dst.extend_from_slice(&[src.get_u8()]);
1253            }
1254        }
1255
1256        Ok(())
1257    }
1258}
1259
1260// =============================================================================
1261// NbcRow Parsing Implementation
1262// =============================================================================
1263
1264impl NbcRow {
1265    /// Decode an NBCROW token from bytes.
1266    ///
1267    /// NBCROW (Null Bitmap Compressed Row) stores a bitmap indicating which
1268    /// columns are NULL, followed by only the non-NULL values.
1269    pub fn decode(src: &mut impl Buf, metadata: &ColMetaData) -> Result<Self, ProtocolError> {
1270        let col_count = metadata.columns.len();
1271        let bitmap_len = (col_count + 7) / 8;
1272
1273        if src.remaining() < bitmap_len {
1274            return Err(ProtocolError::UnexpectedEof);
1275        }
1276
1277        // Read null bitmap
1278        let mut null_bitmap = vec![0u8; bitmap_len];
1279        for byte in &mut null_bitmap {
1280            *byte = src.get_u8();
1281        }
1282
1283        // Read non-null values
1284        let mut data = bytes::BytesMut::new();
1285
1286        for (i, col) in metadata.columns.iter().enumerate() {
1287            let byte_idx = i / 8;
1288            let bit_idx = i % 8;
1289            let is_null = (null_bitmap[byte_idx] & (1 << bit_idx)) != 0;
1290
1291            if !is_null {
1292                // Read the value - for NBCROW, we read without the length prefix
1293                // for fixed-length types, and with length prefix for variable types
1294                RawRow::decode_column_value(src, col, &mut data)?;
1295            }
1296        }
1297
1298        Ok(Self {
1299            null_bitmap,
1300            data: data.freeze(),
1301        })
1302    }
1303
1304    /// Check if a column at the given index is NULL.
1305    #[must_use]
1306    pub fn is_null(&self, column_index: usize) -> bool {
1307        let byte_idx = column_index / 8;
1308        let bit_idx = column_index % 8;
1309        if byte_idx < self.null_bitmap.len() {
1310            (self.null_bitmap[byte_idx] & (1 << bit_idx)) != 0
1311        } else {
1312            true // Out of bounds = NULL
1313        }
1314    }
1315}
1316
1317// =============================================================================
1318// ReturnValue Parsing Implementation
1319// =============================================================================
1320
1321impl ReturnValue {
1322    /// Decode a RETURNVALUE token from bytes.
1323    pub fn decode(src: &mut impl Buf) -> Result<Self, ProtocolError> {
1324        // Length (2 bytes)
1325        if src.remaining() < 2 {
1326            return Err(ProtocolError::UnexpectedEof);
1327        }
1328        let _length = src.get_u16_le();
1329
1330        // Parameter ordinal (2 bytes)
1331        if src.remaining() < 2 {
1332            return Err(ProtocolError::UnexpectedEof);
1333        }
1334        let param_ordinal = src.get_u16_le();
1335
1336        // Parameter name (B_VARCHAR)
1337        let param_name = read_b_varchar(src).ok_or(ProtocolError::UnexpectedEof)?;
1338
1339        // Status (1 byte)
1340        if src.remaining() < 1 {
1341            return Err(ProtocolError::UnexpectedEof);
1342        }
1343        let status = src.get_u8();
1344
1345        // User type (4 bytes) + flags (2 bytes) + type id (1 byte)
1346        if src.remaining() < 7 {
1347            return Err(ProtocolError::UnexpectedEof);
1348        }
1349        let user_type = src.get_u32_le();
1350        let flags = src.get_u16_le();
1351        let col_type = src.get_u8();
1352
1353        let type_id = TypeId::from_u8(col_type).unwrap_or(TypeId::Null);
1354
1355        // Parse type info
1356        let type_info = ColMetaData::decode_type_info(src, type_id, col_type)?;
1357
1358        // Read the value data
1359        let mut value_buf = bytes::BytesMut::new();
1360
1361        // Create a temporary column for value parsing
1362        let temp_col = ColumnData {
1363            name: String::new(),
1364            type_id,
1365            col_type,
1366            flags,
1367            user_type,
1368            type_info: type_info.clone(),
1369        };
1370
1371        RawRow::decode_column_value(src, &temp_col, &mut value_buf)?;
1372
1373        Ok(Self {
1374            param_ordinal,
1375            param_name,
1376            status,
1377            user_type,
1378            flags,
1379            type_info,
1380            value: value_buf.freeze(),
1381        })
1382    }
1383}
1384
1385// =============================================================================
1386// SessionState Parsing Implementation
1387// =============================================================================
1388
1389impl SessionState {
1390    /// Decode a SESSIONSTATE token from bytes.
1391    pub fn decode(src: &mut impl Buf) -> Result<Self, ProtocolError> {
1392        if src.remaining() < 4 {
1393            return Err(ProtocolError::UnexpectedEof);
1394        }
1395
1396        let length = src.get_u32_le() as usize;
1397
1398        if src.remaining() < length {
1399            return Err(ProtocolError::IncompletePacket {
1400                expected: length,
1401                actual: src.remaining(),
1402            });
1403        }
1404
1405        let data = src.copy_to_bytes(length);
1406
1407        Ok(Self { data })
1408    }
1409}
1410
1411// =============================================================================
1412// Token Parsing Implementation
1413// =============================================================================
1414
1415/// Done token status flags bit positions.
1416mod done_status_bits {
1417    pub const DONE_MORE: u16 = 0x0001;
1418    pub const DONE_ERROR: u16 = 0x0002;
1419    pub const DONE_INXACT: u16 = 0x0004;
1420    pub const DONE_COUNT: u16 = 0x0010;
1421    pub const DONE_ATTN: u16 = 0x0020;
1422    pub const DONE_SRVERROR: u16 = 0x0100;
1423}
1424
1425impl DoneStatus {
1426    /// Parse done status from raw bits.
1427    #[must_use]
1428    pub fn from_bits(bits: u16) -> Self {
1429        use done_status_bits::*;
1430        Self {
1431            more: (bits & DONE_MORE) != 0,
1432            error: (bits & DONE_ERROR) != 0,
1433            in_xact: (bits & DONE_INXACT) != 0,
1434            count: (bits & DONE_COUNT) != 0,
1435            attn: (bits & DONE_ATTN) != 0,
1436            srverror: (bits & DONE_SRVERROR) != 0,
1437        }
1438    }
1439
1440    /// Convert to raw bits.
1441    #[must_use]
1442    pub fn to_bits(&self) -> u16 {
1443        use done_status_bits::*;
1444        let mut bits = 0u16;
1445        if self.more {
1446            bits |= DONE_MORE;
1447        }
1448        if self.error {
1449            bits |= DONE_ERROR;
1450        }
1451        if self.in_xact {
1452            bits |= DONE_INXACT;
1453        }
1454        if self.count {
1455            bits |= DONE_COUNT;
1456        }
1457        if self.attn {
1458            bits |= DONE_ATTN;
1459        }
1460        if self.srverror {
1461            bits |= DONE_SRVERROR;
1462        }
1463        bits
1464    }
1465}
1466
1467impl Done {
1468    /// Size of the DONE token in bytes (excluding token type byte).
1469    pub const SIZE: usize = 12; // 2 (status) + 2 (curcmd) + 8 (rowcount)
1470
1471    /// Decode a DONE token from bytes.
1472    pub fn decode(src: &mut impl Buf) -> Result<Self, ProtocolError> {
1473        if src.remaining() < Self::SIZE {
1474            return Err(ProtocolError::IncompletePacket {
1475                expected: Self::SIZE,
1476                actual: src.remaining(),
1477            });
1478        }
1479
1480        let status = DoneStatus::from_bits(src.get_u16_le());
1481        let cur_cmd = src.get_u16_le();
1482        let row_count = src.get_u64_le();
1483
1484        Ok(Self {
1485            status,
1486            cur_cmd,
1487            row_count,
1488        })
1489    }
1490
1491    /// Encode the DONE token to bytes.
1492    pub fn encode(&self, dst: &mut impl BufMut) {
1493        dst.put_u8(TokenType::Done as u8);
1494        dst.put_u16_le(self.status.to_bits());
1495        dst.put_u16_le(self.cur_cmd);
1496        dst.put_u64_le(self.row_count);
1497    }
1498
1499    /// Check if more results follow this DONE token.
1500    #[must_use]
1501    pub const fn has_more(&self) -> bool {
1502        self.status.more
1503    }
1504
1505    /// Check if an error occurred.
1506    #[must_use]
1507    pub const fn has_error(&self) -> bool {
1508        self.status.error
1509    }
1510
1511    /// Check if the row count is valid.
1512    #[must_use]
1513    pub const fn has_count(&self) -> bool {
1514        self.status.count
1515    }
1516}
1517
1518impl DoneProc {
1519    /// Size of the DONEPROC token in bytes (excluding token type byte).
1520    pub const SIZE: usize = 12;
1521
1522    /// Decode a DONEPROC token from bytes.
1523    pub fn decode(src: &mut impl Buf) -> Result<Self, ProtocolError> {
1524        if src.remaining() < Self::SIZE {
1525            return Err(ProtocolError::IncompletePacket {
1526                expected: Self::SIZE,
1527                actual: src.remaining(),
1528            });
1529        }
1530
1531        let status = DoneStatus::from_bits(src.get_u16_le());
1532        let cur_cmd = src.get_u16_le();
1533        let row_count = src.get_u64_le();
1534
1535        Ok(Self {
1536            status,
1537            cur_cmd,
1538            row_count,
1539        })
1540    }
1541
1542    /// Encode the DONEPROC token to bytes.
1543    pub fn encode(&self, dst: &mut impl BufMut) {
1544        dst.put_u8(TokenType::DoneProc as u8);
1545        dst.put_u16_le(self.status.to_bits());
1546        dst.put_u16_le(self.cur_cmd);
1547        dst.put_u64_le(self.row_count);
1548    }
1549}
1550
1551impl DoneInProc {
1552    /// Size of the DONEINPROC token in bytes (excluding token type byte).
1553    pub const SIZE: usize = 12;
1554
1555    /// Decode a DONEINPROC token from bytes.
1556    pub fn decode(src: &mut impl Buf) -> Result<Self, ProtocolError> {
1557        if src.remaining() < Self::SIZE {
1558            return Err(ProtocolError::IncompletePacket {
1559                expected: Self::SIZE,
1560                actual: src.remaining(),
1561            });
1562        }
1563
1564        let status = DoneStatus::from_bits(src.get_u16_le());
1565        let cur_cmd = src.get_u16_le();
1566        let row_count = src.get_u64_le();
1567
1568        Ok(Self {
1569            status,
1570            cur_cmd,
1571            row_count,
1572        })
1573    }
1574
1575    /// Encode the DONEINPROC token to bytes.
1576    pub fn encode(&self, dst: &mut impl BufMut) {
1577        dst.put_u8(TokenType::DoneInProc as u8);
1578        dst.put_u16_le(self.status.to_bits());
1579        dst.put_u16_le(self.cur_cmd);
1580        dst.put_u64_le(self.row_count);
1581    }
1582}
1583
1584impl ServerError {
1585    /// Decode an ERROR token from bytes.
1586    pub fn decode(src: &mut impl Buf) -> Result<Self, ProtocolError> {
1587        // ERROR token: length (2) + number (4) + state (1) + class (1) +
1588        //              message (us_varchar) + server (b_varchar) + procedure (b_varchar) + line (4)
1589        if src.remaining() < 2 {
1590            return Err(ProtocolError::UnexpectedEof);
1591        }
1592
1593        let _length = src.get_u16_le();
1594
1595        if src.remaining() < 6 {
1596            return Err(ProtocolError::UnexpectedEof);
1597        }
1598
1599        let number = src.get_i32_le();
1600        let state = src.get_u8();
1601        let class = src.get_u8();
1602
1603        let message = read_us_varchar(src).ok_or(ProtocolError::UnexpectedEof)?;
1604        let server = read_b_varchar(src).ok_or(ProtocolError::UnexpectedEof)?;
1605        let procedure = read_b_varchar(src).ok_or(ProtocolError::UnexpectedEof)?;
1606
1607        if src.remaining() < 4 {
1608            return Err(ProtocolError::UnexpectedEof);
1609        }
1610        let line = src.get_i32_le();
1611
1612        Ok(Self {
1613            number,
1614            state,
1615            class,
1616            message,
1617            server,
1618            procedure,
1619            line,
1620        })
1621    }
1622
1623    /// Check if this is a fatal error (severity >= 20).
1624    #[must_use]
1625    pub const fn is_fatal(&self) -> bool {
1626        self.class >= 20
1627    }
1628
1629    /// Check if this error indicates the batch was aborted (severity >= 16).
1630    #[must_use]
1631    pub const fn is_batch_abort(&self) -> bool {
1632        self.class >= 16
1633    }
1634}
1635
1636impl ServerInfo {
1637    /// Decode an INFO token from bytes.
1638    ///
1639    /// INFO tokens have the same structure as ERROR tokens but with lower severity.
1640    pub fn decode(src: &mut impl Buf) -> Result<Self, ProtocolError> {
1641        if src.remaining() < 2 {
1642            return Err(ProtocolError::UnexpectedEof);
1643        }
1644
1645        let _length = src.get_u16_le();
1646
1647        if src.remaining() < 6 {
1648            return Err(ProtocolError::UnexpectedEof);
1649        }
1650
1651        let number = src.get_i32_le();
1652        let state = src.get_u8();
1653        let class = src.get_u8();
1654
1655        let message = read_us_varchar(src).ok_or(ProtocolError::UnexpectedEof)?;
1656        let server = read_b_varchar(src).ok_or(ProtocolError::UnexpectedEof)?;
1657        let procedure = read_b_varchar(src).ok_or(ProtocolError::UnexpectedEof)?;
1658
1659        if src.remaining() < 4 {
1660            return Err(ProtocolError::UnexpectedEof);
1661        }
1662        let line = src.get_i32_le();
1663
1664        Ok(Self {
1665            number,
1666            state,
1667            class,
1668            message,
1669            server,
1670            procedure,
1671            line,
1672        })
1673    }
1674}
1675
1676impl LoginAck {
1677    /// Decode a LOGINACK token from bytes.
1678    pub fn decode(src: &mut impl Buf) -> Result<Self, ProtocolError> {
1679        // LOGINACK: length (2) + interface (1) + tds_version (4) + prog_name (b_varchar) + prog_version (4)
1680        if src.remaining() < 2 {
1681            return Err(ProtocolError::UnexpectedEof);
1682        }
1683
1684        let _length = src.get_u16_le();
1685
1686        if src.remaining() < 5 {
1687            return Err(ProtocolError::UnexpectedEof);
1688        }
1689
1690        let interface = src.get_u8();
1691        let tds_version = src.get_u32_le();
1692        let prog_name = read_b_varchar(src).ok_or(ProtocolError::UnexpectedEof)?;
1693
1694        if src.remaining() < 4 {
1695            return Err(ProtocolError::UnexpectedEof);
1696        }
1697        let prog_version = src.get_u32_le();
1698
1699        Ok(Self {
1700            interface,
1701            tds_version,
1702            prog_name,
1703            prog_version,
1704        })
1705    }
1706
1707    /// Get the TDS version as a `TdsVersion`.
1708    #[must_use]
1709    pub fn tds_version(&self) -> crate::version::TdsVersion {
1710        crate::version::TdsVersion::new(self.tds_version)
1711    }
1712}
1713
1714impl EnvChangeType {
1715    /// Create from raw byte value.
1716    pub fn from_u8(value: u8) -> Option<Self> {
1717        match value {
1718            1 => Some(Self::Database),
1719            2 => Some(Self::Language),
1720            3 => Some(Self::CharacterSet),
1721            4 => Some(Self::PacketSize),
1722            5 => Some(Self::UnicodeSortingLocalId),
1723            6 => Some(Self::UnicodeComparisonFlags),
1724            7 => Some(Self::SqlCollation),
1725            8 => Some(Self::BeginTransaction),
1726            9 => Some(Self::CommitTransaction),
1727            10 => Some(Self::RollbackTransaction),
1728            11 => Some(Self::EnlistDtcTransaction),
1729            12 => Some(Self::DefectTransaction),
1730            13 => Some(Self::RealTimeLogShipping),
1731            15 => Some(Self::PromoteTransaction),
1732            16 => Some(Self::TransactionManagerAddress),
1733            17 => Some(Self::TransactionEnded),
1734            18 => Some(Self::ResetConnectionCompletionAck),
1735            19 => Some(Self::UserInstanceStarted),
1736            20 => Some(Self::Routing),
1737            _ => None,
1738        }
1739    }
1740}
1741
1742impl EnvChange {
1743    /// Decode an ENVCHANGE token from bytes.
1744    pub fn decode(src: &mut impl Buf) -> Result<Self, ProtocolError> {
1745        if src.remaining() < 3 {
1746            return Err(ProtocolError::UnexpectedEof);
1747        }
1748
1749        let length = src.get_u16_le() as usize;
1750        if src.remaining() < length {
1751            return Err(ProtocolError::IncompletePacket {
1752                expected: length,
1753                actual: src.remaining(),
1754            });
1755        }
1756
1757        let env_type_byte = src.get_u8();
1758        let env_type = EnvChangeType::from_u8(env_type_byte)
1759            .ok_or(ProtocolError::InvalidTokenType(env_type_byte))?;
1760
1761        let (new_value, old_value) = match env_type {
1762            EnvChangeType::Routing => {
1763                // Routing has special format
1764                let new_value = Self::decode_routing_value(src)?;
1765                let old_value = EnvChangeValue::Binary(Bytes::new());
1766                (new_value, old_value)
1767            }
1768            EnvChangeType::BeginTransaction
1769            | EnvChangeType::CommitTransaction
1770            | EnvChangeType::RollbackTransaction
1771            | EnvChangeType::EnlistDtcTransaction
1772            | EnvChangeType::SqlCollation => {
1773                // These use binary format per MS-TDS spec:
1774                // - Transaction tokens: transaction descriptor (8 bytes)
1775                // - SqlCollation: collation info (5 bytes: LCID + sort flags)
1776                let new_len = src.get_u8() as usize;
1777                let new_value = if new_len > 0 && src.remaining() >= new_len {
1778                    EnvChangeValue::Binary(src.copy_to_bytes(new_len))
1779                } else {
1780                    EnvChangeValue::Binary(Bytes::new())
1781                };
1782
1783                let old_len = src.get_u8() as usize;
1784                let old_value = if old_len > 0 && src.remaining() >= old_len {
1785                    EnvChangeValue::Binary(src.copy_to_bytes(old_len))
1786                } else {
1787                    EnvChangeValue::Binary(Bytes::new())
1788                };
1789
1790                (new_value, old_value)
1791            }
1792            _ => {
1793                // String format for most env changes
1794                let new_value = read_b_varchar(src)
1795                    .map(EnvChangeValue::String)
1796                    .unwrap_or(EnvChangeValue::String(String::new()));
1797
1798                let old_value = read_b_varchar(src)
1799                    .map(EnvChangeValue::String)
1800                    .unwrap_or(EnvChangeValue::String(String::new()));
1801
1802                (new_value, old_value)
1803            }
1804        };
1805
1806        Ok(Self {
1807            env_type,
1808            new_value,
1809            old_value,
1810        })
1811    }
1812
1813    fn decode_routing_value(src: &mut impl Buf) -> Result<EnvChangeValue, ProtocolError> {
1814        // Routing format: length (2) + protocol (1) + port (2) + server_len (2) + server (utf16)
1815        if src.remaining() < 2 {
1816            return Err(ProtocolError::UnexpectedEof);
1817        }
1818
1819        let _routing_len = src.get_u16_le();
1820
1821        if src.remaining() < 5 {
1822            return Err(ProtocolError::UnexpectedEof);
1823        }
1824
1825        let _protocol = src.get_u8();
1826        let port = src.get_u16_le();
1827        let server_len = src.get_u16_le() as usize;
1828
1829        // Read UTF-16LE server name
1830        if src.remaining() < server_len * 2 {
1831            return Err(ProtocolError::UnexpectedEof);
1832        }
1833
1834        let mut chars = Vec::with_capacity(server_len);
1835        for _ in 0..server_len {
1836            chars.push(src.get_u16_le());
1837        }
1838
1839        let host = String::from_utf16(&chars).map_err(|_| {
1840            ProtocolError::StringEncoding(
1841                #[cfg(feature = "std")]
1842                "invalid UTF-16 in routing hostname".to_string(),
1843                #[cfg(not(feature = "std"))]
1844                "invalid UTF-16 in routing hostname",
1845            )
1846        })?;
1847
1848        Ok(EnvChangeValue::Routing { host, port })
1849    }
1850
1851    /// Check if this is a routing redirect.
1852    #[must_use]
1853    pub fn is_routing(&self) -> bool {
1854        self.env_type == EnvChangeType::Routing
1855    }
1856
1857    /// Get routing information if this is a routing change.
1858    #[must_use]
1859    pub fn routing_info(&self) -> Option<(&str, u16)> {
1860        if let EnvChangeValue::Routing { host, port } = &self.new_value {
1861            Some((host, *port))
1862        } else {
1863            None
1864        }
1865    }
1866
1867    /// Get the new database name if this is a database change.
1868    #[must_use]
1869    pub fn new_database(&self) -> Option<&str> {
1870        if self.env_type == EnvChangeType::Database {
1871            if let EnvChangeValue::String(s) = &self.new_value {
1872                return Some(s);
1873            }
1874        }
1875        None
1876    }
1877}
1878
1879impl Order {
1880    /// Decode an ORDER token from bytes.
1881    pub fn decode(src: &mut impl Buf) -> Result<Self, ProtocolError> {
1882        if src.remaining() < 2 {
1883            return Err(ProtocolError::UnexpectedEof);
1884        }
1885
1886        let length = src.get_u16_le() as usize;
1887        let column_count = length / 2;
1888
1889        if src.remaining() < length {
1890            return Err(ProtocolError::IncompletePacket {
1891                expected: length,
1892                actual: src.remaining(),
1893            });
1894        }
1895
1896        let mut columns = Vec::with_capacity(column_count);
1897        for _ in 0..column_count {
1898            columns.push(src.get_u16_le());
1899        }
1900
1901        Ok(Self { columns })
1902    }
1903}
1904
1905impl FeatureExtAck {
1906    /// Feature terminator byte.
1907    pub const TERMINATOR: u8 = 0xFF;
1908
1909    /// Decode a FEATUREEXTACK token from bytes.
1910    pub fn decode(src: &mut impl Buf) -> Result<Self, ProtocolError> {
1911        let mut features = Vec::new();
1912
1913        loop {
1914            if !src.has_remaining() {
1915                return Err(ProtocolError::UnexpectedEof);
1916            }
1917
1918            let feature_id = src.get_u8();
1919            if feature_id == Self::TERMINATOR {
1920                break;
1921            }
1922
1923            if src.remaining() < 4 {
1924                return Err(ProtocolError::UnexpectedEof);
1925            }
1926
1927            let data_len = src.get_u32_le() as usize;
1928
1929            if src.remaining() < data_len {
1930                return Err(ProtocolError::IncompletePacket {
1931                    expected: data_len,
1932                    actual: src.remaining(),
1933                });
1934            }
1935
1936            let data = src.copy_to_bytes(data_len);
1937            features.push(FeatureAck { feature_id, data });
1938        }
1939
1940        Ok(Self { features })
1941    }
1942}
1943
1944impl SspiToken {
1945    /// Decode an SSPI token from bytes.
1946    pub fn decode(src: &mut impl Buf) -> Result<Self, ProtocolError> {
1947        if src.remaining() < 2 {
1948            return Err(ProtocolError::UnexpectedEof);
1949        }
1950
1951        let length = src.get_u16_le() as usize;
1952
1953        if src.remaining() < length {
1954            return Err(ProtocolError::IncompletePacket {
1955                expected: length,
1956                actual: src.remaining(),
1957            });
1958        }
1959
1960        let data = src.copy_to_bytes(length);
1961        Ok(Self { data })
1962    }
1963}
1964
1965impl FedAuthInfo {
1966    /// Decode a FEDAUTHINFO token from bytes.
1967    pub fn decode(src: &mut impl Buf) -> Result<Self, ProtocolError> {
1968        if src.remaining() < 4 {
1969            return Err(ProtocolError::UnexpectedEof);
1970        }
1971
1972        let _length = src.get_u32_le();
1973
1974        if src.remaining() < 5 {
1975            return Err(ProtocolError::UnexpectedEof);
1976        }
1977
1978        let _count = src.get_u8();
1979
1980        // Read option data
1981        let mut sts_url = String::new();
1982        let mut spn = String::new();
1983
1984        // Parse info options until we have both
1985        while src.has_remaining() {
1986            if src.remaining() < 9 {
1987                break;
1988            }
1989
1990            let info_id = src.get_u8();
1991            let info_len = src.get_u32_le() as usize;
1992            let _info_offset = src.get_u32_le();
1993
1994            if src.remaining() < info_len {
1995                break;
1996            }
1997
1998            // Read UTF-16LE string
1999            let char_count = info_len / 2;
2000            let mut chars = Vec::with_capacity(char_count);
2001            for _ in 0..char_count {
2002                chars.push(src.get_u16_le());
2003            }
2004
2005            if let Ok(value) = String::from_utf16(&chars) {
2006                match info_id {
2007                    0x01 => spn = value,
2008                    0x02 => sts_url = value,
2009                    _ => {}
2010                }
2011            }
2012        }
2013
2014        Ok(Self { sts_url, spn })
2015    }
2016}
2017
2018// =============================================================================
2019// Token Parser
2020// =============================================================================
2021
2022/// Token stream parser.
2023///
2024/// Parses a stream of TDS tokens from a byte buffer.
2025///
2026/// # Basic vs Context-Aware Parsing
2027///
2028/// Some tokens (like `Done`, `Error`, `LoginAck`) can be parsed without context.
2029/// Use [`next_token()`](TokenParser::next_token) for these.
2030///
2031/// Other tokens (like `ColMetaData`, `Row`, `NbcRow`) require column metadata
2032/// to parse correctly. Use [`next_token_with_metadata()`](TokenParser::next_token_with_metadata)
2033/// for these.
2034///
2035/// # Example
2036///
2037/// ```rust,ignore
2038/// let mut parser = TokenParser::new(data);
2039/// let mut metadata = None;
2040///
2041/// while let Some(token) = parser.next_token_with_metadata(metadata.as_ref())? {
2042///     match token {
2043///         Token::ColMetaData(meta) => {
2044///             metadata = Some(meta);
2045///         }
2046///         Token::Row(row) => {
2047///             // Process row using metadata
2048///         }
2049///         Token::Done(done) => {
2050///             if !done.has_more() {
2051///                 break;
2052///             }
2053///         }
2054///         _ => {}
2055///     }
2056/// }
2057/// ```
2058pub struct TokenParser {
2059    data: Bytes,
2060    position: usize,
2061}
2062
2063impl TokenParser {
2064    /// Create a new token parser from bytes.
2065    #[must_use]
2066    pub fn new(data: Bytes) -> Self {
2067        Self { data, position: 0 }
2068    }
2069
2070    /// Get remaining bytes in the buffer.
2071    #[must_use]
2072    pub fn remaining(&self) -> usize {
2073        self.data.len().saturating_sub(self.position)
2074    }
2075
2076    /// Check if there are more bytes to parse.
2077    #[must_use]
2078    pub fn has_remaining(&self) -> bool {
2079        self.position < self.data.len()
2080    }
2081
2082    /// Peek at the next token type without consuming it.
2083    #[must_use]
2084    pub fn peek_token_type(&self) -> Option<TokenType> {
2085        if self.position < self.data.len() {
2086            TokenType::from_u8(self.data[self.position])
2087        } else {
2088            None
2089        }
2090    }
2091
2092    /// Parse the next token from the stream.
2093    ///
2094    /// This method can only parse context-independent tokens. For tokens that
2095    /// require column metadata (ColMetaData, Row, NbcRow), use
2096    /// [`next_token_with_metadata()`](TokenParser::next_token_with_metadata).
2097    ///
2098    /// Returns `None` if no more tokens are available.
2099    pub fn next_token(&mut self) -> Result<Option<Token>, ProtocolError> {
2100        self.next_token_with_metadata(None)
2101    }
2102
2103    /// Parse the next token with optional column metadata context.
2104    ///
2105    /// When `metadata` is provided, this method can parse Row and NbcRow tokens.
2106    /// Without metadata, those tokens will return an error.
2107    ///
2108    /// Returns `None` if no more tokens are available.
2109    pub fn next_token_with_metadata(
2110        &mut self,
2111        metadata: Option<&ColMetaData>,
2112    ) -> Result<Option<Token>, ProtocolError> {
2113        if !self.has_remaining() {
2114            return Ok(None);
2115        }
2116
2117        let mut buf = &self.data[self.position..];
2118        let start_pos = self.position;
2119
2120        let token_type_byte = buf.get_u8();
2121        let token_type = TokenType::from_u8(token_type_byte);
2122
2123        let token = match token_type {
2124            Some(TokenType::Done) => {
2125                let done = Done::decode(&mut buf)?;
2126                Token::Done(done)
2127            }
2128            Some(TokenType::DoneProc) => {
2129                let done = DoneProc::decode(&mut buf)?;
2130                Token::DoneProc(done)
2131            }
2132            Some(TokenType::DoneInProc) => {
2133                let done = DoneInProc::decode(&mut buf)?;
2134                Token::DoneInProc(done)
2135            }
2136            Some(TokenType::Error) => {
2137                let error = ServerError::decode(&mut buf)?;
2138                Token::Error(error)
2139            }
2140            Some(TokenType::Info) => {
2141                let info = ServerInfo::decode(&mut buf)?;
2142                Token::Info(info)
2143            }
2144            Some(TokenType::LoginAck) => {
2145                let login_ack = LoginAck::decode(&mut buf)?;
2146                Token::LoginAck(login_ack)
2147            }
2148            Some(TokenType::EnvChange) => {
2149                let env_change = EnvChange::decode(&mut buf)?;
2150                Token::EnvChange(env_change)
2151            }
2152            Some(TokenType::Order) => {
2153                let order = Order::decode(&mut buf)?;
2154                Token::Order(order)
2155            }
2156            Some(TokenType::FeatureExtAck) => {
2157                let ack = FeatureExtAck::decode(&mut buf)?;
2158                Token::FeatureExtAck(ack)
2159            }
2160            Some(TokenType::Sspi) => {
2161                let sspi = SspiToken::decode(&mut buf)?;
2162                Token::Sspi(sspi)
2163            }
2164            Some(TokenType::FedAuthInfo) => {
2165                let info = FedAuthInfo::decode(&mut buf)?;
2166                Token::FedAuthInfo(info)
2167            }
2168            Some(TokenType::ReturnStatus) => {
2169                if buf.remaining() < 4 {
2170                    return Err(ProtocolError::UnexpectedEof);
2171                }
2172                let status = buf.get_i32_le();
2173                Token::ReturnStatus(status)
2174            }
2175            Some(TokenType::ColMetaData) => {
2176                let col_meta = ColMetaData::decode(&mut buf)?;
2177                Token::ColMetaData(col_meta)
2178            }
2179            Some(TokenType::Row) => {
2180                let meta = metadata.ok_or_else(|| {
2181                    ProtocolError::StringEncoding(
2182                        #[cfg(feature = "std")]
2183                        "Row token requires column metadata".to_string(),
2184                        #[cfg(not(feature = "std"))]
2185                        "Row token requires column metadata",
2186                    )
2187                })?;
2188                let row = RawRow::decode(&mut buf, meta)?;
2189                Token::Row(row)
2190            }
2191            Some(TokenType::NbcRow) => {
2192                let meta = metadata.ok_or_else(|| {
2193                    ProtocolError::StringEncoding(
2194                        #[cfg(feature = "std")]
2195                        "NbcRow token requires column metadata".to_string(),
2196                        #[cfg(not(feature = "std"))]
2197                        "NbcRow token requires column metadata",
2198                    )
2199                })?;
2200                let row = NbcRow::decode(&mut buf, meta)?;
2201                Token::NbcRow(row)
2202            }
2203            Some(TokenType::ReturnValue) => {
2204                let ret_val = ReturnValue::decode(&mut buf)?;
2205                Token::ReturnValue(ret_val)
2206            }
2207            Some(TokenType::SessionState) => {
2208                let session = SessionState::decode(&mut buf)?;
2209                Token::SessionState(session)
2210            }
2211            Some(TokenType::ColInfo) | Some(TokenType::TabName) | Some(TokenType::Offset) => {
2212                // These tokens are rarely used and have complex formats.
2213                // Skip them by reading the length and advancing.
2214                if buf.remaining() < 2 {
2215                    return Err(ProtocolError::UnexpectedEof);
2216                }
2217                let length = buf.get_u16_le() as usize;
2218                if buf.remaining() < length {
2219                    return Err(ProtocolError::IncompletePacket {
2220                        expected: length,
2221                        actual: buf.remaining(),
2222                    });
2223                }
2224                // Skip the data
2225                buf.advance(length);
2226                // Recursively get the next token
2227                self.position = start_pos + (self.data.len() - start_pos - buf.remaining());
2228                return self.next_token_with_metadata(metadata);
2229            }
2230            None => {
2231                return Err(ProtocolError::InvalidTokenType(token_type_byte));
2232            }
2233        };
2234
2235        // Update position based on how much was consumed
2236        let consumed = self.data.len() - start_pos - buf.remaining();
2237        self.position = start_pos + consumed;
2238
2239        Ok(Some(token))
2240    }
2241
2242    /// Skip the current token without fully parsing it.
2243    ///
2244    /// This is useful for skipping unknown or uninteresting tokens.
2245    pub fn skip_token(&mut self) -> Result<(), ProtocolError> {
2246        if !self.has_remaining() {
2247            return Ok(());
2248        }
2249
2250        let token_type_byte = self.data[self.position];
2251        let token_type = TokenType::from_u8(token_type_byte);
2252
2253        // Calculate how many bytes to skip based on token type
2254        let skip_amount = match token_type {
2255            // Fixed-size tokens
2256            Some(TokenType::Done) | Some(TokenType::DoneProc) | Some(TokenType::DoneInProc) => {
2257                1 + Done::SIZE // token type + 12 bytes
2258            }
2259            Some(TokenType::ReturnStatus) => {
2260                1 + 4 // token type + 4 bytes
2261            }
2262            // Variable-length tokens with 2-byte length prefix
2263            Some(TokenType::Error)
2264            | Some(TokenType::Info)
2265            | Some(TokenType::LoginAck)
2266            | Some(TokenType::EnvChange)
2267            | Some(TokenType::Order)
2268            | Some(TokenType::Sspi)
2269            | Some(TokenType::ColInfo)
2270            | Some(TokenType::TabName)
2271            | Some(TokenType::Offset)
2272            | Some(TokenType::ReturnValue) => {
2273                if self.remaining() < 3 {
2274                    return Err(ProtocolError::UnexpectedEof);
2275                }
2276                let length = u16::from_le_bytes([
2277                    self.data[self.position + 1],
2278                    self.data[self.position + 2],
2279                ]) as usize;
2280                1 + 2 + length // token type + length prefix + data
2281            }
2282            // Tokens with 4-byte length prefix
2283            Some(TokenType::SessionState) | Some(TokenType::FedAuthInfo) => {
2284                if self.remaining() < 5 {
2285                    return Err(ProtocolError::UnexpectedEof);
2286                }
2287                let length = u32::from_le_bytes([
2288                    self.data[self.position + 1],
2289                    self.data[self.position + 2],
2290                    self.data[self.position + 3],
2291                    self.data[self.position + 4],
2292                ]) as usize;
2293                1 + 4 + length
2294            }
2295            // FeatureExtAck has no length prefix - must parse
2296            Some(TokenType::FeatureExtAck) => {
2297                // Parse to find end
2298                let mut buf = &self.data[self.position + 1..];
2299                let _ = FeatureExtAck::decode(&mut buf)?;
2300                self.data.len() - self.position - buf.remaining()
2301            }
2302            // ColMetaData, Row, NbcRow require context and can't be easily skipped
2303            Some(TokenType::ColMetaData) | Some(TokenType::Row) | Some(TokenType::NbcRow) => {
2304                return Err(ProtocolError::InvalidTokenType(token_type_byte));
2305            }
2306            None => {
2307                return Err(ProtocolError::InvalidTokenType(token_type_byte));
2308            }
2309        };
2310
2311        if self.remaining() < skip_amount {
2312            return Err(ProtocolError::UnexpectedEof);
2313        }
2314
2315        self.position += skip_amount;
2316        Ok(())
2317    }
2318
2319    /// Get the current position in the buffer.
2320    #[must_use]
2321    pub fn position(&self) -> usize {
2322        self.position
2323    }
2324
2325    /// Reset the parser to the beginning.
2326    pub fn reset(&mut self) {
2327        self.position = 0;
2328    }
2329}
2330
2331// =============================================================================
2332// no_std support
2333// =============================================================================
2334
2335#[cfg(not(feature = "std"))]
2336use alloc::string::String;
2337#[cfg(not(feature = "std"))]
2338use alloc::vec::Vec;
2339
2340// =============================================================================
2341// Tests
2342// =============================================================================
2343
2344#[cfg(test)]
2345#[allow(clippy::unwrap_used, clippy::panic)]
2346mod tests {
2347    use super::*;
2348    use bytes::BytesMut;
2349
2350    #[test]
2351    fn test_done_roundtrip() {
2352        let done = Done {
2353            status: DoneStatus {
2354                more: false,
2355                error: false,
2356                in_xact: false,
2357                count: true,
2358                attn: false,
2359                srverror: false,
2360            },
2361            cur_cmd: 193, // SELECT
2362            row_count: 42,
2363        };
2364
2365        let mut buf = BytesMut::new();
2366        done.encode(&mut buf);
2367
2368        // Skip the token type byte
2369        let mut cursor = &buf[1..];
2370        let decoded = Done::decode(&mut cursor).unwrap();
2371
2372        assert_eq!(decoded.status.count, done.status.count);
2373        assert_eq!(decoded.cur_cmd, done.cur_cmd);
2374        assert_eq!(decoded.row_count, done.row_count);
2375    }
2376
2377    #[test]
2378    fn test_done_status_bits() {
2379        let status = DoneStatus {
2380            more: true,
2381            error: true,
2382            in_xact: true,
2383            count: true,
2384            attn: false,
2385            srverror: false,
2386        };
2387
2388        let bits = status.to_bits();
2389        let restored = DoneStatus::from_bits(bits);
2390
2391        assert_eq!(status.more, restored.more);
2392        assert_eq!(status.error, restored.error);
2393        assert_eq!(status.in_xact, restored.in_xact);
2394        assert_eq!(status.count, restored.count);
2395    }
2396
2397    #[test]
2398    fn test_token_parser_done() {
2399        // DONE token: type (1) + status (2) + curcmd (2) + rowcount (8)
2400        let data = Bytes::from_static(&[
2401            0xFD, // DONE token type
2402            0x10, 0x00, // status: DONE_COUNT
2403            0xC1, 0x00, // cur_cmd: 193 (SELECT)
2404            0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // row_count: 5
2405        ]);
2406
2407        let mut parser = TokenParser::new(data);
2408        let token = parser.next_token().unwrap().unwrap();
2409
2410        match token {
2411            Token::Done(done) => {
2412                assert!(done.status.count);
2413                assert!(!done.status.more);
2414                assert_eq!(done.cur_cmd, 193);
2415                assert_eq!(done.row_count, 5);
2416            }
2417            _ => panic!("Expected Done token"),
2418        }
2419
2420        // No more tokens
2421        assert!(parser.next_token().unwrap().is_none());
2422    }
2423
2424    #[test]
2425    fn test_env_change_type_from_u8() {
2426        assert_eq!(EnvChangeType::from_u8(1), Some(EnvChangeType::Database));
2427        assert_eq!(EnvChangeType::from_u8(20), Some(EnvChangeType::Routing));
2428        assert_eq!(EnvChangeType::from_u8(100), None);
2429    }
2430
2431    #[test]
2432    fn test_colmetadata_no_columns() {
2433        // No metadata marker (0xFFFF)
2434        let data = Bytes::from_static(&[0xFF, 0xFF]);
2435        let mut cursor: &[u8] = &data;
2436        let meta = ColMetaData::decode(&mut cursor).unwrap();
2437        assert!(meta.is_empty());
2438        assert_eq!(meta.column_count(), 0);
2439    }
2440
2441    #[test]
2442    fn test_colmetadata_single_int_column() {
2443        // COLMETADATA with 1 INT column
2444        // Format: column_count (2) + [user_type (4) + flags (2) + type_id (1) + name (b_varchar)]
2445        let mut data = BytesMut::new();
2446        data.extend_from_slice(&[0x01, 0x00]); // 1 column
2447        data.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); // user_type = 0
2448        data.extend_from_slice(&[0x01, 0x00]); // flags (nullable)
2449        data.extend_from_slice(&[0x38]); // TypeId::Int4
2450        // Column name "id" as B_VARCHAR (1 byte length + UTF-16LE)
2451        data.extend_from_slice(&[0x02]); // 2 characters
2452        data.extend_from_slice(&[b'i', 0x00, b'd', 0x00]); // "id" in UTF-16LE
2453
2454        let mut cursor: &[u8] = &data;
2455        let meta = ColMetaData::decode(&mut cursor).unwrap();
2456
2457        assert_eq!(meta.column_count(), 1);
2458        assert_eq!(meta.columns[0].name, "id");
2459        assert_eq!(meta.columns[0].type_id, TypeId::Int4);
2460        assert!(meta.columns[0].is_nullable());
2461    }
2462
2463    #[test]
2464    fn test_colmetadata_nvarchar_column() {
2465        // COLMETADATA with 1 NVARCHAR(50) column
2466        let mut data = BytesMut::new();
2467        data.extend_from_slice(&[0x01, 0x00]); // 1 column
2468        data.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); // user_type = 0
2469        data.extend_from_slice(&[0x01, 0x00]); // flags (nullable)
2470        data.extend_from_slice(&[0xE7]); // TypeId::NVarChar
2471        // Type info: max_length (2 bytes) + collation (5 bytes)
2472        data.extend_from_slice(&[0x64, 0x00]); // max_length = 100 (50 chars * 2)
2473        data.extend_from_slice(&[0x09, 0x04, 0xD0, 0x00, 0x34]); // collation
2474        // Column name "name"
2475        data.extend_from_slice(&[0x04]); // 4 characters
2476        data.extend_from_slice(&[b'n', 0x00, b'a', 0x00, b'm', 0x00, b'e', 0x00]);
2477
2478        let mut cursor: &[u8] = &data;
2479        let meta = ColMetaData::decode(&mut cursor).unwrap();
2480
2481        assert_eq!(meta.column_count(), 1);
2482        assert_eq!(meta.columns[0].name, "name");
2483        assert_eq!(meta.columns[0].type_id, TypeId::NVarChar);
2484        assert_eq!(meta.columns[0].type_info.max_length, Some(100));
2485        assert!(meta.columns[0].type_info.collation.is_some());
2486    }
2487
2488    #[test]
2489    fn test_raw_row_decode_int() {
2490        // Create metadata for a single INT column
2491        let metadata = ColMetaData {
2492            columns: vec![ColumnData {
2493                name: "id".to_string(),
2494                type_id: TypeId::Int4,
2495                col_type: 0x38,
2496                flags: 0,
2497                user_type: 0,
2498                type_info: TypeInfo::default(),
2499            }],
2500        };
2501
2502        // Row data: just 4 bytes for the int value 42
2503        let data = Bytes::from_static(&[0x2A, 0x00, 0x00, 0x00]); // 42 in little-endian
2504        let mut cursor: &[u8] = &data;
2505        let row = RawRow::decode(&mut cursor, &metadata).unwrap();
2506
2507        // The raw data should contain the 4 bytes
2508        assert_eq!(row.data.len(), 4);
2509        assert_eq!(&row.data[..], &[0x2A, 0x00, 0x00, 0x00]);
2510    }
2511
2512    #[test]
2513    fn test_raw_row_decode_nullable_int() {
2514        // Create metadata for a nullable INT column (IntN)
2515        let metadata = ColMetaData {
2516            columns: vec![ColumnData {
2517                name: "id".to_string(),
2518                type_id: TypeId::IntN,
2519                col_type: 0x26,
2520                flags: 0x01, // nullable
2521                user_type: 0,
2522                type_info: TypeInfo {
2523                    max_length: Some(4),
2524                    ..Default::default()
2525                },
2526            }],
2527        };
2528
2529        // Row data with value: 1 byte length + 4 bytes value
2530        let data = Bytes::from_static(&[0x04, 0x2A, 0x00, 0x00, 0x00]); // length=4, value=42
2531        let mut cursor: &[u8] = &data;
2532        let row = RawRow::decode(&mut cursor, &metadata).unwrap();
2533
2534        assert_eq!(row.data.len(), 5);
2535        assert_eq!(row.data[0], 4); // length
2536        assert_eq!(&row.data[1..], &[0x2A, 0x00, 0x00, 0x00]);
2537    }
2538
2539    #[test]
2540    fn test_raw_row_decode_null_value() {
2541        // Create metadata for a nullable INT column (IntN)
2542        let metadata = ColMetaData {
2543            columns: vec![ColumnData {
2544                name: "id".to_string(),
2545                type_id: TypeId::IntN,
2546                col_type: 0x26,
2547                flags: 0x01, // nullable
2548                user_type: 0,
2549                type_info: TypeInfo {
2550                    max_length: Some(4),
2551                    ..Default::default()
2552                },
2553            }],
2554        };
2555
2556        // NULL value: length = 0xFF (for bytelen types)
2557        let data = Bytes::from_static(&[0xFF]);
2558        let mut cursor: &[u8] = &data;
2559        let row = RawRow::decode(&mut cursor, &metadata).unwrap();
2560
2561        assert_eq!(row.data.len(), 1);
2562        assert_eq!(row.data[0], 0xFF); // NULL marker
2563    }
2564
2565    #[test]
2566    fn test_nbcrow_null_bitmap() {
2567        let row = NbcRow {
2568            null_bitmap: vec![0b00000101], // columns 0 and 2 are NULL
2569            data: Bytes::new(),
2570        };
2571
2572        assert!(row.is_null(0));
2573        assert!(!row.is_null(1));
2574        assert!(row.is_null(2));
2575        assert!(!row.is_null(3));
2576    }
2577
2578    #[test]
2579    fn test_token_parser_colmetadata() {
2580        // Build a COLMETADATA token with 1 INT column
2581        let mut data = BytesMut::new();
2582        data.extend_from_slice(&[0x81]); // COLMETADATA token type
2583        data.extend_from_slice(&[0x01, 0x00]); // 1 column
2584        data.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); // user_type = 0
2585        data.extend_from_slice(&[0x01, 0x00]); // flags (nullable)
2586        data.extend_from_slice(&[0x38]); // TypeId::Int4
2587        data.extend_from_slice(&[0x02]); // column name length
2588        data.extend_from_slice(&[b'i', 0x00, b'd', 0x00]); // "id"
2589
2590        let mut parser = TokenParser::new(data.freeze());
2591        let token = parser.next_token().unwrap().unwrap();
2592
2593        match token {
2594            Token::ColMetaData(meta) => {
2595                assert_eq!(meta.column_count(), 1);
2596                assert_eq!(meta.columns[0].name, "id");
2597                assert_eq!(meta.columns[0].type_id, TypeId::Int4);
2598            }
2599            _ => panic!("Expected ColMetaData token"),
2600        }
2601    }
2602
2603    #[test]
2604    fn test_token_parser_row_with_metadata() {
2605        // Build metadata
2606        let metadata = ColMetaData {
2607            columns: vec![ColumnData {
2608                name: "id".to_string(),
2609                type_id: TypeId::Int4,
2610                col_type: 0x38,
2611                flags: 0,
2612                user_type: 0,
2613                type_info: TypeInfo::default(),
2614            }],
2615        };
2616
2617        // Build ROW token
2618        let mut data = BytesMut::new();
2619        data.extend_from_slice(&[0xD1]); // ROW token type
2620        data.extend_from_slice(&[0x2A, 0x00, 0x00, 0x00]); // value = 42
2621
2622        let mut parser = TokenParser::new(data.freeze());
2623        let token = parser
2624            .next_token_with_metadata(Some(&metadata))
2625            .unwrap()
2626            .unwrap();
2627
2628        match token {
2629            Token::Row(row) => {
2630                assert_eq!(row.data.len(), 4);
2631            }
2632            _ => panic!("Expected Row token"),
2633        }
2634    }
2635
2636    #[test]
2637    fn test_token_parser_row_without_metadata_fails() {
2638        // Build ROW token
2639        let mut data = BytesMut::new();
2640        data.extend_from_slice(&[0xD1]); // ROW token type
2641        data.extend_from_slice(&[0x2A, 0x00, 0x00, 0x00]); // value = 42
2642
2643        let mut parser = TokenParser::new(data.freeze());
2644        let result = parser.next_token(); // No metadata provided
2645
2646        assert!(result.is_err());
2647    }
2648
2649    #[test]
2650    fn test_token_parser_peek() {
2651        let data = Bytes::from_static(&[
2652            0xFD, // DONE token type
2653            0x10, 0x00, // status
2654            0xC1, 0x00, // cur_cmd
2655            0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // row_count
2656        ]);
2657
2658        let parser = TokenParser::new(data);
2659        assert_eq!(parser.peek_token_type(), Some(TokenType::Done));
2660    }
2661
2662    #[test]
2663    fn test_column_data_fixed_size() {
2664        let col = ColumnData {
2665            name: String::new(),
2666            type_id: TypeId::Int4,
2667            col_type: 0x38,
2668            flags: 0,
2669            user_type: 0,
2670            type_info: TypeInfo::default(),
2671        };
2672        assert_eq!(col.fixed_size(), Some(4));
2673
2674        let col2 = ColumnData {
2675            name: String::new(),
2676            type_id: TypeId::NVarChar,
2677            col_type: 0xE7,
2678            flags: 0,
2679            user_type: 0,
2680            type_info: TypeInfo::default(),
2681        };
2682        assert_eq!(col2.fixed_size(), None);
2683    }
2684
2685    // ========================================================================
2686    // End-to-End Decode Tests (Wire → Stored → Verification)
2687    // ========================================================================
2688    //
2689    // These tests verify that RawRow::decode_column_value correctly stores
2690    // column values in a format that can be parsed back.
2691
2692    #[test]
2693    fn test_decode_nvarchar_then_intn_roundtrip() {
2694        // Simulate wire data for: "World" (NVarChar), 42 (IntN)
2695        // This tests the scenario from the MCP parameterized query
2696
2697        // Build wire data (what the server sends)
2698        let mut wire_data = BytesMut::new();
2699
2700        // Column 0: NVarChar "World" - 2-byte length prefix in bytes
2701        // "World" in UTF-16LE: W=0x0057, o=0x006F, r=0x0072, l=0x006C, d=0x0064
2702        let word = "World";
2703        let utf16: Vec<u16> = word.encode_utf16().collect();
2704        wire_data.put_u16_le((utf16.len() * 2) as u16); // byte length = 10
2705        for code_unit in &utf16 {
2706            wire_data.put_u16_le(*code_unit);
2707        }
2708
2709        // Column 1: IntN 42 - 1-byte length prefix
2710        wire_data.put_u8(4); // 4 bytes for INT
2711        wire_data.put_i32_le(42);
2712
2713        // Build column metadata
2714        let metadata = ColMetaData {
2715            columns: vec![
2716                ColumnData {
2717                    name: "greeting".to_string(),
2718                    type_id: TypeId::NVarChar,
2719                    col_type: 0xE7,
2720                    flags: 0x01,
2721                    user_type: 0,
2722                    type_info: TypeInfo {
2723                        max_length: Some(10), // non-MAX
2724                        precision: None,
2725                        scale: None,
2726                        collation: None,
2727                    },
2728                },
2729                ColumnData {
2730                    name: "number".to_string(),
2731                    type_id: TypeId::IntN,
2732                    col_type: 0x26,
2733                    flags: 0x01,
2734                    user_type: 0,
2735                    type_info: TypeInfo {
2736                        max_length: Some(4),
2737                        precision: None,
2738                        scale: None,
2739                        collation: None,
2740                    },
2741                },
2742            ],
2743        };
2744
2745        // Decode the wire data into stored format
2746        let mut wire_cursor = wire_data.freeze();
2747        let raw_row = RawRow::decode(&mut wire_cursor, &metadata).unwrap();
2748
2749        // Verify wire data was fully consumed
2750        assert_eq!(
2751            wire_cursor.remaining(),
2752            0,
2753            "wire data should be fully consumed"
2754        );
2755
2756        // Now parse the stored data
2757        let mut stored_cursor: &[u8] = &raw_row.data;
2758
2759        // Parse column 0 (NVarChar)
2760        // Stored format for non-MAX NVarChar: [2-byte len][data]
2761        assert!(
2762            stored_cursor.remaining() >= 2,
2763            "need at least 2 bytes for length"
2764        );
2765        let len0 = stored_cursor.get_u16_le() as usize;
2766        assert_eq!(len0, 10, "NVarChar length should be 10 bytes");
2767        assert!(
2768            stored_cursor.remaining() >= len0,
2769            "need {len0} bytes for data"
2770        );
2771
2772        // Read UTF-16LE and convert to string
2773        let mut utf16_read = Vec::new();
2774        for _ in 0..(len0 / 2) {
2775            utf16_read.push(stored_cursor.get_u16_le());
2776        }
2777        let string0 = String::from_utf16(&utf16_read).unwrap();
2778        assert_eq!(string0, "World", "column 0 should be 'World'");
2779
2780        // Parse column 1 (IntN)
2781        // Stored format for IntN: [1-byte len][data]
2782        assert!(
2783            stored_cursor.remaining() >= 1,
2784            "need at least 1 byte for length"
2785        );
2786        let len1 = stored_cursor.get_u8();
2787        assert_eq!(len1, 4, "IntN length should be 4");
2788        assert!(stored_cursor.remaining() >= 4, "need 4 bytes for INT data");
2789        let int1 = stored_cursor.get_i32_le();
2790        assert_eq!(int1, 42, "column 1 should be 42");
2791
2792        // Verify stored data was fully consumed
2793        assert_eq!(
2794            stored_cursor.remaining(),
2795            0,
2796            "stored data should be fully consumed"
2797        );
2798    }
2799
2800    #[test]
2801    fn test_decode_nvarchar_max_then_intn_roundtrip() {
2802        // Test NVARCHAR(MAX) followed by IntN - uses PLP encoding
2803
2804        // Build wire data for PLP NVARCHAR(MAX) + IntN
2805        let mut wire_data = BytesMut::new();
2806
2807        // Column 0: NVARCHAR(MAX) "Hello" - PLP format
2808        // PLP: 8-byte total length, then chunks
2809        let word = "Hello";
2810        let utf16: Vec<u16> = word.encode_utf16().collect();
2811        let byte_len = (utf16.len() * 2) as u64;
2812
2813        wire_data.put_u64_le(byte_len); // total length = 10
2814        wire_data.put_u32_le(byte_len as u32); // chunk length = 10
2815        for code_unit in &utf16 {
2816            wire_data.put_u16_le(*code_unit);
2817        }
2818        wire_data.put_u32_le(0); // terminating zero-length chunk
2819
2820        // Column 1: IntN 99
2821        wire_data.put_u8(4);
2822        wire_data.put_i32_le(99);
2823
2824        // Build metadata with MAX type
2825        let metadata = ColMetaData {
2826            columns: vec![
2827                ColumnData {
2828                    name: "text".to_string(),
2829                    type_id: TypeId::NVarChar,
2830                    col_type: 0xE7,
2831                    flags: 0x01,
2832                    user_type: 0,
2833                    type_info: TypeInfo {
2834                        max_length: Some(0xFFFF), // MAX indicator
2835                        precision: None,
2836                        scale: None,
2837                        collation: None,
2838                    },
2839                },
2840                ColumnData {
2841                    name: "num".to_string(),
2842                    type_id: TypeId::IntN,
2843                    col_type: 0x26,
2844                    flags: 0x01,
2845                    user_type: 0,
2846                    type_info: TypeInfo {
2847                        max_length: Some(4),
2848                        precision: None,
2849                        scale: None,
2850                        collation: None,
2851                    },
2852                },
2853            ],
2854        };
2855
2856        // Decode wire data
2857        let mut wire_cursor = wire_data.freeze();
2858        let raw_row = RawRow::decode(&mut wire_cursor, &metadata).unwrap();
2859
2860        // Verify wire data was fully consumed
2861        assert_eq!(
2862            wire_cursor.remaining(),
2863            0,
2864            "wire data should be fully consumed"
2865        );
2866
2867        // Parse stored PLP data for column 0
2868        let mut stored_cursor: &[u8] = &raw_row.data;
2869
2870        // PLP stored format: [8-byte total][chunks...][4-byte 0]
2871        let total_len = stored_cursor.get_u64_le();
2872        assert_eq!(total_len, 10, "PLP total length should be 10");
2873
2874        let chunk_len = stored_cursor.get_u32_le();
2875        assert_eq!(chunk_len, 10, "PLP chunk length should be 10");
2876
2877        let mut utf16_read = Vec::new();
2878        for _ in 0..(chunk_len / 2) {
2879            utf16_read.push(stored_cursor.get_u16_le());
2880        }
2881        let string0 = String::from_utf16(&utf16_read).unwrap();
2882        assert_eq!(string0, "Hello", "column 0 should be 'Hello'");
2883
2884        let terminator = stored_cursor.get_u32_le();
2885        assert_eq!(terminator, 0, "PLP should end with 0");
2886
2887        // Parse IntN
2888        let len1 = stored_cursor.get_u8();
2889        assert_eq!(len1, 4);
2890        let int1 = stored_cursor.get_i32_le();
2891        assert_eq!(int1, 99, "column 1 should be 99");
2892
2893        // Verify fully consumed
2894        assert_eq!(
2895            stored_cursor.remaining(),
2896            0,
2897            "stored data should be fully consumed"
2898        );
2899    }
2900}