1#![forbid(unsafe_code)]
2
3use super::*;
17use crate::oson::{decode_oson_with_limits, encode_oson, OsonValue};
18use crate::wire::ProtocolLimits;
19
20#[derive(Clone, Debug, PartialEq, Eq)]
23pub enum AqPayloadKind {
24 Raw,
26 Json,
28 Object,
30}
31
32#[derive(Clone, Debug)]
34pub struct AqQueueDesc {
35 pub name: String,
36 pub kind: AqPayloadKind,
37 pub payload_toid: Vec<u8>,
40}
41
42impl AqQueueDesc {
43 pub fn new(name: String, kind: AqPayloadKind, object_oid: Option<Vec<u8>>) -> Self {
46 let payload_toid = match kind {
47 AqPayloadKind::Raw => raw_payload_toid(),
48 AqPayloadKind::Json => json_payload_toid(),
49 AqPayloadKind::Object => object_oid.unwrap_or_default(),
50 };
51 Self {
52 name,
53 kind,
54 payload_toid,
55 }
56 }
57}
58
59fn raw_payload_toid() -> Vec<u8> {
60 let mut toid = vec![0u8; 15];
61 toid.push(0x17);
62 toid
63}
64
65fn json_payload_toid() -> Vec<u8> {
66 let mut toid = vec![0u8; 15];
67 toid.push(0x47);
68 toid
69}
70
71#[derive(Clone, Debug)]
73pub enum AqPayloadValue {
74 Raw(Vec<u8>),
76 Json(OsonValue),
78 Object { oid: Vec<u8>, image: Vec<u8> },
80}
81
82#[derive(Clone, Debug)]
85pub struct AqMsgProps {
86 pub priority: i32,
87 pub delay: i32,
88 pub expiration: i32,
89 pub correlation: Option<String>,
90 pub exception_queue: Option<String>,
91 pub state: i32,
92 pub enq_txn_id: Option<Vec<u8>>,
93 pub recipients: Option<Vec<String>>,
95 pub payload: Option<AqPayloadValue>,
98}
99
100impl Default for AqMsgProps {
101 fn default() -> Self {
102 Self {
103 priority: 0,
104 delay: 0,
105 expiration: -1,
106 correlation: None,
107 exception_queue: None,
108 state: 0,
109 enq_txn_id: None,
110 recipients: Some(Vec::new()),
114 payload: None,
115 }
116 }
117}
118
119#[derive(Clone, Debug)]
122pub struct AqEnqOptions {
123 pub visibility: u32,
124 pub delivery_mode: u16,
125}
126
127impl Default for AqEnqOptions {
128 fn default() -> Self {
129 Self {
130 visibility: 2,
131 delivery_mode: TNS_AQ_MSG_PERSISTENT,
132 }
133 }
134}
135
136#[derive(Clone, Debug)]
140pub struct AqDeqOptions {
141 pub condition: Option<String>,
142 pub consumer_name: Option<String>,
143 pub correlation: Option<String>,
144 pub delivery_mode: u16,
145 pub mode: i32,
146 pub msgid: Option<Vec<u8>>,
147 pub navigation: i32,
148 pub visibility: i32,
149 pub wait: u32,
150}
151
152impl Default for AqDeqOptions {
153 fn default() -> Self {
154 Self {
155 condition: None,
156 consumer_name: None,
157 correlation: None,
158 delivery_mode: TNS_AQ_MSG_PERSISTENT,
159 mode: 3,
160 msgid: None,
161 navigation: 3,
162 visibility: 2,
163 wait: 0xFFFF_FFFF,
164 }
165 }
166}
167
168#[derive(Clone, Debug, Default)]
171pub struct AqDeqMessage {
172 pub priority: i32,
173 pub delay: i32,
174 pub expiration: i32,
175 pub correlation: Option<String>,
176 pub num_attempts: i32,
177 pub exception_queue: Option<String>,
178 pub state: i32,
179 pub enq_time: Option<QueryValue>,
181 pub delivery_mode: u16,
182 pub msgid: Option<Vec<u8>>,
183 pub payload: Option<AqDeqPayload>,
185}
186
187#[derive(Clone, Debug)]
189pub enum AqDeqPayload {
190 Raw(Vec<u8>),
192 Json(OsonValue),
194 Object(Vec<u8>),
197}
198
199fn write_aq_function_code(
207 writer: &mut TtcWriter,
208 function_code: u8,
209 seq_num: u8,
210 ttc_field_version: u8,
211) {
212 writer.write_function_header(function_code, seq_num, ttc_field_version);
213}
214
215fn write_value_with_length(writer: &mut TtcWriter, value: Option<&[u8]>) -> Result<()> {
216 match value {
217 None => {
218 writer.write_ub4(0);
219 Ok(())
220 }
221 Some(bytes) => writer.write_bytes_with_two_lengths(Some(bytes)),
222 }
223}
224
225fn write_msg_props(
227 writer: &mut TtcWriter,
228 props: &AqMsgProps,
229 ttc_field_version: u8,
230) -> Result<()> {
231 writer.write_ub4(props.priority as u32);
232 writer.write_ub4(props.delay as u32);
233 writer.write_sb4(props.expiration);
234 write_value_with_length(writer, props.correlation.as_deref().map(str::as_bytes))?;
235 writer.write_ub4(0); write_value_with_length(writer, props.exception_queue.as_deref().map(str::as_bytes))?;
237 writer.write_ub4(props.state as u32);
238 writer.write_ub4(0); write_value_with_length(writer, props.enq_txn_id.as_deref())?;
240 writer.write_ub4(4); writer.write_u8(0x0e); writer.write_keyword_value_pair(None, None, TNS_AQ_EXT_KEYWORD_AGENT_NAME)?;
243 writer.write_keyword_value_pair(None, None, TNS_AQ_EXT_KEYWORD_AGENT_ADDRESS)?;
244 writer.write_keyword_value_pair(None, Some(b"\x00"), TNS_AQ_EXT_KEYWORD_AGENT_PROTOCOL)?;
245 writer.write_keyword_value_pair(None, None, TNS_AQ_EXT_KEYWORD_ORIGINAL_MSGID)?;
246 writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0); if version_gates::carries_aq_shard_id(ttc_field_version) {
251 writer.write_ub4(0xFFFF_FFFF); }
253 Ok(())
254}
255
256fn write_recipients(writer: &mut TtcWriter, recipients: &[String]) -> Result<()> {
258 let mut index: u16 = 0;
259 for recipient in recipients {
260 writer.write_keyword_value_pair(Some(recipient.as_bytes()), None, index)?;
261 writer.write_keyword_value_pair(None, None, index + 1)?;
262 writer.write_keyword_value_pair(None, Some(b"\x00"), index + 2)?;
263 index += 3;
264 }
265 Ok(())
266}
267
268fn write_payload(
270 writer: &mut TtcWriter,
271 payload: &AqPayloadValue,
272 supports_oson_long_fnames: bool,
273) -> Result<()> {
274 match payload {
275 AqPayloadValue::Json(value) => {
276 let image = encode_oson(value, supports_oson_long_fnames)?;
279 crate::vector::write_oson_aq_payload(writer, &image)
280 }
281 AqPayloadValue::Object { oid, image } => write_dbobject_bind(writer, oid, image),
282 AqPayloadValue::Raw(bytes) => {
283 writer.write_raw(bytes);
284 Ok(())
285 }
286 }
287}
288
289pub fn build_aq_enq_payload(
296 queue: &AqQueueDesc,
297 props: &AqMsgProps,
298 enq_options: &AqEnqOptions,
299 seq_num: u8,
300 ttc_field_version: u8,
301 supports_oson_long_fnames: bool,
302) -> Result<Vec<u8>> {
303 let payload = props
304 .payload
305 .as_ref()
306 .ok_or(ProtocolError::TtcDecode("AQ enqueue has no payload"))?;
307 let queue_name = queue.name.as_bytes();
308 let mut writer = TtcWriter::new();
309 write_aq_function_code(&mut writer, TNS_FUNC_AQ_ENQ, seq_num, ttc_field_version);
310 writer.write_u8(1); writer.write_ub4(queue_name.len() as u32);
312 write_msg_props(&mut writer, props, ttc_field_version)?;
313 match props.recipients.as_ref() {
314 None => {
315 writer.write_u8(0); writer.write_ub4(0); }
318 Some(recipients) => {
319 writer.write_u8(1);
320 writer.write_ub4(3 * recipients.len() as u32);
321 }
322 }
323 writer.write_ub4(enq_options.visibility);
324 writer.write_u8(0); writer.write_ub4(0); writer.write_ub4(0); writer.write_u8(1); writer.write_ub4(16); writer.write_ub2(TNS_AQ_MESSAGE_VERSION);
330 match queue.kind {
331 AqPayloadKind::Json => {
332 writer.write_u8(0); writer.write_u8(0); writer.write_ub4(0); }
336 AqPayloadKind::Object => {
337 writer.write_u8(1); writer.write_u8(0); writer.write_ub4(0); }
341 AqPayloadKind::Raw => {
342 let raw_len = match payload {
343 AqPayloadValue::Raw(bytes) => bytes.len() as u32,
344 _ => return Err(ProtocolError::TtcDecode("RAW queue requires RAW payload")),
345 };
346 writer.write_u8(0); writer.write_u8(1); writer.write_ub4(raw_len);
349 }
350 }
351 writer.write_u8(1); writer.write_ub4(TNS_AQ_MESSAGE_ID_LENGTH as u32);
353 let mut enq_flags = 0u32;
354 if enq_options.delivery_mode == TNS_AQ_MSG_BUFFERED {
355 enq_flags |= TNS_KPD_AQ_BUFMSG;
356 }
357 writer.write_ub4(enq_flags); writer.write_u8(0); writer.write_ub4(0); writer.write_u8(0); writer.write_ub4(0); writer.write_u8(0); writer.write_ub4(0); writer.write_u8(0); writer.write_ub4(0); writer.write_u8(0); writer.write_u8(0); writer.write_ub4(0); writer.write_u8(0); writer.write_ub4(0); writer.write_u8(0); writer.write_ub4(0); writer.write_u8(0); writer.write_u8(0); if version_gates::writes_aq_json_payload(ttc_field_version) {
376 writer.write_u8(u8::from(queue.kind == AqPayloadKind::Json));
378 }
379
380 writer.write_bytes_with_length(queue_name)?;
381 if let Some(recipients) = props.recipients.as_ref() {
382 write_recipients(&mut writer, recipients)?;
383 }
384 writer.write_raw(&queue.payload_toid);
385 write_payload(&mut writer, payload, supports_oson_long_fnames)?;
386 Ok(writer.into_bytes())
387}
388
389pub fn parse_aq_enq_response(
392 payload: &[u8],
393 capabilities: ClientCapabilities,
394) -> Result<Option<Vec<u8>>> {
395 parse_aq_enq_response_with_limits(payload, capabilities, ProtocolLimits::DEFAULT)
396}
397
398pub fn parse_aq_enq_response_with_limits(
399 payload: &[u8],
400 capabilities: ClientCapabilities,
401 limits: ProtocolLimits,
402) -> Result<Option<Vec<u8>>> {
403 let mut reader = TtcReader::with_limits(payload, limits)?;
404 let mut msgid: Option<Vec<u8>> = None;
405 while reader.remaining() > 0 {
406 let message_type = reader.read_u8()?;
407 match message_type {
408 0 => {}
409 TNS_MSG_TYPE_PARAMETER => {
410 let id = reader.read_raw(TNS_AQ_MESSAGE_ID_LENGTH)?.to_vec();
411 let _ext_len = reader.read_ub2()?;
412 msgid = Some(id);
413 }
414 TNS_MSG_TYPE_STATUS => {
415 let _call_status = reader.read_ub4()?;
416 let _seq = reader.read_ub2()?;
417 }
418 TNS_MSG_TYPE_SERVER_SIDE_PIGGYBACK => {
419 let _ = skip_server_side_piggyback(&mut reader)?;
420 }
421 TNS_MSG_TYPE_END_OF_RESPONSE => break,
422 TNS_MSG_TYPE_ERROR => {
423 let info = parse_server_error_info(&mut reader, capabilities.ttc_field_version)?;
424 if info.number != 0 {
425 return Err(ProtocolError::ServerErrorInfo(Box::new(
426 info.into_details(),
427 )));
428 }
429 }
430 _ => {
431 return Err(ProtocolError::UnknownMessageType {
432 message_type,
433 position: reader.position().saturating_sub(1),
434 })
435 }
436 }
437 }
438 Ok(msgid)
439}
440
441pub fn build_aq_deq_payload(
448 queue: &AqQueueDesc,
449 deq_options: &AqDeqOptions,
450 seq_num: u8,
451 ttc_field_version: u8,
452) -> Result<Vec<u8>> {
453 let queue_name = queue.name.as_bytes();
454 let mut writer = TtcWriter::new();
455 write_aq_function_code(&mut writer, TNS_FUNC_AQ_DEQ, seq_num, ttc_field_version);
456 writer.write_u8(1); writer.write_ub4(queue_name.len() as u32);
458 writer.write_u8(1); writer.write_u8(1); writer.write_u8(1); writer.write_u8(1); let consumer_name = deq_options
463 .consumer_name
464 .as_ref()
465 .filter(|name| !name.is_empty());
466 match consumer_name {
467 Some(name) => {
468 writer.write_u8(1);
469 writer.write_ub4(name.len() as u32);
470 }
471 None => {
472 writer.write_u8(0);
473 writer.write_ub4(0);
474 }
475 }
476 writer.write_sb4(deq_options.mode);
477 writer.write_sb4(deq_options.navigation);
478 writer.write_sb4(deq_options.visibility);
479 writer.write_sb4(deq_options.wait as i32);
480 let msgid = deq_options.msgid.as_ref().filter(|id| !id.is_empty());
481 match msgid {
482 Some(_) => {
483 writer.write_u8(1);
484 writer.write_ub4(TNS_AQ_MESSAGE_ID_LENGTH as u32);
485 }
486 None => {
487 writer.write_u8(0);
488 writer.write_ub4(0);
489 }
490 }
491 let correlation = deq_options.correlation.as_ref().filter(|c| !c.is_empty());
492 match correlation {
493 Some(c) => {
494 writer.write_u8(1);
495 writer.write_ub4(c.len() as u32);
496 }
497 None => {
498 writer.write_u8(0);
499 writer.write_ub4(0);
500 }
501 }
502 writer.write_u8(1); writer.write_ub4(16); writer.write_ub2(TNS_AQ_MESSAGE_VERSION);
505 writer.write_u8(1); writer.write_u8(1); writer.write_ub4(TNS_AQ_MESSAGE_ID_LENGTH as u32);
508 let mut deq_flags = 0u32;
509 match deq_options.delivery_mode {
510 TNS_AQ_MSG_BUFFERED => deq_flags |= TNS_KPD_AQ_BUFMSG,
511 TNS_AQ_MSG_PERSISTENT_OR_BUFFERED => deq_flags |= TNS_KPD_AQ_EITHER,
512 _ => {}
513 }
514 writer.write_ub4(deq_flags);
515 let condition = deq_options.condition.as_ref().filter(|c| !c.is_empty());
516 match condition {
517 Some(c) => {
518 writer.write_u8(1);
519 writer.write_ub4(c.len() as u32);
520 }
521 None => {
522 writer.write_u8(0);
523 writer.write_ub4(0);
524 }
525 }
526 writer.write_u8(0); writer.write_ub4(0); if version_gates::writes_aq_json_payload(ttc_field_version) {
529 writer.write_u8(0); }
531 if version_gates::carries_aq_shard_id(ttc_field_version) {
532 writer.write_ub4(0xFFFF_FFFF); }
534
535 writer.write_bytes_with_length(queue_name)?;
536 if let Some(name) = consumer_name {
537 writer.write_bytes_with_length(name.as_bytes())?;
538 }
539 if let Some(id) = msgid {
540 let mut id = id.clone();
541 id.truncate(16);
542 if id.len() < 16 {
543 id.resize(16, 0);
544 }
545 writer.write_raw(&id);
546 }
547 if let Some(c) = correlation {
548 writer.write_bytes_with_length(c.as_bytes())?;
549 }
550 writer.write_raw(&queue.payload_toid);
551 if let Some(c) = condition {
552 writer.write_bytes_with_length(c.as_bytes())?;
553 }
554 Ok(writer.into_bytes())
555}
556
557#[derive(Clone, Debug, Default)]
559pub struct AqDeqResult {
560 pub message: Option<AqDeqMessage>,
562}
563
564pub fn parse_aq_deq_response(
567 payload: &[u8],
568 capabilities: ClientCapabilities,
569 kind: &AqPayloadKind,
570) -> Result<AqDeqResult> {
571 parse_aq_deq_response_with_limits(payload, capabilities, kind, ProtocolLimits::DEFAULT)
572}
573
574pub fn parse_aq_deq_response_with_limits(
575 payload: &[u8],
576 capabilities: ClientCapabilities,
577 kind: &AqPayloadKind,
578 limits: ProtocolLimits,
579) -> Result<AqDeqResult> {
580 let mut reader = TtcReader::with_limits(payload, limits)?;
581 let mut result = AqDeqResult::default();
582 let mut no_msg_found = false;
583 while reader.remaining() > 0 {
584 let message_type = reader.read_u8()?;
585 match message_type {
586 0 => {}
587 TNS_MSG_TYPE_PARAMETER => {
588 let num_bytes = reader.read_ub4()?;
589 if num_bytes > 0 {
590 let mut message = AqDeqMessage::default();
591 process_msg_props(&mut reader, &mut message, capabilities.ttc_field_version)?;
592 process_recipients(&mut reader)?;
593 message.payload = process_payload(&mut reader, kind)?;
594 message.msgid = Some(process_msg_id(&mut reader)?);
595 result.message = Some(message);
596 }
597 }
598 TNS_MSG_TYPE_STATUS => {
599 let _call_status = reader.read_ub4()?;
600 let _seq = reader.read_ub2()?;
601 }
602 TNS_MSG_TYPE_SERVER_SIDE_PIGGYBACK => {
603 let _ = skip_server_side_piggyback(&mut reader)?;
604 }
605 TNS_MSG_TYPE_END_OF_RESPONSE => break,
606 TNS_MSG_TYPE_ERROR => {
607 let info = parse_server_error_info(&mut reader, capabilities.ttc_field_version)?;
608 if info.number == TNS_ERR_NO_MESSAGES_FOUND as u32 {
609 no_msg_found = true;
610 } else if info.number != 0 {
611 return Err(ProtocolError::ServerErrorInfo(Box::new(
612 info.into_details(),
613 )));
614 }
615 }
616 _ => {
617 return Err(ProtocolError::UnknownMessageType {
618 message_type,
619 position: reader.position().saturating_sub(1),
620 })
621 }
622 }
623 }
624 if no_msg_found {
625 result.message = None;
626 }
627 Ok(result)
628}
629
630pub fn build_aq_array_enq_payload(
637 queue: &AqQueueDesc,
638 props_list: &[AqMsgProps],
639 enq_options: &AqEnqOptions,
640 seq_num: u8,
641 ttc_field_version: u8,
642 supports_oson_long_fnames: bool,
643) -> Result<Vec<u8>> {
644 let num_iters = props_list.len() as u32;
645 let queue_name = queue.name.as_bytes();
646 let mut writer = TtcWriter::new();
647 write_aq_function_code(&mut writer, TNS_FUNC_AQ_ARRAY, seq_num, ttc_field_version);
648 writer.write_u8(0); writer.write_ub4(0); writer.write_ub4(TNS_AQ_ARRAY_FLAGS_RETURN_MESSAGE_ID);
651 writer.write_u8(1); writer.write_u8(0); writer.write_sb4(TNS_AQ_ARRAY_ENQ);
654 writer.write_u8(1); if version_gates::carries_aq_shard_id(ttc_field_version) {
656 writer.write_ub4(0xFFFF); }
658 writer.write_ub4(num_iters);
659
660 let mut flags = 0u32;
661 if enq_options.delivery_mode == TNS_AQ_MSG_BUFFERED {
662 flags |= TNS_KPD_AQ_BUFMSG;
663 }
664 writer.write_ub4(0); writer.write_u8(TNS_MSG_TYPE_ROW_HEADER);
666 writer.write_bytes_with_two_lengths(Some(queue_name))?;
667 writer.write_raw(&queue.payload_toid);
668 writer.write_ub2(TNS_AQ_MESSAGE_VERSION);
669 writer.write_ub4(flags);
670 for props in props_list {
671 let payload = props
672 .payload
673 .as_ref()
674 .ok_or(ProtocolError::TtcDecode("AQ array enqueue has no payload"))?;
675 writer.write_u8(TNS_MSG_TYPE_ROW_DATA);
676 writer.write_ub4(flags); write_msg_props(&mut writer, props, ttc_field_version)?;
678 match props.recipients.as_ref() {
679 None => writer.write_ub4(0),
680 Some(recipients) => {
681 writer.write_ub4(3 * recipients.len() as u32);
682 write_recipients(&mut writer, recipients)?;
683 }
684 }
685 writer.write_sb4(enq_options.visibility as i32);
686 writer.write_ub4(0); writer.write_sb4(0); if matches!(queue.kind, AqPayloadKind::Raw) {
689 let raw_len = match payload {
690 AqPayloadValue::Raw(bytes) => bytes.len() as u32,
691 _ => return Err(ProtocolError::TtcDecode("RAW queue requires RAW payload")),
692 };
693 writer.write_ub4(raw_len);
694 }
695 write_payload(&mut writer, payload, supports_oson_long_fnames)?;
696 }
697 writer.write_u8(TNS_MSG_TYPE_STATUS);
698 Ok(writer.into_bytes())
699}
700
701pub fn build_aq_array_deq_payload(
704 queue: &AqQueueDesc,
705 deq_options: &AqDeqOptions,
706 num_iters: u32,
707 seq_num: u8,
708 ttc_field_version: u8,
709) -> Result<Vec<u8>> {
710 let queue_name = queue.name.as_bytes();
711 let mut writer = TtcWriter::new();
712 write_aq_function_code(&mut writer, TNS_FUNC_AQ_ARRAY, seq_num, ttc_field_version);
713 writer.write_u8(1); writer.write_ub4(num_iters);
715 writer.write_ub4(TNS_AQ_ARRAY_FLAGS_RETURN_MESSAGE_ID);
716 writer.write_u8(1); writer.write_u8(1); writer.write_sb4(TNS_AQ_ARRAY_DEQ);
719 writer.write_u8(0); if version_gates::carries_aq_shard_id(ttc_field_version) {
721 writer.write_ub4(0xFFFF); }
723
724 let mut flags = 0u32;
725 match deq_options.delivery_mode {
726 TNS_AQ_MSG_BUFFERED => flags |= TNS_KPD_AQ_BUFMSG,
727 TNS_AQ_MSG_PERSISTENT_OR_BUFFERED => flags |= TNS_KPD_AQ_EITHER,
728 _ => {}
729 }
730 let consumer_name = deq_options
731 .consumer_name
732 .as_ref()
733 .filter(|name| !name.is_empty())
734 .map(|name| name.as_bytes());
735 let correlation = deq_options
736 .correlation
737 .as_ref()
738 .filter(|c| !c.is_empty())
739 .map(|c| c.as_bytes());
740 let condition = deq_options
741 .condition
742 .as_ref()
743 .filter(|c| !c.is_empty())
744 .map(|c| c.as_bytes());
745 let props = AqMsgProps::default();
746 for _ in 0..num_iters {
747 writer.write_bytes_with_two_lengths(Some(queue_name))?;
748 write_msg_props(&mut writer, &props, ttc_field_version)?;
749 writer.write_ub4(0); write_value_with_length(&mut writer, consumer_name)?;
751 writer.write_sb4(deq_options.mode);
752 writer.write_sb4(deq_options.navigation);
753 writer.write_sb4(deq_options.visibility);
754 writer.write_sb4(deq_options.wait as i32);
755 write_value_with_length(&mut writer, deq_options.msgid.as_deref())?;
756 write_value_with_length(&mut writer, correlation)?;
757 write_value_with_length(&mut writer, condition)?;
758 writer.write_ub4(0); writer.write_ub4(0); writer.write_sb4(0); writer.write_bytes_with_two_lengths(Some(&queue.payload_toid))?;
762 writer.write_ub2(TNS_AQ_MESSAGE_VERSION);
763 writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0);
766 writer.write_ub4(flags);
767 writer.write_ub4(0); writer.write_ub4(0); }
770 Ok(writer.into_bytes())
771}
772
773#[derive(Clone, Debug, Default)]
776pub struct AqArrayResult {
777 pub enq_msgids: Vec<Vec<u8>>,
779 pub deq_messages: Vec<AqDeqMessage>,
781}
782
783pub fn parse_aq_array_response(
789 payload: &[u8],
790 capabilities: ClientCapabilities,
791 operation: i32,
792 props_count: u32,
793 kind: &AqPayloadKind,
794) -> Result<AqArrayResult> {
795 parse_aq_array_response_with_limits(
796 payload,
797 capabilities,
798 operation,
799 props_count,
800 kind,
801 ProtocolLimits::DEFAULT,
802 )
803}
804
805pub fn parse_aq_array_response_with_limits(
806 payload: &[u8],
807 capabilities: ClientCapabilities,
808 operation: i32,
809 props_count: u32,
810 kind: &AqPayloadKind,
811 limits: ProtocolLimits,
812) -> Result<AqArrayResult> {
813 limits.check_batch_rows(props_count as usize)?;
814 let mut reader = TtcReader::with_limits(payload, limits)?;
815 let mut result = AqArrayResult::default();
816 let mut messages: Vec<AqDeqMessage> = Vec::new();
817 let mut enq_msgid_blob: Option<Vec<u8>> = None;
818 let mut response_num_iters: u32 = 0;
819 let mut no_msg_found = false;
820 while reader.remaining() > 0 {
821 let message_type = reader.read_u8()?;
822 match message_type {
823 0 => {}
824 TNS_MSG_TYPE_PARAMETER => {
825 let num_iters = reader.read_ub4()?;
826 reader.limits().check_batch_rows(num_iters as usize)?;
827 response_num_iters = num_iters;
828 for i in 0..num_iters {
829 let mut message = AqDeqMessage::default();
830 let props_len = reader.read_ub2()?;
831 if props_len > 0 {
832 reader.read_u8()?; process_msg_props(
834 &mut reader,
835 &mut message,
836 capabilities.ttc_field_version,
837 )?;
838 }
839 process_recipients(&mut reader)?;
840 let payload_len = reader.read_ub2()?;
841 if payload_len > 0 {
842 message.payload = process_payload(&mut reader, kind)?;
843 }
844 let msgid = reader.read_bytes_with_length()?.unwrap_or_default();
845 if operation == TNS_AQ_ARRAY_ENQ {
846 enq_msgid_blob = Some(msgid);
847 } else {
848 message.msgid = Some(msgid);
849 }
850 let ext_len = reader.read_ub2()?;
851 if ext_len > 0 {
852 return Err(ProtocolError::UnsupportedFeature("AQ array extensions"));
853 }
854 let _output_ack = reader.read_ub2()?;
855 if operation != TNS_AQ_ARRAY_ENQ {
856 let _ = i;
857 messages.push(message);
858 }
859 }
860 if operation == TNS_AQ_ARRAY_ENQ {
861 response_num_iters = reader.read_ub4()?;
862 reader
863 .limits()
864 .check_batch_rows(response_num_iters as usize)?;
865 }
866 }
867 TNS_MSG_TYPE_STATUS => {
868 let _call_status = reader.read_ub4()?;
869 let _seq = reader.read_ub2()?;
870 }
871 TNS_MSG_TYPE_SERVER_SIDE_PIGGYBACK => {
872 let _ = skip_server_side_piggyback(&mut reader)?;
873 }
874 TNS_MSG_TYPE_END_OF_RESPONSE => break,
875 TNS_MSG_TYPE_ERROR => {
876 let info = parse_server_error_info(&mut reader, capabilities.ttc_field_version)?;
877 if info.number == TNS_ERR_NO_MESSAGES_FOUND as u32 {
878 no_msg_found = true;
879 } else if info.number != 0 {
880 return Err(ProtocolError::ServerErrorInfo(Box::new(
881 info.into_details(),
882 )));
883 }
884 }
885 _ => {
886 return Err(ProtocolError::UnknownMessageType {
887 message_type,
888 position: reader.position().saturating_sub(1),
889 })
890 }
891 }
892 }
893 if operation == TNS_AQ_ARRAY_ENQ {
894 if let Some(blob) = enq_msgid_blob {
895 let count = props_count as usize;
896 result.enq_msgids = (0..count)
897 .map(|j| {
898 let start = j * 16;
899 let end = start + 16;
900 blob.get(start..end).map(<[u8]>::to_vec).unwrap_or_default()
901 })
902 .collect();
903 }
904 } else if no_msg_found {
905 result.deq_messages = Vec::new();
906 } else {
907 let keep = response_num_iters as usize;
908 messages.truncate(keep);
909 result.deq_messages = messages;
910 }
911 Ok(result)
912}
913
914fn process_msg_props(
919 reader: &mut TtcReader<'_>,
920 message: &mut AqDeqMessage,
921 ttc_field_version: u8,
922) -> Result<()> {
923 message.priority = reader.read_sb4()?;
924 message.delay = reader.read_sb4()?;
925 message.expiration = reader.read_sb4()?;
926 message.correlation = reader.read_string_with_length()?;
927 message.num_attempts = reader.read_sb4()?;
928 message.exception_queue = reader.read_string_with_length()?;
929 message.state = reader.read_sb4()?;
930 message.enq_time = process_date(reader)?;
931 let _enq_txn_id = reader.read_bytes_with_length()?;
932 process_extensions(reader)?;
933 let user_props = reader.read_ub4()?;
934 if user_props > 0 {
935 return Err(ProtocolError::UnsupportedFeature("AQ user properties"));
936 }
937 let _csn = reader.read_ub4()?;
938 let _dsn = reader.read_ub4()?;
939 let flags = reader.read_ub4()?;
940 message.delivery_mode = if flags == TNS_KPD_AQ_BUFMSG {
941 TNS_AQ_MSG_BUFFERED
942 } else {
943 TNS_AQ_MSG_PERSISTENT
944 };
945 if version_gates::carries_aq_shard_id(ttc_field_version) {
946 let _shard = reader.read_ub4()?;
947 }
948 Ok(())
949}
950
951fn process_date(reader: &mut TtcReader<'_>) -> Result<Option<QueryValue>> {
955 let num_bytes = reader.read_ub4()?;
956 if num_bytes == 0 {
957 return Ok(None);
958 }
959 let len = usize::from(reader.read_u8()?);
960 if len == 0 {
961 return Ok(None);
962 }
963 let bytes = reader.read_raw(len)?;
964 Ok(Some(decode_datetime_value(bytes)?))
965}
966
967fn process_extensions(reader: &mut TtcReader<'_>) -> Result<()> {
968 let num_extensions = reader.read_ub4()?;
969 if num_extensions > 0 {
970 reader.read_u8()?; for _ in 0..num_extensions {
972 let _text = reader.read_bytes_with_length()?;
973 let _binary = reader.read_bytes_with_length()?;
974 let _keyword = reader.read_ub2()?;
975 }
976 }
977 Ok(())
978}
979
980fn process_recipients(reader: &mut TtcReader<'_>) -> Result<()> {
981 let count = reader.read_ub4()?;
982 if count > 0 {
983 return Err(ProtocolError::UnsupportedFeature(
984 "AQ recipients on dequeue",
985 ));
986 }
987 Ok(())
988}
989
990fn process_msg_id(reader: &mut TtcReader<'_>) -> Result<Vec<u8>> {
991 Ok(reader.read_raw(TNS_AQ_MESSAGE_ID_LENGTH)?.to_vec())
992}
993
994fn process_payload(
996 reader: &mut TtcReader<'_>,
997 kind: &AqPayloadKind,
998) -> Result<Option<AqDeqPayload>> {
999 if matches!(kind, AqPayloadKind::Object) {
1000 let _toid = reader.read_bytes_with_length()?;
1004 let _oid = reader.read_bytes_with_length()?;
1005 let _snapshot = reader.read_bytes_with_length()?;
1006 let _version = reader.read_ub2()?;
1007 let image_length = reader.read_ub4()?;
1008 reader
1009 .limits()
1010 .check_response_bytes(image_length as usize)?;
1011 let _flags = reader.read_ub2()?;
1012 if image_length == 0 {
1013 return Ok(None);
1014 }
1015 let image = reader
1016 .read_bytes()?
1017 .ok_or(ProtocolError::TtcDecode("AQ object payload missing"))?;
1018 if image.len() != image_length as usize {
1019 return Err(ProtocolError::TtcDecode(
1020 "AQ object payload length differs from declared image length",
1021 ));
1022 }
1023 return Ok(Some(AqDeqPayload::Object(image)));
1024 }
1025 let _toid = reader.read_bytes_with_length()?;
1027 let _oid = reader.read_bytes_with_length()?;
1028 let _snapshot = reader.read_bytes_with_length()?;
1029 let _version = reader.read_ub2()?;
1030 let image_length = reader.read_ub4()? as usize;
1031 reader.limits().check_response_bytes(image_length)?;
1032 let _flags = reader.read_ub2()?;
1033 if image_length > 0 {
1034 if image_length < 4 {
1036 return Err(ProtocolError::TtcDecode(
1037 "AQ payload image shorter than header",
1038 ));
1039 }
1040 let raw = reader
1041 .read_bytes()?
1042 .ok_or(ProtocolError::TtcDecode("AQ payload missing"))?;
1043 if raw.len() != image_length {
1044 return Err(ProtocolError::TtcDecode(
1045 "AQ payload length differs from declared image length",
1046 ));
1047 }
1048 let end = image_length;
1049 let start = 4.min(end);
1050 let payload = raw.get(start..end).unwrap_or_default().to_vec();
1051 if matches!(kind, AqPayloadKind::Json) {
1052 let value = decode_oson_with_limits(&payload, reader.limits())?;
1053 return Ok(Some(AqDeqPayload::Json(value)));
1054 }
1055 return Ok(Some(AqDeqPayload::Raw(payload)));
1056 }
1057 if matches!(kind, AqPayloadKind::Raw) {
1058 return Ok(Some(AqDeqPayload::Raw(Vec::new())));
1059 }
1060 Ok(None)
1061}
1062
1063#[cfg(test)]
1064mod tests {
1065 use super::*;
1066
1067 const FV: u8 = 24;
1070
1071 fn caps() -> ClientCapabilities {
1072 ClientCapabilities {
1073 ttc_field_version: FV,
1074 max_string_size: 32767,
1075 charset_id: 873,
1076 }
1077 }
1078
1079 const GOLDEN_RAW_ENQ: &[u8] = &[
1084 0x03, 0x79, 0x04, 0x00, 0x01, 0x01, 0x0e, 0x01, 0x02, 0x00, 0x81, 0x01, 0x01, 0x05, 0x05,
1085 0x43, 0x4f, 0x52, 0x52, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x0e, 0x00, 0x00,
1086 0x01, 0x40, 0x00, 0x00, 0x01, 0x41, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x42, 0x00, 0x00,
1087 0x01, 0x45, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x01, 0x02,
1088 0x00, 0x00, 0x00, 0x01, 0x01, 0x10, 0x01, 0x01, 0x00, 0x01, 0x01, 0x11, 0x01, 0x01, 0x10,
1089 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1090 0x00, 0x00, 0x00, 0x00, 0x0e, 0x54, 0x45, 0x53, 0x54, 0x5f, 0x52, 0x41, 0x57, 0x5f, 0x51,
1091 0x55, 0x45, 0x55, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1092 0x00, 0x00, 0x00, 0x00, 0x17, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x72, 0x61, 0x77,
1093 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x31,
1094 ];
1095
1096 const GOLDEN_RAW_DEQ: &[u8] = &[
1099 0x03, 0x7a, 0x06, 0x00, 0x01, 0x01, 0x0e, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x03,
1100 0x01, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01,
1101 0x01, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x0e,
1102 0x54, 0x45, 0x53, 0x54, 0x5f, 0x52, 0x41, 0x57, 0x5f, 0x51, 0x55, 0x45, 0x55, 0x45, 0x00,
1103 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17,
1104 ];
1105
1106 #[test]
1107 fn raw_enqueue_request_matches_golden() {
1108 let queue = AqQueueDesc::new("TEST_RAW_QUEUE".to_string(), AqPayloadKind::Raw, None);
1109 let props = AqMsgProps {
1110 priority: 2,
1111 correlation: Some("CORR1".to_string()),
1112 payload: Some(AqPayloadValue::Raw(b"sample raw data 1".to_vec())),
1113 ..AqMsgProps::default()
1114 };
1115 let bytes = build_aq_enq_payload(&queue, &props, &AqEnqOptions::default(), 4, FV, false)
1116 .expect("build enqueue");
1117 assert_eq!(bytes, GOLDEN_RAW_ENQ);
1118 }
1119
1120 #[test]
1121 fn raw_dequeue_request_matches_golden() {
1122 let queue = AqQueueDesc::new("TEST_RAW_QUEUE".to_string(), AqPayloadKind::Raw, None);
1123 let deq = AqDeqOptions {
1124 wait: 0,
1125 navigation: 1,
1126 ..AqDeqOptions::default()
1127 };
1128 let bytes = build_aq_deq_payload(&queue, &deq, 6, FV).expect("build dequeue");
1129 assert_eq!(bytes, GOLDEN_RAW_DEQ);
1130 }
1131
1132 #[test]
1133 fn empty_queue_dequeue_yields_no_message() {
1134 let caps = caps();
1138 let res = parse_aq_deq_response(&[], caps, &AqPayloadKind::Raw).expect("parse");
1139 assert!(res.message.is_none());
1140 }
1141
1142 fn deq_response_with_raw_image(image_length: u32, raw_image: &[u8]) -> Vec<u8> {
1143 let mut writer = TtcWriter::new();
1144 writer.write_u8(TNS_MSG_TYPE_PARAMETER);
1145 writer.write_ub4(1);
1146 write_msg_props(&mut writer, &AqMsgProps::default(), FV).expect("write message props");
1147 writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0); writer.write_ub2(0); writer.write_ub4(image_length);
1153 writer.write_ub2(0); writer
1155 .write_bytes_with_length(raw_image)
1156 .expect("write raw image field");
1157 writer.write_raw(&[0u8; TNS_AQ_MESSAGE_ID_LENGTH]);
1158 writer.write_u8(TNS_MSG_TYPE_END_OF_RESPONSE);
1159 writer.into_bytes()
1160 }
1161
1162 fn deq_response_with_object_image(image_length: u32, object_image: &[u8]) -> Vec<u8> {
1163 let mut writer = TtcWriter::new();
1164 writer.write_u8(TNS_MSG_TYPE_PARAMETER);
1165 writer.write_ub4(1);
1166 write_msg_props(&mut writer, &AqMsgProps::default(), FV).expect("write message props");
1167 writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0); writer.write_ub2(0); writer.write_ub4(image_length);
1173 writer.write_ub2(0); writer
1175 .write_bytes_with_length(object_image)
1176 .expect("write object image field");
1177 writer.write_raw(&[0u8; TNS_AQ_MESSAGE_ID_LENGTH]);
1178 writer.write_u8(TNS_MSG_TYPE_END_OF_RESPONSE);
1179 writer.into_bytes()
1180 }
1181
1182 #[test]
1183 fn raw_dequeue_rejects_declared_image_length_shortfall() {
1184 let response = deq_response_with_raw_image(8, &[0, 0, 0, 0, b'A', b'B']);
1185 let err = parse_aq_deq_response(&response, caps(), &AqPayloadKind::Raw)
1186 .expect_err("short RAW image must fail");
1187 assert!(matches!(err, ProtocolError::TtcDecode(_)), "got {err:?}");
1188 }
1189
1190 #[test]
1191 fn json_dequeue_rejects_declared_image_length_shortfall() {
1192 let response = deq_response_with_raw_image(8, &[0, 0, 0, 0, b'A', b'B']);
1193 let err = parse_aq_deq_response(&response, caps(), &AqPayloadKind::Json)
1194 .expect_err("short JSON image must fail");
1195 assert!(matches!(err, ProtocolError::TtcDecode(_)), "got {err:?}");
1196 }
1197
1198 #[test]
1199 fn raw_dequeue_rejects_nonzero_image_length_below_header() {
1200 let response = deq_response_with_raw_image(3, &[0, 0, 0]);
1201 let err = parse_aq_deq_response(&response, caps(), &AqPayloadKind::Raw)
1202 .expect_err("nonzero RAW image shorter than header must fail");
1203 assert!(matches!(err, ProtocolError::TtcDecode(_)), "got {err:?}");
1204 }
1205
1206 #[test]
1207 fn raw_dequeue_rejects_declared_image_length_trailing_bytes() {
1208 let response = deq_response_with_raw_image(6, &[0, 0, 0, 0, b'A', b'B', b'X']);
1209 let err = parse_aq_deq_response(&response, caps(), &AqPayloadKind::Raw)
1210 .expect_err("trailing RAW image bytes must fail");
1211 assert!(matches!(err, ProtocolError::TtcDecode(_)), "got {err:?}");
1212 }
1213
1214 #[test]
1215 fn raw_dequeue_accepts_exact_declared_image_length() {
1216 let response = deq_response_with_raw_image(6, &[0, 0, 0, 0, b'A', b'B']);
1217 let res = parse_aq_deq_response(&response, caps(), &AqPayloadKind::Raw)
1218 .expect("exact RAW image should parse");
1219 let message = res.message.expect("message present");
1220 match message.payload {
1221 Some(AqDeqPayload::Raw(payload)) => assert_eq!(payload, vec![b'A', b'B']),
1222 other => panic!("unexpected payload {other:?}"),
1223 }
1224 }
1225
1226 #[test]
1227 fn object_dequeue_rejects_declared_image_length_mismatch() {
1228 let response = deq_response_with_object_image(2, b"ABC");
1229 let err = parse_aq_deq_response(&response, caps(), &AqPayloadKind::Object)
1230 .expect_err("mismatched object image length must fail");
1231 assert!(matches!(err, ProtocolError::TtcDecode(_)), "got {err:?}");
1232 }
1233
1234 #[test]
1235 fn object_dequeue_accepts_exact_declared_image_length() {
1236 let response = deq_response_with_object_image(3, b"ABC");
1237 let res = parse_aq_deq_response(&response, caps(), &AqPayloadKind::Object)
1238 .expect("exact object image should parse");
1239 let message = res.message.expect("message present");
1240 match message.payload {
1241 Some(AqDeqPayload::Object(payload)) => assert_eq!(payload, b"ABC"),
1242 other => panic!("unexpected payload {other:?}"),
1243 }
1244 }
1245
1246 #[test]
1247 fn aq_array_response_respects_protocol_batch_limit() {
1248 let limits = ProtocolLimits {
1249 max_batch_rows: 1,
1250 ..ProtocolLimits::DEFAULT
1251 };
1252 let err = parse_aq_array_response_with_limits(
1253 &[],
1254 caps(),
1255 TNS_AQ_ARRAY_ENQ,
1256 2,
1257 &AqPayloadKind::Raw,
1258 limits,
1259 )
1260 .expect_err("client-side AQ batch count above policy must fail");
1261 assert!(
1262 matches!(
1263 err,
1264 ProtocolError::ResourceLimit {
1265 limit: "batch_rows",
1266 observed: 2,
1267 maximum: 1,
1268 }
1269 ),
1270 "got {err:?}"
1271 );
1272 }
1273
1274 const GOLDEN_JSON_ENQ: &[u8] = &[
1278 0x03, 0x79, 0x04, 0x00, 0x01, 0x01, 0x0f, 0x00, 0x00, 0x81, 0x01, 0x00, 0x00, 0x00, 0x00,
1279 0x00, 0x00, 0x01, 0x04, 0x0e, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0x41, 0x00, 0x01,
1280 0x01, 0x01, 0x00, 0x01, 0x42, 0x00, 0x00, 0x01, 0x45, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff,
1281 0xff, 0xff, 0xff, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x10, 0x01, 0x01,
1282 0x00, 0x00, 0x00, 0x01, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1283 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x54, 0x45, 0x53, 0x54,
1284 0x5f, 0x4a, 0x53, 0x4f, 0x4e, 0x5f, 0x51, 0x55, 0x45, 0x55, 0x45, 0x00, 0x00, 0x00, 0x00,
1285 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x01, 0x28, 0x00,
1286 0x26, 0x00, 0x04, 0x61, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1287 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1288 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0xff, 0x4a, 0x5a, 0x01, 0x21,
1289 0x02, 0x03, 0x00, 0x0e, 0x00, 0x1f, 0x00, 0x00, 0x42, 0x9c, 0xe6, 0x00, 0x09, 0x00, 0x05,
1290 0x00, 0x00, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x03, 0x61, 0x67, 0x65, 0x04, 0x63, 0x69, 0x74,
1291 0x79, 0xa4, 0x03, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x17, 0x00,
1292 0x00, 0x00, 0x1b, 0x33, 0x04, 0x4a, 0x6f, 0x68, 0x6e, 0x34, 0x02, 0xc1, 0x1f, 0x33, 0x02,
1293 0x4e, 0x59,
1294 ];
1295
1296 #[test]
1297 fn json_enqueue_request_matches_golden() {
1298 let queue = AqQueueDesc::new("TEST_JSON_QUEUE".to_string(), AqPayloadKind::Json, None);
1299 let value = OsonValue::Object(vec![
1301 ("name".to_string(), OsonValue::String("John".to_string())),
1302 ("age".to_string(), OsonValue::Number("30".to_string())),
1303 ("city".to_string(), OsonValue::String("NY".to_string())),
1304 ]);
1305 let props = AqMsgProps {
1306 payload: Some(AqPayloadValue::Json(value)),
1307 ..AqMsgProps::default()
1308 };
1309 let bytes = build_aq_enq_payload(&queue, &props, &AqEnqOptions::default(), 4, FV, true)
1311 .expect("build json enqueue");
1312 assert_eq!(bytes, GOLDEN_JSON_ENQ);
1313 }
1314
1315 fn assert_single_insertion(lo: &[u8], hi: &[u8], label: &str) {
1327 assert!(
1328 hi.len() > lo.len(),
1329 "{label}: gated field must add bytes at/above the boundary"
1330 );
1331 let prefix = lo.iter().zip(hi).take_while(|(a, b)| a == b).count();
1332 let suffix = lo[prefix..]
1333 .iter()
1334 .rev()
1335 .zip(hi[prefix..].iter().rev())
1336 .take_while(|(a, b)| a == b)
1337 .count();
1338 assert_eq!(
1339 prefix + suffix,
1340 lo.len(),
1341 "{label}: below-boundary bytes must equal above-boundary bytes minus one contiguous inserted block"
1342 );
1343 }
1344
1345 fn caps_fv(fv: u8) -> ClientCapabilities {
1346 ClientCapabilities {
1347 ttc_field_version: fv,
1348 max_string_size: 32767,
1349 charset_id: 873,
1350 }
1351 }
1352
1353 #[test]
1355 fn aq_enq_gates_json_payload_pointer_on_20_1() {
1356 let queue = AqQueueDesc::new("Q".to_string(), AqPayloadKind::Raw, None);
1357 let props = AqMsgProps {
1358 payload: Some(AqPayloadValue::Raw(b"x".to_vec())),
1359 ..AqMsgProps::default()
1360 };
1361 let build = |fv| {
1362 build_aq_enq_payload(&queue, &props, &AqEnqOptions::default(), 1, fv, false)
1363 .expect("enq payload")
1364 };
1365 assert_single_insertion(
1366 &build(TNS_CCAP_FIELD_VERSION_20_1 - 1),
1367 &build(TNS_CCAP_FIELD_VERSION_20_1),
1368 "aq enq JSON-payload pointer (20.1)",
1369 );
1370 }
1371
1372 #[test]
1374 fn aq_deq_gates_json_payload_flag_on_20_1() {
1375 let queue = AqQueueDesc::new("Q".to_string(), AqPayloadKind::Raw, None);
1376 let build = |fv| {
1377 build_aq_deq_payload(&queue, &AqDeqOptions::default(), 1, fv).expect("deq payload")
1378 };
1379 assert_single_insertion(
1380 &build(TNS_CCAP_FIELD_VERSION_20_1 - 1),
1381 &build(TNS_CCAP_FIELD_VERSION_20_1),
1382 "aq deq JSON-payload flag (20.1)",
1383 );
1384 }
1385
1386 #[test]
1389 fn aq_deq_gates_shard_id_on_21_1() {
1390 let queue = AqQueueDesc::new("Q".to_string(), AqPayloadKind::Raw, None);
1391 let build = |fv| {
1392 build_aq_deq_payload(&queue, &AqDeqOptions::default(), 1, fv).expect("deq payload")
1393 };
1394 assert_single_insertion(
1395 &build(TNS_CCAP_FIELD_VERSION_21_1 - 1),
1396 &build(TNS_CCAP_FIELD_VERSION_21_1),
1397 "aq deq shard id (21.1)",
1398 );
1399 }
1400
1401 #[test]
1406 fn aq_array_gates_shard_id_on_21_1() {
1407 let queue = AqQueueDesc::new("Q".to_string(), AqPayloadKind::Raw, None);
1408 let build = |fv| {
1409 build_aq_array_enq_payload(&queue, &[], &AqEnqOptions::default(), 1, fv, false)
1410 .expect("array enq payload")
1411 };
1412 assert_single_insertion(
1413 &build(TNS_CCAP_FIELD_VERSION_21_1 - 1),
1414 &build(TNS_CCAP_FIELD_VERSION_21_1),
1415 "aq array shard id (21.1)",
1416 );
1417 }
1418
1419 #[test]
1421 fn aq_msg_props_gates_shard_id_on_21_1() {
1422 let build = |fv| {
1423 let mut w = TtcWriter::new();
1424 write_msg_props(&mut w, &AqMsgProps::default(), fv).expect("msg props");
1425 w.into_bytes()
1426 };
1427 assert_single_insertion(
1428 &build(TNS_CCAP_FIELD_VERSION_21_1 - 1),
1429 &build(TNS_CCAP_FIELD_VERSION_21_1),
1430 "aq message-props shard id (21.1)",
1431 );
1432 }
1433
1434 #[test]
1439 fn aq_deq_msg_props_read_gates_shard_on_21_1() {
1440 let response_at = |fv: u8| {
1441 let mut writer = TtcWriter::new();
1442 writer.write_u8(TNS_MSG_TYPE_PARAMETER);
1443 writer.write_ub4(1);
1444 write_msg_props(&mut writer, &AqMsgProps::default(), fv).expect("write message props");
1445 writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0); writer.write_ub2(0); writer.write_ub4(6); writer.write_ub2(0); writer
1453 .write_bytes_with_length(&[0, 0, 0, 0, b'A', b'B'])
1454 .expect("write raw image field");
1455 writer.write_raw(&[0u8; TNS_AQ_MESSAGE_ID_LENGTH]);
1456 writer.write_u8(TNS_MSG_TYPE_END_OF_RESPONSE);
1457 writer.into_bytes()
1458 };
1459 let lo = TNS_CCAP_FIELD_VERSION_21_1 - 1;
1460 let hi = TNS_CCAP_FIELD_VERSION_21_1;
1461
1462 let raw_of = |resp: AqDeqResult| match resp.message.and_then(|m| m.payload) {
1464 Some(AqDeqPayload::Raw(bytes)) => Some(bytes),
1465 _ => None,
1466 };
1467 let matched_hi = raw_of(
1468 parse_aq_deq_response(&response_at(hi), caps_fv(hi), &AqPayloadKind::Raw)
1469 .expect("matched hi parse"),
1470 );
1471 let matched_lo = raw_of(
1472 parse_aq_deq_response(&response_at(lo), caps_fv(lo), &AqPayloadKind::Raw)
1473 .expect("matched lo parse"),
1474 );
1475 assert_eq!(
1476 matched_hi, matched_lo,
1477 "both bands decode the same RAW payload"
1478 );
1479 assert_eq!(matched_hi.as_deref(), Some(&b"AB"[..]));
1480
1481 let mismatched = parse_aq_deq_response(&response_at(hi), caps_fv(lo), &AqPayloadKind::Raw);
1486 let diverged = match mismatched {
1487 Err(_) => true,
1488 Ok(resp) => raw_of(resp) != matched_hi,
1489 };
1490 assert!(
1491 diverged,
1492 "read side must consume the 21.1 shard: skipping it changes the decode"
1493 );
1494 }
1495}