1use crate::charset::Charset;
30use crate::connection::Connection;
31use crate::error::{Error, Result};
32use crate::transaction::Transaction;
33use crate::value::Value;
34use crate::wire::consts::{blr, op, sdl};
35use crate::wire::response::{read_op, read_response, read_response_body};
36use crate::wire::stream::{FbStream, op_name, op_packet};
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub struct Dimension {
41 pub lower: i32,
43 pub upper: i32,
45}
46
47impl Dimension {
48 pub fn len(&self) -> usize {
50 (self.upper - self.lower + 1).max(0) as usize
51 }
52
53 pub fn is_empty(&self) -> bool {
55 self.upper < self.lower
56 }
57}
58
59#[derive(Debug, Clone)]
63pub struct ArrayDesc {
64 pub relation: String,
66 pub field: String,
68 pub blr_type: u8,
70 pub sub_type: i32,
72 pub scale: i32,
74 pub length: u16,
76 pub dimensions: Vec<Dimension>,
78}
79
80impl ArrayDesc {
81 pub fn element_count(&self) -> usize {
83 self.dimensions.iter().map(Dimension::len).product()
84 }
85
86 fn element_stride(&self) -> usize {
89 match self.blr_type {
90 blr::VARYING => self.length as usize + 2,
91 blr::TEXT => self.length as usize,
92 blr::SHORT => 2,
93 blr::LONG | blr::FLOAT | blr::SQL_DATE | blr::SQL_TIME => 4,
94 blr::INT64 | blr::DOUBLE | blr::D_FLOAT | blr::TIMESTAMP | blr::QUAD | blr::DEC64 => 8,
95 blr::INT128 | blr::DEC128 => 16,
96 blr::BOOL => 1,
97 _ => self.length.max(1) as usize,
99 }
100 }
101
102 fn slice_len(&self) -> usize {
104 self.element_count() * self.element_stride()
105 }
106
107 pub fn to_sdl(&self) -> Vec<u8> {
111 let mut s = Vec::with_capacity(32);
112 s.push(sdl::VERSION1);
113 s.push(sdl::STRUCT);
115 s.push(1);
116 s.push(self.blr_type);
117 match self.blr_type {
118 blr::TEXT | blr::VARYING => s.extend_from_slice(&self.length.to_le_bytes()),
120 blr::SHORT | blr::LONG | blr::INT64 | blr::INT128 | blr::QUAD => {
122 s.push(self.scale as i8 as u8)
123 }
124 _ => {}
126 }
127 s.push(sdl::RELATION);
129 s.push(self.relation.len() as u8);
130 s.extend_from_slice(self.relation.as_bytes());
131 s.push(sdl::FIELD);
132 s.push(self.field.len() as u8);
133 s.extend_from_slice(self.field.as_bytes());
134 for (i, dim) in self.dimensions.iter().enumerate() {
137 if dim.lower == 1 {
138 s.push(sdl::DO1);
139 s.push(i as u8);
140 put_sdl_literal(&mut s, dim.upper);
141 } else {
142 s.push(sdl::DO2);
143 s.push(i as u8);
144 put_sdl_literal(&mut s, dim.lower);
145 put_sdl_literal(&mut s, dim.upper);
146 }
147 }
148 s.push(sdl::ELEMENT);
150 s.push(1);
151 s.push(sdl::SCALAR);
152 s.push(0); s.push(self.dimensions.len() as u8); for i in 0..self.dimensions.len() {
155 s.push(sdl::VARIABLE);
156 s.push(i as u8);
157 }
158 s.push(sdl::EOC);
159 s
160 }
161}
162
163fn put_sdl_literal(s: &mut Vec<u8>, v: i32) {
165 if (i8::MIN as i32..=i8::MAX as i32).contains(&v) {
166 s.push(sdl::TINY_INTEGER);
167 s.push(v as i8 as u8);
168 } else if (i16::MIN as i32..=i16::MAX as i32).contains(&v) {
169 s.push(sdl::SHORT_INTEGER);
170 s.extend_from_slice(&(v as i16).to_le_bytes());
171 } else {
172 s.push(sdl::LONG_INTEGER);
173 s.extend_from_slice(&v.to_le_bytes());
174 }
175}
176
177impl Connection {
178 pub fn array_desc(
183 &mut self,
184 tx: &Transaction,
185 relation: &str,
186 field: &str,
187 ) -> Result<ArrayDesc> {
188 let mut stmt = self.prepare(
191 tx,
192 "SELECT f.RDB$FIELD_TYPE, f.RDB$FIELD_SUB_TYPE, f.RDB$FIELD_SCALE, \
193 f.RDB$FIELD_LENGTH, f.RDB$DIMENSIONS, f.RDB$FIELD_NAME \
194 FROM RDB$RELATION_FIELDS rf \
195 JOIN RDB$FIELDS f ON f.RDB$FIELD_NAME = rf.RDB$FIELD_SOURCE \
196 WHERE rf.RDB$RELATION_NAME = ? AND rf.RDB$FIELD_NAME = ?",
197 )?;
198 stmt.execute(
199 self,
200 tx,
201 &[Value::Text(relation.into()), Value::Text(field.into())],
202 )?;
203 let rows = stmt.fetch_all(self)?;
204 stmt.drop_statement(self)?;
205
206 let row = rows.into_iter().next().ok_or_else(|| {
207 Error::protocol(format!(
208 "coluna '{relation}.{field}' não encontrada no catálogo"
209 ))
210 })?;
211 let blr_type = val_i64(&row[0]).unwrap_or(0) as u8;
212 let sub_type = val_i64(&row[1]).unwrap_or(0) as i32;
213 let scale = val_i64(&row[2]).unwrap_or(0) as i32;
214 let length = val_i64(&row[3]).unwrap_or(0) as u16;
215 let dims = val_i64(&row[4]).unwrap_or(0);
216 let source = row[5].as_str().unwrap_or("").trim_end().to_string();
217 if dims <= 0 {
218 return Err(Error::protocol(format!(
219 "'{relation}.{field}' não é uma coluna ARRAY"
220 )));
221 }
222
223 let mut stmt = self.prepare(
225 tx,
226 "SELECT fd.RDB$LOWER_BOUND, fd.RDB$UPPER_BOUND \
227 FROM RDB$FIELD_DIMENSIONS fd WHERE fd.RDB$FIELD_NAME = ? \
228 ORDER BY fd.RDB$DIMENSION",
229 )?;
230 stmt.execute(self, tx, &[Value::Text(source)])?;
231 let dim_rows = stmt.fetch_all(self)?;
232 stmt.drop_statement(self)?;
233
234 let dimensions: Vec<Dimension> = dim_rows
235 .iter()
236 .map(|r| Dimension {
237 lower: val_i64(&r[0]).unwrap_or(1) as i32,
238 upper: val_i64(&r[1]).unwrap_or(0) as i32,
239 })
240 .collect();
241
242 Ok(ArrayDesc {
243 relation: relation.into(),
244 field: field.into(),
245 blr_type,
246 sub_type,
247 scale,
248 length,
249 dimensions,
250 })
251 }
252
253 pub fn read_array(
257 &mut self,
258 tx: &Transaction,
259 array_id: u64,
260 desc: &ArrayDesc,
261 ) -> Result<Vec<Value>> {
262 let sdl = desc.to_sdl();
263 let count = desc.element_count();
264
265 let mut w = op_packet(op::GET_SLICE);
266 w.put_i32(tx.handle());
267 w.put_i64(array_id as i64); w.put_i32(desc.slice_len() as i32); w.put_bytes(&sdl);
270 w.put_bytes(&[]); w.put_i32(0); self.io().send(&w)?;
273
274 let code = read_op(self.io())?;
277 if code == op::RESPONSE {
278 read_response_body(self.io())?.into_result()?;
279 return Err(Error::protocol("op_get_slice falhou sem status de erro"));
280 }
281 if code != op::SLICE {
282 return Err(Error::protocol(format!(
283 "esperava op_slice, recebi {} ({code})",
284 op_name(code)
285 )));
286 }
287 let _slr_length = self.io().read_i32()?; let _xdr_length = self.io().read_i32()?; let charset = self.charset();
291 let mut out = Vec::with_capacity(count);
292 for _ in 0..count {
293 out.push(decode_element(self.io(), desc, charset)?);
294 }
295 Ok(out)
296 }
297
298 pub fn write_array(
302 &mut self,
303 tx: &Transaction,
304 desc: &ArrayDesc,
305 values: &[Value],
306 ) -> Result<u64> {
307 let count = desc.element_count();
308 if values.len() != count {
309 return Err(Error::protocol(format!(
310 "o array espera {count} elementos, recebeu {}",
311 values.len()
312 )));
313 }
314 let sdl = desc.to_sdl();
315 let charset = self.charset();
316
317 let mut data = Vec::new();
319 for v in values {
320 encode_element(&mut data, desc, v, charset)?;
321 }
322
323 let mut w = op_packet(op::PUT_SLICE);
324 w.put_i32(tx.handle());
325 w.put_i64(0); w.put_i32(desc.slice_len() as i32); w.put_bytes(&sdl);
328 w.put_bytes(&[]); w.put_i32(desc.slice_len() as i32); w.put_raw(&data);
331 w.align();
332 self.io().send(&w)?;
333
334 Ok(read_response(self.io())?.blob_id)
336 }
337}
338
339fn val_i64(v: &Value) -> Option<i64> {
341 v.as_i64()
342}
343
344fn decode_element(stream: &mut FbStream, desc: &ArrayDesc, charset: Charset) -> Result<Value> {
346 Ok(match desc.blr_type {
347 blr::SHORT => Value::Short(stream.read_i32()? as i16),
348 blr::LONG => Value::Int(stream.read_i32()?),
349 blr::INT64 => Value::BigInt(stream.read_i64()?),
350 blr::INT128 => {
351 let b = stream.read_raw(16)?;
352 Value::Int128(i128::from_be_bytes(b.try_into().unwrap()))
353 }
354 blr::FLOAT => Value::Float(f32::from_bits(stream.read_i32()? as u32)),
355 blr::DOUBLE | blr::D_FLOAT => Value::Double(stream.read_f64()?),
356 blr::SQL_DATE => Value::Date(stream.read_i32()?),
357 blr::SQL_TIME => Value::Time(stream.read_i32()? as u32),
358 blr::TIMESTAMP => {
359 let date = stream.read_i32()?;
360 let time = stream.read_i32()? as u32;
361 Value::Timestamp(date, time)
362 }
363 blr::BOOL => {
364 let b = stream.read_raw(1)?;
365 stream.read_pad(1)?;
366 Value::Bool(b[0] != 0)
367 }
368 blr::DEC64 => {
369 let b = stream.read_raw(8)?;
370 Value::DecFloat(crate::decfloat::DecFloat::from_decimal64(
371 b.try_into().unwrap(),
372 ))
373 }
374 blr::DEC128 => {
375 let b = stream.read_raw(16)?;
376 Value::DecFloat(crate::decfloat::DecFloat::from_decimal128(
377 b.try_into().unwrap(),
378 ))
379 }
380 blr::TEXT => {
381 let n = desc.length as usize;
382 let raw = stream.read_raw(n)?;
383 stream.read_pad(n)?;
384 text_or_bytes(desc, raw, charset, true)
385 }
386 blr::VARYING => {
387 let raw = stream.read_bytes()?; text_or_bytes(desc, raw, charset, false)
389 }
390 other => {
391 let _ = other;
393 Value::Bytes(stream.read_raw(desc.element_stride())?)
394 }
395 })
396}
397
398fn encode_element(
400 out: &mut Vec<u8>,
401 desc: &ArrayDesc,
402 val: &Value,
403 charset: Charset,
404) -> Result<()> {
405 let mismatch = || {
406 Error::protocol(format!(
407 "valor não cabe no tipo de elemento BLR {}",
408 desc.blr_type
409 ))
410 };
411 match desc.blr_type {
412 blr::SHORT => put_i32_be(out, i32::from(val.as_i64().ok_or_else(mismatch)? as i16)),
413 blr::LONG => put_i32_be(out, val.as_i64().ok_or_else(mismatch)? as i32),
414 blr::INT64 => out.extend_from_slice(&val.as_i64().ok_or_else(mismatch)?.to_be_bytes()),
415 blr::INT128 => match val {
416 Value::Int128(v) => out.extend_from_slice(&v.to_be_bytes()),
417 _ => {
418 out.extend_from_slice(&i128::from(val.as_i64().ok_or_else(mismatch)?).to_be_bytes())
419 }
420 },
421 blr::FLOAT => match val {
422 Value::Float(f) => out.extend_from_slice(&f.to_bits().to_be_bytes()),
423 Value::Double(f) => out.extend_from_slice(&(*f as f32).to_bits().to_be_bytes()),
424 _ => return Err(mismatch()),
425 },
426 blr::DOUBLE | blr::D_FLOAT => match val {
427 Value::Double(f) => out.extend_from_slice(&f.to_bits().to_be_bytes()),
428 Value::Float(f) => out.extend_from_slice(&f64::from(*f).to_bits().to_be_bytes()),
429 _ => return Err(mismatch()),
430 },
431 blr::SQL_DATE => match val {
432 Value::Date(d) | Value::Timestamp(d, _) => put_i32_be(out, *d),
433 _ => return Err(mismatch()),
434 },
435 blr::SQL_TIME => match val {
436 Value::Time(t) | Value::Timestamp(_, t) => put_i32_be(out, *t as i32),
437 _ => return Err(mismatch()),
438 },
439 blr::TIMESTAMP => match val {
440 Value::Timestamp(d, t) => {
441 put_i32_be(out, *d);
442 put_i32_be(out, *t as i32);
443 }
444 _ => return Err(mismatch()),
445 },
446 blr::BOOL => {
447 out.push(matches!(val, Value::Bool(true)) as u8);
448 put_pad(out, 1);
449 }
450 blr::DEC64 => match val {
451 Value::DecFloat(d) => out.extend_from_slice(&d.to_decimal64().ok_or_else(mismatch)?),
452 _ => return Err(mismatch()),
453 },
454 blr::DEC128 => match val {
455 Value::DecFloat(d) => out.extend_from_slice(&d.to_decimal128().ok_or_else(mismatch)?),
456 _ => return Err(mismatch()),
457 },
458 blr::VARYING => {
459 let bytes = elem_text_bytes(val, charset)?;
460 put_i32_be(out, bytes.len() as i32);
461 out.extend_from_slice(&bytes);
462 put_pad(out, bytes.len());
463 }
464 blr::TEXT => {
465 let bytes = elem_text_bytes(val, charset)?;
466 let n = desc.length as usize;
467 out.extend_from_slice(&bytes);
468 for _ in bytes.len()..n {
469 out.push(b' '); }
471 put_pad(out, n.max(bytes.len()));
472 }
473 _ => {
474 return Err(Error::protocol(format!(
475 "tipo de elemento BLR {} não suportado para escrita",
476 desc.blr_type
477 )));
478 }
479 }
480 Ok(())
481}
482
483fn put_i32_be(out: &mut Vec<u8>, v: i32) {
484 out.extend_from_slice(&v.to_be_bytes());
485}
486
487fn put_pad(out: &mut Vec<u8>, data_len: usize) {
488 for _ in 0..(crate::value::align4(data_len) - data_len) {
489 out.push(0);
490 }
491}
492
493fn elem_text_bytes(val: &Value, charset: Charset) -> Result<std::borrow::Cow<'_, [u8]>> {
494 use std::borrow::Cow;
495 match val {
496 Value::Text(s) => Ok(Cow::Owned(charset.encode(s))),
497 Value::Bytes(b) => Ok(Cow::Borrowed(b)),
498 _ => Err(Error::protocol(
499 "esperava um valor de texto/bytes para elemento de array",
500 )),
501 }
502}
503
504fn text_or_bytes(desc: &ArrayDesc, raw: Vec<u8>, charset: Charset, is_char: bool) -> Value {
507 const CS_OCTETS: i32 = 1;
508 if desc.sub_type == CS_OCTETS {
509 Value::Bytes(raw)
510 } else {
511 let s = charset.decode(&raw);
512 if is_char {
513 Value::Text(s.trim_end_matches(' ').to_string())
514 } else {
515 Value::Text(s)
516 }
517 }
518}
519
520#[cfg(test)]
521mod tests {
522 use super::*;
523
524 fn varchar15_1to5() -> ArrayDesc {
525 ArrayDesc {
526 relation: "JOB".into(),
527 field: "LANGUAGE_REQ".into(),
528 blr_type: blr::VARYING,
529 sub_type: 0,
530 scale: 0,
531 length: 15,
532 dimensions: vec![Dimension { lower: 1, upper: 5 }],
533 }
534 }
535
536 #[test]
537 fn sdl_matches_captured_bytes() {
538 let expected: &[u8] = &[
541 0x01, 0x06, 0x01, 0x25, 0x0f, 0x00, 0x02, 0x03, b'J', b'O', b'B', 0x04, 0x0c, b'L',
542 b'A', b'N', b'G', b'U', b'A', b'G', b'E', b'_', b'R', b'E', b'Q', 0x23, 0x00, 0x09,
543 0x05, 0x24, 0x01, 0x08, 0x00, 0x01, 0x07, 0x00, 0xff,
544 ];
545 assert_eq!(varchar15_1to5().to_sdl(), expected);
546 }
547
548 #[test]
549 fn element_count_and_stride() {
550 let d = varchar15_1to5();
551 assert_eq!(d.element_count(), 5);
552 assert_eq!(d.element_stride(), 17); assert_eq!(d.slice_len(), 85);
554 }
555
556 #[test]
557 fn sdl_uses_do2_when_lower_bound_not_one() {
558 let mut d = varchar15_1to5();
559 d.dimensions = vec![Dimension {
560 lower: -2,
561 upper: 3,
562 }];
563 let s = d.to_sdl();
564 let pos = s.iter().position(|&b| b == sdl::DO2).expect("esperava DO2");
566 assert_eq!(s[pos + 1], 0); assert_eq!(s[pos + 2], sdl::TINY_INTEGER);
568 assert_eq!(s[pos + 3] as i8, -2); assert_eq!(s[pos + 4], sdl::TINY_INTEGER);
570 assert_eq!(s[pos + 5] as i8, 3); assert_eq!(d.element_count(), 6);
572 }
573
574 #[test]
575 fn sdl_multidim_emits_two_loops_and_two_subscripts() {
576 let mut d = varchar15_1to5();
577 d.blr_type = blr::LONG;
578 d.length = 4;
579 d.dimensions = vec![
580 Dimension { lower: 1, upper: 2 },
581 Dimension { lower: 1, upper: 3 },
582 ];
583 let s = d.to_sdl();
584 assert_eq!(s.iter().filter(|&&b| b == sdl::DO1).count(), 2);
585 assert_eq!(s.iter().filter(|&&b| b == sdl::VARIABLE).count(), 2);
586 assert_eq!(d.element_count(), 6);
587 let pos = s.iter().position(|&b| b == sdl::SCALAR).unwrap();
589 assert_eq!(s[pos + 2], 2);
590 }
591}