Skip to main content

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