1use std::collections::BTreeMap;
11use std::error::Error;
12use std::{io, str};
13
14use bytes::{BufMut, BytesMut};
15use chrono::{DateTime, NaiveDateTime, NaiveTime, Utc};
16use dec::OrderedDecimal;
17use itertools::Itertools;
18use mz_ore::cast::ReinterpretCast;
19use mz_ore::fmt::FormatBuffer;
20use mz_pgrepr_consts::oid::TYPE_INT2_OID;
21use mz_pgwire_common::Format;
22use mz_repr::adt::array::ArrayDimension;
23use mz_repr::adt::char;
24use mz_repr::adt::date::Date;
25use mz_repr::adt::jsonb::JsonbRef;
26use mz_repr::adt::mz_acl_item::{AclItem, MzAclItem};
27use mz_repr::adt::numeric::{self as mz_repr_numeric, NumericMaxScale, rescale};
28use mz_repr::adt::pg_legacy_name::NAME_MAX_BYTES;
29use mz_repr::adt::range::{Range, RangeInner};
30use mz_repr::adt::timestamp::CheckedTimestamp;
31use mz_repr::strconv::{self, Nestable};
32use mz_repr::{Datum, RowArena, RowPacker, RowRef, SqlRelationType, SqlScalarType};
33use postgres_types::{FromSql, IsNull, ToSql, Type as PgType};
34use uuid::Uuid;
35
36use crate::types::{NumericConstraints, UINT2, UINT4, UINT8};
37use crate::value::error::{IntoDatumError, NulCharacterError};
38use crate::{Interval, Jsonb, Numeric, Type, UInt2, UInt4, UInt8};
39
40pub mod error;
41pub mod interval;
42pub mod jsonb;
43pub mod numeric;
44pub mod record;
45pub mod unsigned;
46
47#[derive(Debug)]
49pub enum Value {
50 Array {
52 dims: Vec<ArrayDimension>,
54 elements: Vec<Option<Value>>,
56 },
57 Bool(bool),
59 Bytea(Vec<u8>),
61 Char(u8),
63 Date(Date),
65 Float4(f32),
67 Float8(f64),
69 Int2(i16),
71 Int4(i32),
73 Int8(i64),
75 UInt2(UInt2),
77 UInt4(UInt4),
79 UInt8(UInt8),
81 Interval(Interval),
83 Jsonb(Jsonb),
85 List(Vec<Option<Value>>),
87 Map(BTreeMap<String, Option<Value>>),
89 Name(String),
91 Numeric(Numeric),
93 Oid(u32),
95 Record(Vec<Option<Value>>),
97 Time(NaiveTime),
99 Timestamp(CheckedTimestamp<NaiveDateTime>),
101 TimestampTz(CheckedTimestamp<DateTime<Utc>>),
103 Text(String),
105 BpChar(String),
107 VarChar(String),
109 Uuid(Uuid),
111 Int2Vector {
113 elements: Vec<Option<Value>>,
115 },
116 MzTimestamp(mz_repr::Timestamp),
118 Range(Range<Box<Value>>),
120 MzAclItem(MzAclItem),
123 AclItem(AclItem),
126}
127
128impl Value {
129 pub fn from_datum(datum: Datum, typ: &SqlScalarType) -> Option<Value> {
134 match (datum, typ) {
135 (Datum::Null, _) => None,
136 (Datum::True, SqlScalarType::Bool) => Some(Value::Bool(true)),
137 (Datum::False, SqlScalarType::Bool) => Some(Value::Bool(false)),
138 (Datum::Int16(i), SqlScalarType::Int16) => Some(Value::Int2(i)),
139 (Datum::Int32(i), SqlScalarType::Int32) => Some(Value::Int4(i)),
140 (Datum::Int64(i), SqlScalarType::Int64) => Some(Value::Int8(i)),
141 (Datum::UInt8(c), SqlScalarType::PgLegacyChar) => Some(Value::Char(c)),
142 (Datum::UInt16(u), SqlScalarType::UInt16) => Some(Value::UInt2(UInt2(u))),
143 (Datum::UInt32(oid), SqlScalarType::Oid) => Some(Value::Oid(oid)),
144 (Datum::UInt32(oid), SqlScalarType::RegClass) => Some(Value::Oid(oid)),
145 (Datum::UInt32(oid), SqlScalarType::RegProc) => Some(Value::Oid(oid)),
146 (Datum::UInt32(oid), SqlScalarType::RegType) => Some(Value::Oid(oid)),
147 (Datum::UInt32(u), SqlScalarType::UInt32) => Some(Value::UInt4(UInt4(u))),
148 (Datum::UInt64(u), SqlScalarType::UInt64) => Some(Value::UInt8(UInt8(u))),
149 (Datum::Float32(f), SqlScalarType::Float32) => Some(Value::Float4(*f)),
150 (Datum::Float64(f), SqlScalarType::Float64) => Some(Value::Float8(*f)),
151 (Datum::Numeric(d), SqlScalarType::Numeric { .. }) => Some(Value::Numeric(Numeric(d))),
152 (Datum::MzTimestamp(t), SqlScalarType::MzTimestamp) => Some(Value::MzTimestamp(t)),
153 (Datum::MzAclItem(mai), SqlScalarType::MzAclItem) => Some(Value::MzAclItem(mai)),
154 (Datum::AclItem(ai), SqlScalarType::AclItem) => Some(Value::AclItem(ai)),
155 (Datum::Date(d), SqlScalarType::Date) => Some(Value::Date(d)),
156 (Datum::Time(t), SqlScalarType::Time) => Some(Value::Time(t)),
157 (Datum::Timestamp(ts), SqlScalarType::Timestamp { .. }) => Some(Value::Timestamp(ts)),
158 (Datum::TimestampTz(ts), SqlScalarType::TimestampTz { .. }) => {
159 Some(Value::TimestampTz(ts))
160 }
161 (Datum::Interval(iv), SqlScalarType::Interval) => Some(Value::Interval(Interval(iv))),
162 (Datum::Bytes(b), SqlScalarType::Bytes) => Some(Value::Bytea(b.to_vec())),
163 (Datum::String(s), SqlScalarType::String) => Some(Value::Text(s.to_owned())),
164 (Datum::String(s), SqlScalarType::VarChar { .. }) => Some(Value::VarChar(s.to_owned())),
165 (Datum::String(s), SqlScalarType::Char { length }) => {
166 Some(Value::BpChar(char::format_str_pad(s, *length)))
167 }
168 (Datum::String(s), SqlScalarType::PgLegacyName) => Some(Value::Name(s.into())),
169 (_, SqlScalarType::Jsonb) => {
170 Some(Value::Jsonb(Jsonb(JsonbRef::from_datum(datum).to_owned())))
171 }
172 (Datum::Uuid(u), SqlScalarType::Uuid) => Some(Value::Uuid(u)),
173 (Datum::Array(array), SqlScalarType::Array(elem_type)) => {
174 let dims = array.dims().into_iter().collect();
175 let elements = array
176 .elements()
177 .iter()
178 .map(|elem| Value::from_datum(elem, elem_type))
179 .collect();
180 Some(Value::Array { dims, elements })
181 }
182 (Datum::Array(array), SqlScalarType::Int2Vector) => {
183 assert!(
184 array.has_int2vector_dims(),
185 "int2vector must be 1 dimensional, or empty"
186 );
187 let elements = array
188 .elements()
189 .iter()
190 .map(|elem| Value::from_datum(elem, &SqlScalarType::Int16))
191 .collect();
192 Some(Value::Int2Vector { elements })
193 }
194 (Datum::List(list), SqlScalarType::List { element_type, .. }) => {
195 let elements = list
196 .iter()
197 .map(|elem| Value::from_datum(elem, element_type))
198 .collect();
199 Some(Value::List(elements))
200 }
201 (Datum::List(record), SqlScalarType::Record { fields, .. }) => {
202 let fields = record
203 .iter()
204 .zip_eq(fields)
205 .map(|(e, (_name, ty))| Value::from_datum(e, &ty.scalar_type))
206 .collect();
207 Some(Value::Record(fields))
208 }
209 (Datum::Map(dict), SqlScalarType::Map { value_type, .. }) => {
210 let entries = dict
211 .iter()
212 .map(|(k, v)| (k.to_owned(), Value::from_datum(v, value_type)))
213 .collect();
214 Some(Value::Map(entries))
215 }
216 (Datum::Range(range), SqlScalarType::Range { element_type }) => {
217 let value_range = range.into_bounds(|b| {
218 Box::new(
219 Value::from_datum(b.datum(), element_type)
220 .expect("RangeBounds never contain Datum::Null"),
221 )
222 });
223 Some(Value::Range(value_range))
224 }
225 _ => panic!("can't serialize {}::{:?}", datum, typ),
226 }
227 }
228
229 pub fn into_datum<'a>(
231 self,
232 buf: &'a RowArena,
233 typ: &Type,
234 ) -> Result<Datum<'a>, IntoDatumError> {
235 Ok(match self {
236 Value::Array { dims, elements } => {
237 let element_pg_type = match typ {
238 Type::Array(t) => &*t,
239 _ => panic!("Value::Array should have type Type::Array. Found {:?}", typ),
240 };
241 let elements: Result<Vec<_>, _> = elements
242 .into_iter()
243 .map(|element| match element {
244 Some(element) => element.into_datum(buf, element_pg_type),
245 None => Ok(Datum::Null),
246 })
247 .collect();
248 let elements = elements?;
249 buf.try_make_datum(|packer| {
250 packer
251 .try_push_array(&dims, elements)
252 .map_err(IntoDatumError::from)
253 })?
254 }
255 Value::Int2Vector { .. } => {
256 unreachable!("into_datum cannot be called on Value::Int2Vector");
259 }
260 Value::Bool(true) => Datum::True,
261 Value::Bool(false) => Datum::False,
262 Value::Bytea(b) => Datum::Bytes(buf.push_bytes(b)),
263 Value::Char(c) => Datum::UInt8(c),
264 Value::Date(d) => Datum::Date(d),
265 Value::Float4(f) => Datum::Float32(f.into()),
266 Value::Float8(f) => Datum::Float64(f.into()),
267 Value::Int2(i) => Datum::Int16(i),
268 Value::Int4(i) => Datum::Int32(i),
269 Value::Int8(i) => Datum::Int64(i),
270 Value::UInt2(u) => Datum::UInt16(u.0),
271 Value::UInt4(u) => Datum::UInt32(u.0),
272 Value::UInt8(u) => Datum::UInt64(u.0),
273 Value::Jsonb(js) => buf.push_unary_row(js.0.into_row()),
274 Value::List(elems) => {
275 let elem_pg_type = match typ {
276 Type::List(t) => &*t,
277 _ => panic!("Value::List should have type Type::List. Found {:?}", typ),
278 };
279 let elems: Result<Vec<_>, _> = elems
280 .into_iter()
281 .map(|elem| match elem {
282 Some(elem) => elem.into_datum(buf, elem_pg_type),
283 None => Ok(Datum::Null),
284 })
285 .collect();
286 let elems = elems?;
287 buf.make_datum(|packer| packer.push_list(elems))
288 }
289 Value::Map(map) => {
290 let elem_pg_type = match typ {
291 Type::Map { value_type } => &*value_type,
292 _ => panic!("Value::Map should have type Type::Map. Found {:?}", typ),
293 };
294 buf.try_make_datum(|packer| {
295 packer.try_push_dict_with(|row| {
296 for (k, v) in map {
297 row.push(Datum::String(buf.push_string(k)));
298 let datum = match v {
299 Some(elem) => elem.into_datum(buf, elem_pg_type)?,
300 None => Datum::Null,
301 };
302 row.push(datum);
303 }
304 Ok::<_, IntoDatumError>(())
305 })
306 })?
307 }
308 Value::Oid(oid) => Datum::UInt32(oid),
309 Value::Record(_) => {
310 unreachable!("into_datum cannot be called on Value::Record");
313 }
314 Value::Time(t) => Datum::Time(t),
315 Value::Timestamp(ts) => Datum::Timestamp(ts),
316 Value::TimestampTz(ts) => Datum::TimestampTz(ts),
317 Value::Interval(iv) => Datum::Interval(iv.0),
318 Value::Text(s) | Value::VarChar(s) | Value::Name(s) => {
319 Datum::String(buf.push_string(s))
320 }
321 Value::BpChar(s) => Datum::String(buf.push_string(s.trim_end().into())),
322 Value::Uuid(u) => Datum::Uuid(u),
323 Value::Numeric(n) => Datum::Numeric(n.0),
324 Value::MzTimestamp(t) => Datum::MzTimestamp(t),
325 Value::Range(range) => {
326 let elem_pg_type = match typ {
327 Type::Range { element_type } => &*element_type,
328 _ => panic!("Value::Range should have type Type::Range. Found {:?}", typ),
329 };
330 let range = range.try_into_bounds(|elem| elem.into_datum(buf, elem_pg_type))?;
331 buf.try_make_datum(|packer| packer.push_range(range).map_err(IntoDatumError::from))?
332 }
333 Value::MzAclItem(mz_acl_item) => Datum::MzAclItem(mz_acl_item),
334 Value::AclItem(acl_item) => Datum::AclItem(acl_item),
335 })
336 }
337
338 pub fn into_datum_decode_error<'a>(
343 self,
344 buf: &'a RowArena,
345 typ: &Type,
346 context: &str,
347 ) -> Result<Datum<'a>, String> {
348 self.into_datum(buf, typ)
349 .map_err(|e| format!("unable to decode {}: {}", context, e))
350 }
351
352 pub fn encode(
356 &self,
357 ty: &Type,
358 format: Format,
359 buf: &mut BytesMut,
360 settings: TextEncodeSettings,
361 ) -> Result<(), io::Error> {
362 match format {
363 Format::Text => {
364 self.encode_text(buf, settings);
365 Ok(())
366 }
367 Format::Binary => self.encode_binary(ty, buf),
368 }
369 }
370
371 pub fn encode_text(&self, buf: &mut BytesMut, settings: TextEncodeSettings) -> Nestable {
374 let extra_float_digits = settings.extra_float_digits;
375 match self {
376 Value::Array { dims, elements } => {
377 strconv::format_array(buf, dims, elements, |buf, elem| match elem {
378 None => Ok::<_, ()>(buf.write_null()),
379 Some(elem) => Ok(elem.encode_text(buf.nonnull_buffer(), settings)),
380 })
381 .expect("provided closure never fails")
382 }
383 Value::Int2Vector { elements } => {
384 strconv::format_legacy_vector(buf, elements, |buf, elem| {
385 Ok::<_, ()>(
386 elem.as_ref()
387 .expect("Int2Vector does not support NULL values")
388 .encode_text(buf.nonnull_buffer(), settings),
389 )
390 })
391 .expect("provided closure never fails")
392 }
393 Value::Bool(b) => strconv::format_bool(buf, *b),
394 Value::Bytea(b) => strconv::format_bytes(buf, b),
395 Value::Char(c) => {
396 buf.put_u8(*c);
397 Nestable::MayNeedEscaping
398 }
399 Value::Date(d) => strconv::format_date(buf, *d),
400 Value::Int2(i) => strconv::format_int16(buf, *i),
401 Value::Int4(i) => strconv::format_int32(buf, *i),
402 Value::Int8(i) => strconv::format_int64(buf, *i),
403 Value::UInt2(u) => strconv::format_uint16(buf, u.0),
404 Value::UInt4(u) => strconv::format_uint32(buf, u.0),
405 Value::UInt8(u) => strconv::format_uint64(buf, u.0),
406 Value::Interval(iv) => strconv::format_interval(buf, iv.0),
407 Value::Float4(f) if extra_float_digits > 0 => strconv::format_float32(buf, *f),
408 Value::Float4(f) => {
409 format_float_limited(buf, f64::from(*f), FLOAT4_DIGITS + extra_float_digits)
410 }
411 Value::Float8(f) if extra_float_digits > 0 => strconv::format_float64(buf, *f),
412 Value::Float8(f) => format_float_limited(buf, *f, FLOAT8_DIGITS + extra_float_digits),
413 Value::Jsonb(js) => strconv::format_jsonb(buf, js.0.as_ref()),
414 Value::List(elems) => strconv::format_list(buf, elems, |buf, elem| match elem {
415 None => Ok::<_, ()>(buf.write_null()),
416 Some(elem) => Ok(elem.encode_text(buf.nonnull_buffer(), settings)),
417 })
418 .expect("provided closure never fails"),
419 Value::Map(elems) => strconv::format_map(buf, elems, |buf, value| match value {
420 None => Ok::<_, ()>(buf.write_null()),
421 Some(elem) => Ok(elem.encode_text(buf.nonnull_buffer(), settings)),
422 })
423 .expect("provided closure never fails"),
424 Value::Oid(oid) => strconv::format_uint32(buf, *oid),
425 Value::Record(elems) => strconv::format_record(buf, elems, |buf, elem| match elem {
426 None => Ok::<_, ()>(buf.write_null()),
427 Some(elem) => Ok(elem.encode_text(buf.nonnull_buffer(), settings)),
428 })
429 .expect("provided closure never fails"),
430 Value::Text(s) | Value::VarChar(s) | Value::BpChar(s) | Value::Name(s) => {
431 strconv::format_string(buf, s)
432 }
433 Value::Time(t) => strconv::format_time(buf, *t),
434 Value::Timestamp(ts) => strconv::format_timestamp(buf, ts),
435 Value::TimestampTz(ts) => strconv::format_timestamptz(buf, ts),
436 Value::Uuid(u) => strconv::format_uuid(buf, *u),
437 Value::Numeric(d) => strconv::format_numeric(buf, &d.0),
438 Value::MzTimestamp(t) => strconv::format_mz_timestamp(buf, *t),
439 Value::Range(range) => strconv::format_range(buf, range, |buf, elem| match elem {
440 Some(elem) => Ok(elem.encode_text(buf.nonnull_buffer(), settings)),
441 None => Ok::<_, ()>(buf.write_null()),
442 })
443 .expect("provided closure never fails"),
444 Value::MzAclItem(mz_acl_item) => strconv::format_mz_acl_item(buf, *mz_acl_item),
445 Value::AclItem(acl_item) => strconv::format_acl_item(buf, *acl_item),
446 }
447 }
448
449 pub fn encode_binary(&self, ty: &Type, buf: &mut BytesMut) -> Result<(), io::Error> {
452 let is_null = match self {
455 Value::Array { dims, elements } => {
456 let ndims = pg_len("number of array dimensions", dims.len())?;
457 let has_null = elements.iter().any(|e| e.is_none());
458 let elem_type = match ty {
459 Type::Array(elem_type) => elem_type,
460 _ => unreachable!(),
461 };
462 buf.put_i32(ndims);
463 buf.put_i32(has_null.into());
464 buf.put_u32(elem_type.oid());
465 for dim in dims {
466 buf.put_i32(pg_len("array dimension length", dim.length)?);
467 buf.put_i32(dim.lower_bound.try_into().map_err(|_| {
468 io::Error::new(
469 io::ErrorKind::InvalidData,
470 "array dimension lower bound does not fit into an i32",
471 )
472 })?);
473 }
474 for elem in elements {
475 encode_element(buf, elem.as_ref(), elem_type)?;
476 }
477 Ok(postgres_types::IsNull::No)
478 }
479 Value::Int2Vector { elements } => {
480 let has_null = elements.iter().any(|e| e.is_none());
483 buf.put_i32(1);
484 buf.put_i32(has_null.into());
485 buf.put_u32(TYPE_INT2_OID);
486 buf.put_i32(pg_len("int2vector dimension length", elements.len())?);
487 buf.put_i32(0);
488 for elem in elements {
489 encode_element(buf, elem.as_ref(), &Type::Int2)?;
490 }
491 Ok(postgres_types::IsNull::No)
492 }
493 Value::Bool(b) => b.to_sql(&PgType::BOOL, buf),
494 Value::Bytea(b) => b.to_sql(&PgType::BYTEA, buf),
495 Value::Char(c) => i8::reinterpret_cast(*c).to_sql(&PgType::CHAR, buf),
496 Value::Date(d) => d.pg_epoch_days().to_sql(&PgType::DATE, buf),
497 Value::Float4(f) => f.to_sql(&PgType::FLOAT4, buf),
498 Value::Float8(f) => f.to_sql(&PgType::FLOAT8, buf),
499 Value::Int2(i) => i.to_sql(&PgType::INT2, buf),
500 Value::Int4(i) => i.to_sql(&PgType::INT4, buf),
501 Value::Int8(i) => i.to_sql(&PgType::INT8, buf),
502 Value::UInt2(u) => u.to_sql(&*UINT2, buf),
503 Value::UInt4(u) => u.to_sql(&*UINT4, buf),
504 Value::UInt8(u) => u.to_sql(&*UINT8, buf),
505 Value::Interval(iv) => iv.to_sql(&PgType::INTERVAL, buf),
506 Value::Jsonb(js) => js.to_sql(&PgType::JSONB, buf),
507 Value::List(_) => {
508 Err("binary encoding of list types is not implemented".into())
539 }
540 Value::Map(_) => {
541 Err("binary encoding of map types is not implemented".into())
545 }
546 Value::Name(s) => s.to_sql(&PgType::NAME, buf),
547 Value::Oid(i) => i.to_sql(&PgType::OID, buf),
548 Value::Record(fields) => {
549 let nfields = pg_len("record field length", fields.len())?;
550 buf.put_i32(nfields);
551 let field_types = match ty {
552 Type::Record(fields) => fields,
553 _ => unreachable!(),
554 };
555 for (f, ty) in fields.iter().zip_eq(field_types) {
556 buf.put_u32(ty.oid());
557 encode_element(buf, f.as_ref(), ty)?;
558 }
559 Ok(postgres_types::IsNull::No)
560 }
561 Value::Text(s) => s.to_sql(&PgType::TEXT, buf),
562 Value::BpChar(s) => s.to_sql(&PgType::BPCHAR, buf),
563 Value::VarChar(s) => s.to_sql(&PgType::VARCHAR, buf),
564 Value::Time(t) => t.to_sql(&PgType::TIME, buf),
565 Value::Timestamp(ts) => ts.to_sql(&PgType::TIMESTAMP, buf),
566 Value::TimestampTz(ts) => ts.to_sql(&PgType::TIMESTAMPTZ, buf),
567 Value::Uuid(u) => u.to_sql(&PgType::UUID, buf),
568 Value::Numeric(a) => a.to_sql(&PgType::NUMERIC, buf),
569 Value::MzTimestamp(t) => t.to_string().to_sql(&PgType::TEXT, buf),
570 Value::Range(range) => {
571 buf.put_u8(range.pg_flag_bits());
572
573 let elem_type = match ty {
574 Type::Range { element_type } => element_type,
575 _ => unreachable!(),
576 };
577
578 if let Some(RangeInner { lower, upper }) = &range.inner {
579 for bound in [&lower.bound, &upper.bound] {
580 if let Some(bound) = bound {
581 let base = buf.len();
582 buf.put_i32(0);
583 bound.encode_binary(elem_type, buf)?;
584 let len = pg_len("encoded range bound", buf.len() - base - 4)?;
585 buf[base..base + 4].copy_from_slice(&len.to_be_bytes());
586 }
587 }
588 }
589 Ok(postgres_types::IsNull::No)
590 }
591 Value::MzAclItem(mz_acl_item) => {
592 buf.extend_from_slice(&mz_acl_item.encode_binary());
593 Ok(postgres_types::IsNull::No)
594 }
595 Value::AclItem(_) => Err("aclitem has no binary encoding".into()),
596 }
597 .expect("encode_binary should never trigger a to_sql failure");
598 if let IsNull::Yes = is_null {
599 panic!("encode_binary impossibly called on a null value")
600 }
601 Ok(())
602 }
603
604 pub fn binary_encoding_error(typ: &SqlScalarType) -> Result<(), &'static str> {
618 match typ {
619 SqlScalarType::Bool => Ok(()),
620 SqlScalarType::Int16 => Ok(()),
621 SqlScalarType::Int32 => Ok(()),
622 SqlScalarType::Int64 => Ok(()),
623 SqlScalarType::PgLegacyChar => Ok(()),
624 SqlScalarType::UInt16 => Ok(()),
625 SqlScalarType::Oid => Ok(()),
626 SqlScalarType::RegClass => Ok(()),
627 SqlScalarType::RegProc => Ok(()),
628 SqlScalarType::RegType => Ok(()),
629 SqlScalarType::UInt32 => Ok(()),
630 SqlScalarType::UInt64 => Ok(()),
631 SqlScalarType::Float32 => Ok(()),
632 SqlScalarType::Float64 => Ok(()),
633 SqlScalarType::Numeric { .. } => Ok(()),
634 SqlScalarType::MzTimestamp => Ok(()),
635 SqlScalarType::MzAclItem => Ok(()),
636 SqlScalarType::AclItem => Err("no binary output function available for type aclitem"),
637 SqlScalarType::Date => Ok(()),
638 SqlScalarType::Time => Ok(()),
639 SqlScalarType::Timestamp { .. } => Ok(()),
640 SqlScalarType::TimestampTz { .. } => Ok(()),
641 SqlScalarType::Interval => Ok(()),
642 SqlScalarType::Bytes => Ok(()),
643 SqlScalarType::String => Ok(()),
644 SqlScalarType::VarChar { .. } => Ok(()),
645 SqlScalarType::Char { .. } => Ok(()),
646 SqlScalarType::PgLegacyName => Ok(()),
647 SqlScalarType::Jsonb => Ok(()),
648 SqlScalarType::Uuid => Ok(()),
649 SqlScalarType::Array(elem_type) => Self::binary_encoding_error(elem_type),
650 SqlScalarType::Int2Vector => Ok(()),
651 SqlScalarType::List { .. } => Err("no binary output function available for type list"),
652 SqlScalarType::Map { .. } => Err("no binary output function available for type map"),
653 SqlScalarType::Record { fields, .. } => fields
654 .iter()
655 .try_for_each(|(_, ty)| Self::binary_encoding_error(&ty.scalar_type)),
656 SqlScalarType::Range { element_type } => Self::binary_encoding_error(element_type),
657 }
658 }
659
660 pub fn can_encode_binary(typ: &SqlScalarType) -> bool {
664 Self::binary_encoding_error(typ).is_ok()
665 }
666
667 pub fn decode(
670 format: Format,
671 ty: &Type,
672 raw: &[u8],
673 ) -> Result<Value, Box<dyn Error + Sync + Send>> {
674 match format {
675 Format::Text => Value::decode_text(ty, raw),
676 Format::Binary => Value::decode_binary(ty, raw),
677 }
678 }
679
680 pub fn decode_text<'a>(
683 ty: &'a Type,
684 raw: &'a [u8],
685 ) -> Result<Value, Box<dyn Error + Sync + Send>> {
686 let s = str::from_utf8(raw)?;
687 reject_nul(s)?;
690 Ok(match ty {
691 Type::Array(elem_type) => {
692 let (elements, dims) = strconv::parse_array(
693 s,
694 || None,
695 |elem_text| Value::decode_text(elem_type, elem_text.as_bytes()).map(Some),
696 )?;
697 Value::Array { dims, elements }
698 }
699 Type::Int2Vector { .. } => {
700 return Err("input of Int2Vector types is not implemented".into());
701 }
702 Type::Bool => Value::Bool(strconv::parse_bool(s)?),
703 Type::Bytea => Value::Bytea(strconv::parse_bytes(s)?),
704 Type::Char => Value::Char(raw.get(0).copied().unwrap_or(0)),
705 Type::Date => Value::Date(strconv::parse_date(s)?),
706 Type::Float4 => Value::Float4(strconv::parse_float32(s)?),
707 Type::Float8 => Value::Float8(strconv::parse_float64(s)?),
708 Type::Int2 => Value::Int2(strconv::parse_int16(s)?),
709 Type::Int4 => Value::Int4(strconv::parse_int32(s)?),
710 Type::Int8 => Value::Int8(strconv::parse_int64(s)?),
711 Type::UInt2 => Value::UInt2(UInt2(strconv::parse_uint16(s)?)),
712 Type::UInt4 => Value::UInt4(UInt4(strconv::parse_uint32(s)?)),
713 Type::UInt8 => Value::UInt8(UInt8(strconv::parse_uint64(s)?)),
714 Type::Interval { .. } => Value::Interval(Interval(strconv::parse_interval(s)?)),
715 Type::Json => return Err("input of json types is not implemented".into()),
716 Type::Jsonb => Value::Jsonb(Jsonb(strconv::parse_jsonb(s)?)),
717 Type::List(elem_type) => Value::List(strconv::parse_list(
718 s,
719 matches!(**elem_type, Type::List(..)),
720 || None,
721 |elem_text| Value::decode_text(elem_type, elem_text.as_bytes()).map(Some),
722 )?),
723 Type::Map { value_type } => Value::Map(strconv::parse_map(
724 s,
725 matches!(**value_type, Type::Map { .. }),
726 |elem_text| {
727 elem_text
728 .map(|t| Value::decode_text(value_type, t.as_bytes()))
729 .transpose()
730 },
731 )?),
732 Type::Name => Value::Name(strconv::parse_pg_legacy_name(s)),
733 Type::Numeric { constraints } => Value::Numeric(Numeric(rescale_numeric(
734 strconv::parse_numeric(s)?,
735 constraints.as_ref(),
736 )?)),
737 Type::Oid | Type::RegClass | Type::RegProc | Type::RegType => {
738 Value::Oid(strconv::parse_oid(s)?)
739 }
740 Type::Record(_) => {
741 return Err("input of anonymous composite types is not implemented".into());
742 }
743 Type::Text => Value::Text(s.to_owned()),
744 Type::BpChar { .. } => Value::BpChar(s.to_owned()),
745 Type::VarChar { .. } => Value::VarChar(s.to_owned()),
746 Type::Time { .. } => Value::Time(strconv::parse_time(s)?),
747 Type::TimeTz { .. } => return Err("input of timetz types is not implemented".into()),
748 Type::Timestamp { .. } => Value::Timestamp(strconv::parse_timestamp(s)?),
749 Type::TimestampTz { .. } => Value::TimestampTz(strconv::parse_timestamptz(s)?),
750 Type::Uuid => Value::Uuid(strconv::parse_uuid(s)?),
751 Type::MzTimestamp => Value::MzTimestamp(strconv::parse_mz_timestamp(s)?),
752 Type::Range { element_type } => Value::Range(strconv::parse_range(s, |elem_text| {
753 Value::decode_text(element_type, elem_text.as_bytes()).map(Box::new)
754 })?),
755 Type::MzAclItem => Value::MzAclItem(strconv::parse_mz_acl_item(s)?),
756 Type::AclItem => Value::AclItem(strconv::parse_acl_item(s)?),
757 })
758 }
759
760 pub fn decode_text_into_row<'a>(
762 ty: &'a Type,
763 s: &'a str,
764 packer: &mut RowPacker,
765 ) -> Result<(), Box<dyn Error + Sync + Send>> {
766 reject_nul(s)?;
769 Ok(match ty {
770 Type::Array(elem_type) => {
771 let (elements, dims) =
772 strconv::parse_array(s, || None, |elem_text| Ok::<_, String>(Some(elem_text)))?;
773 unsafe {
775 packer.push_array_with_unchecked(&dims, |packer| {
776 let mut nelements = 0;
777 for element in elements {
778 match element {
779 Some(elem_text) => {
780 Value::decode_text_into_row(elem_type, &elem_text, packer)?
781 }
782
783 None => packer.push(Datum::Null),
784 }
785 nelements += 1;
786 }
787 Ok::<_, Box<dyn Error + Sync + Send>>(nelements)
788 })?
789 }
790 }
791 Type::Int2Vector { .. } => {
792 return Err("input of Int2Vector types is not implemented".into());
793 }
794 Type::Bool => packer.push(Datum::from(strconv::parse_bool(s)?)),
795 Type::Bytea => packer.push(Datum::Bytes(&strconv::parse_bytes(s)?)),
796 Type::Char => packer.push(Datum::UInt8(s.as_bytes().get(0).copied().unwrap_or(0))),
797 Type::Date => packer.push(Datum::Date(strconv::parse_date(s)?)),
798 Type::Float4 => packer.push(Datum::Float32(strconv::parse_float32(s)?.into())),
799 Type::Float8 => packer.push(Datum::Float64(strconv::parse_float64(s)?.into())),
800 Type::Int2 => packer.push(Datum::Int16(strconv::parse_int16(s)?)),
801 Type::Int4 => packer.push(Datum::Int32(strconv::parse_int32(s)?)),
802 Type::Int8 => packer.push(Datum::Int64(strconv::parse_int64(s)?)),
803 Type::UInt2 => packer.push(Datum::UInt16(strconv::parse_uint16(s)?)),
804 Type::UInt4 => packer.push(Datum::UInt32(strconv::parse_uint32(s)?)),
805 Type::UInt8 => packer.push(Datum::UInt64(strconv::parse_uint64(s)?)),
806 Type::Interval { .. } => packer.push(Datum::Interval(strconv::parse_interval(s)?)),
807 Type::Json => return Err("input of json types is not implemented".into()),
808 Type::Jsonb => packer.push(strconv::parse_jsonb(s)?.into_row().unpack_first()),
809 Type::List(elem_type) => {
810 let elems = strconv::parse_list(
811 s,
812 matches!(**elem_type, Type::List(..)),
813 || None,
814 |elem_text| Ok::<_, String>(Some(elem_text)),
815 )?;
816 packer.push_list_with(|packer| {
817 for elem in elems {
818 match elem {
819 Some(elem) => Value::decode_text_into_row(elem_type, &elem, packer)?,
820 None => packer.push(Datum::Null),
821 }
822 }
823 Ok::<_, Box<dyn Error + Sync + Send>>(())
824 })?;
825 }
826 Type::Map { value_type } => {
827 let map =
828 strconv::parse_map(s, matches!(**value_type, Type::Map { .. }), |elem_text| {
829 elem_text.map(Ok::<_, String>).transpose()
830 })?;
831 packer.push_dict_with(|row| {
832 for (k, v) in map {
833 row.push(Datum::String(&k));
834 match v {
835 Some(elem) => Value::decode_text_into_row(value_type, &elem, row)?,
836 None => row.push(Datum::Null),
837 }
838 }
839 Ok::<_, Box<dyn Error + Sync + Send>>(())
840 })?;
841 }
842 Type::Name => packer.push(Datum::String(&strconv::parse_pg_legacy_name(s))),
843 Type::Numeric { constraints } => packer.push(Datum::Numeric(rescale_numeric(
844 strconv::parse_numeric(s)?,
845 constraints.as_ref(),
846 )?)),
847 Type::Oid | Type::RegClass | Type::RegProc | Type::RegType => {
848 packer.push(Datum::UInt32(strconv::parse_oid(s)?))
849 }
850 Type::Record(_) => {
851 return Err("input of anonymous composite types is not implemented".into());
852 }
853 Type::Text => packer.push(Datum::String(s)),
854 Type::BpChar { .. } => packer.push(Datum::String(s.trim_end())),
855 Type::VarChar { .. } => packer.push(Datum::String(s)),
856 Type::Time { .. } => packer.push(Datum::Time(strconv::parse_time(s)?)),
857 Type::TimeTz { .. } => return Err("input of timetz types is not implemented".into()),
858 Type::Timestamp { .. } => packer.push(Datum::Timestamp(strconv::parse_timestamp(s)?)),
859 Type::TimestampTz { .. } => {
860 packer.push(Datum::TimestampTz(strconv::parse_timestamptz(s)?))
861 }
862 Type::Uuid => packer.push(Datum::Uuid(strconv::parse_uuid(s)?)),
863 Type::MzTimestamp => packer.push(Datum::MzTimestamp(strconv::parse_mz_timestamp(s)?)),
864 Type::Range { element_type } => {
865 let range = strconv::parse_range(s, |elem_text| {
866 Value::decode_text(element_type, elem_text.as_bytes()).map(Box::new)
867 })?;
868 let buf = RowArena::new();
871 let range = range
872 .try_into_bounds(|elem| elem.into_datum(&buf, element_type))
873 .map_err(Box::<dyn Error + Sync + Send>::from)?;
874 packer
875 .push_range(range)
876 .map_err(Box::<dyn Error + Sync + Send>::from)?;
877 }
878 Type::MzAclItem => packer.push(Datum::MzAclItem(strconv::parse_mz_acl_item(s)?)),
879 Type::AclItem => packer.push(Datum::AclItem(strconv::parse_acl_item(s)?)),
880 })
881 }
882
883 pub fn decode_binary(ty: &Type, raw: &[u8]) -> Result<Value, Box<dyn Error + Sync + Send>> {
886 match ty {
887 Type::Array(_) => Err("input of array types is not implemented".into()),
888 Type::Int2Vector => Err("input of int2vector types is not implemented".into()),
889 Type::Bool => bool::from_sql(ty.inner(), raw).map(Value::Bool),
890 Type::Bytea => Vec::<u8>::from_sql(ty.inner(), raw).map(Value::Bytea),
891 Type::Char => {
892 i8::from_sql(ty.inner(), raw).map(|c| Value::Char(u8::reinterpret_cast(c)))
893 }
894 Type::Date => {
895 let days = i32::from_sql(ty.inner(), raw)?;
896 Ok(Value::Date(Date::from_pg_epoch(days)?))
897 }
898 Type::Float4 => f32::from_sql(ty.inner(), raw).map(Value::Float4),
899 Type::Float8 => f64::from_sql(ty.inner(), raw).map(Value::Float8),
900 Type::Int2 => i16::from_sql(ty.inner(), raw).map(Value::Int2),
901 Type::Int4 => i32::from_sql(ty.inner(), raw).map(Value::Int4),
902 Type::Int8 => i64::from_sql(ty.inner(), raw).map(Value::Int8),
903 Type::UInt2 => UInt2::from_sql(ty.inner(), raw).map(Value::UInt2),
904 Type::UInt4 => UInt4::from_sql(ty.inner(), raw).map(Value::UInt4),
905 Type::UInt8 => UInt8::from_sql(ty.inner(), raw).map(Value::UInt8),
906 Type::Interval { .. } => Interval::from_sql(ty.inner(), raw).map(Value::Interval),
907 Type::Json => Err("input of json types is not implemented".into()),
908 Type::Jsonb => Jsonb::from_sql(ty.inner(), raw).map(Value::Jsonb),
909 Type::List(_) => Err("binary decoding of list types is not implemented".into()),
910 Type::Map { .. } => Err("binary decoding of map types is not implemented".into()),
911 Type::Name => {
912 let s = String::from_sql(ty.inner(), raw)?;
913 reject_nul(&s)?;
914 if s.len() > NAME_MAX_BYTES {
915 return Err("identifier too long".into());
916 }
917 Ok(Value::Name(s))
918 }
919 Type::Numeric { constraints } => {
920 let n = Numeric::from_sql(ty.inner(), raw)?;
921 if n.0.0.is_infinite() {
929 return Err("numeric infinity is not supported".into());
930 }
931 Ok(Value::Numeric(Numeric(rescale_numeric(
932 n.0,
933 constraints.as_ref(),
934 )?)))
935 }
936 Type::Oid | Type::RegClass | Type::RegProc | Type::RegType => {
937 u32::from_sql(ty.inner(), raw).map(Value::Oid)
938 }
939 Type::Record(_) => Err("input of anonymous composite types is not implemented".into()),
940 Type::Text => decode_binary_string(ty, raw).map(Value::Text),
941 Type::BpChar { .. } => decode_binary_string(ty, raw).map(Value::BpChar),
942 Type::VarChar { .. } => decode_binary_string(ty, raw).map(Value::VarChar),
943 Type::Time { .. } => {
944 const USECS_PER_DAY: i64 = 24 * 60 * 60 * 1_000_000;
951 let usecs = i64::from_sql(ty.inner(), raw)?;
952 if !(0..USECS_PER_DAY).contains(&usecs) {
953 return Err("time out of range".into());
954 }
955 let secs = u32::try_from(usecs / 1_000_000).expect("less than 86,400");
956 let nanos = u32::try_from(usecs % 1_000_000).expect("less than 1,000,000") * 1_000;
957 Ok(Value::Time(
958 NaiveTime::from_num_seconds_from_midnight_opt(secs, nanos)
959 .expect("validated against USECS_PER_DAY"),
960 ))
961 }
962 Type::TimeTz { .. } => Err("input of timetz types is not implemented".into()),
963 Type::Timestamp { .. } => {
964 let ts = NaiveDateTime::from_sql(ty.inner(), raw)?;
965 Ok(Value::Timestamp(CheckedTimestamp::from_timestamplike(ts)?))
966 }
967 Type::TimestampTz { .. } => {
968 let ts = DateTime::<Utc>::from_sql(ty.inner(), raw)?;
969 Ok(Value::TimestampTz(CheckedTimestamp::from_timestamplike(
970 ts,
971 )?))
972 }
973 Type::Uuid => Uuid::from_sql(ty.inner(), raw).map(Value::Uuid),
974 Type::MzTimestamp => {
975 let s = String::from_sql(ty.inner(), raw)?;
976 let t: mz_repr::Timestamp = s.parse()?;
977 Ok(Value::MzTimestamp(t))
978 }
979 Type::Range { .. } => Err("binary decoding of range types is not implemented".into()),
980 Type::MzAclItem => {
981 let mz_acl_item = MzAclItem::decode_binary(raw)?;
982 Ok(Value::MzAclItem(mz_acl_item))
983 }
984 Type::AclItem => Err("aclitem has no binary encoding".into()),
985 }
986 }
987}
988
989#[derive(Debug, Clone, Copy, PartialEq, Eq)]
995pub struct TextEncodeSettings {
996 pub extra_float_digits: i32,
1001}
1002
1003impl TextEncodeSettings {
1004 pub const STABLE: TextEncodeSettings = TextEncodeSettings {
1006 extra_float_digits: 1,
1007 };
1008}
1009
1010const FLOAT4_DIGITS: i32 = 6;
1013const FLOAT8_DIGITS: i32 = 15;
1014
1015fn format_float_limited(buf: &mut BytesMut, f: f64, ndig: i32) -> Nestable {
1020 if f.is_nan() {
1021 buf.write_str("NaN");
1022 return Nestable::Yes;
1023 }
1024 if f.is_infinite() {
1025 buf.write_str(if f < 0.0 { "-Infinity" } else { "Infinity" });
1026 return Nestable::Yes;
1027 }
1028 let ndig = ndig.max(1);
1029 let prec = usize::try_from(ndig - 1).expect("ndig is at least 1");
1033 let sci = format!("{:.prec$e}", f);
1034 let (mantissa, exp) = sci.split_once('e').expect("e format has an exponent");
1035 let exp: i32 = exp.parse().expect("valid exponent");
1036 if exp < -4 || exp >= ndig {
1037 let mantissa = mantissa.trim_end_matches('0').trim_end_matches('.');
1040 write!(buf, "{mantissa}e{exp:+03}");
1041 } else {
1042 let decimals = usize::try_from(ndig - 1 - exp).expect("exp is less than ndig");
1046 let fixed = format!("{f:.decimals$}");
1047 let fixed = match fixed.contains('.') {
1048 true => fixed.trim_end_matches('0').trim_end_matches('.'),
1049 false => &fixed,
1050 };
1051 buf.write_str(fixed);
1052 }
1053 Nestable::Yes
1054}
1055
1056fn reject_nul(s: &str) -> Result<(), Box<dyn Error + Sync + Send>> {
1059 if s.contains('\0') {
1060 Err(Box::new(NulCharacterError))
1061 } else {
1062 Ok(())
1063 }
1064}
1065
1066fn decode_binary_string(ty: &Type, raw: &[u8]) -> Result<String, Box<dyn Error + Sync + Send>> {
1068 let s = String::from_sql(ty.inner(), raw)?;
1069 reject_nul(&s)?;
1070 Ok(s)
1071}
1072
1073fn rescale_numeric(
1075 mut n: OrderedDecimal<mz_repr_numeric::Numeric>,
1076 constraints: Option<&NumericConstraints>,
1077) -> Result<OrderedDecimal<mz_repr_numeric::Numeric>, Box<dyn Error + Sync + Send>> {
1078 if let Some(constraints) = constraints {
1079 rescale(
1080 &mut n.0,
1081 NumericMaxScale::try_from(i64::from(constraints.max_scale()))?.into_u8(),
1082 )?;
1083 }
1084 Ok(n)
1085}
1086
1087fn encode_element(buf: &mut BytesMut, elem: Option<&Value>, ty: &Type) -> Result<(), io::Error> {
1088 match elem {
1089 None => buf.put_i32(-1),
1090 Some(elem) => {
1091 let base = buf.len();
1092 buf.put_i32(0);
1093 elem.encode_binary(ty, buf)?;
1094 let len = pg_len("encoded element", buf.len() - base - 4)?;
1095 buf[base..base + 4].copy_from_slice(&len.to_be_bytes());
1096 }
1097 }
1098 Ok(())
1099}
1100
1101fn pg_len(what: &str, len: usize) -> Result<i32, io::Error> {
1102 len.try_into().map_err(|_| {
1103 io::Error::new(
1104 io::ErrorKind::InvalidData,
1105 format!("{} does not fit into an i32", what),
1106 )
1107 })
1108}
1109
1110pub fn values_from_row(row: &RowRef, typ: &SqlRelationType) -> Vec<Option<Value>> {
1115 row.iter()
1116 .zip_eq(typ.column_types.iter())
1117 .map(|(col, typ)| Value::from_datum(col, &typ.scalar_type))
1118 .collect()
1119}
1120
1121#[cfg(test)]
1122mod tests {
1123 use mz_repr::arb_datum_for_scalar;
1124 use proptest::prelude::*;
1125
1126 use super::*;
1127
1128 #[mz_ore::test]
1136 #[cfg_attr(miri, ignore)] fn proptest_binary_encoding_error_matches_encode_binary() {
1138 let strat =
1139 any::<SqlScalarType>().prop_flat_map(|ty| (Just(ty.clone()), arb_datum_for_scalar(ty)));
1140 proptest!(ProptestConfig::with_cases(256), |((ty, prop_datum) in strat)| {
1141 if Value::binary_encoding_error(&ty).is_err() {
1145 return Ok(());
1146 }
1147 let datum = Datum::from(&prop_datum);
1148 let value = match Value::from_datum(datum, &ty) {
1149 Some(v) => v,
1150 None => return Ok(()),
1152 };
1153 let pg_ty = Type::from(&ty);
1154 let mut buf = BytesMut::new();
1155 value
1156 .encode_binary(&pg_ty, &mut buf)
1157 .expect("encode_binary must succeed when binary_encoding_error returns Ok");
1158 });
1159 }
1160
1161 #[mz_ore::test]
1165 fn format_float_limited_matches_printf_g() {
1166 for (f, ndig, expected) in [
1167 (0.1_f64 + 0.2_f64, 15, "0.3"),
1168 (0.1_f64 + 0.2_f64, 1, "0.3"),
1169 (f64::from(123.45679_f32), 6, "123.457"),
1170 (f64::from(123.45679_f32), 3, "123"),
1171 (1e15, 15, "1e+15"),
1172 (-123456.0, 3, "-1.23e+05"),
1173 (999.999, 3, "1e+03"),
1174 (0.0001, 15, "0.0001"),
1175 (-0.00001, 15, "-1e-05"),
1176 (0.0, 15, "0"),
1177 (-0.0, 15, "-0"),
1178 (100.0, 15, "100"),
1179 (1.23456789012345, 12, "1.23456789012"),
1180 (f64::NAN, 15, "NaN"),
1181 (f64::INFINITY, 15, "Infinity"),
1182 (f64::NEG_INFINITY, -100, "-Infinity"),
1183 (0.1_f64 + 0.2_f64, -5, "0.3"),
1185 ] {
1186 let mut buf = BytesMut::new();
1187 format_float_limited(&mut buf, f, ndig);
1188 assert_eq!(
1189 str::from_utf8(&buf).unwrap(),
1190 expected,
1191 "{f} with {ndig} digits"
1192 );
1193 }
1194 }
1195
1196 #[mz_ore::test]
1198 fn decode_text_error_smoke_test() {
1199 let bool_array = Value::Array {
1200 dims: vec![ArrayDimension {
1201 lower_bound: 0,
1202 length: 1,
1203 }],
1204 elements: vec![Some(Value::Bool(true))],
1205 };
1206
1207 let mut buf = BytesMut::new();
1208 bool_array.encode_text(&mut buf, TextEncodeSettings::STABLE);
1209 let buf = buf.to_vec();
1210
1211 let int_array_tpe = Type::Array(Box::new(Type::Int4));
1212 let decoded_int_array = Value::decode_text(&int_array_tpe, &buf);
1213
1214 assert_eq!(
1215 decoded_int_array.map_err(|e| e.to_string()).unwrap_err(),
1216 "invalid input syntax for type array: Specifying array lower bounds is not supported: \"[0:0]={t}\"".to_string()
1217 );
1218 }
1219
1220 #[mz_ore::test]
1226 #[cfg_attr(miri, ignore)] fn decode_numeric_applies_destination_scale() {
1228 let ty = Type::from(&SqlScalarType::Numeric {
1230 max_scale: Some(NumericMaxScale::try_from(2_i64).unwrap()),
1231 });
1232 let expected = strconv::parse_numeric("10.45").unwrap();
1233
1234 let input = Value::Numeric(Numeric(strconv::parse_numeric("10.447").unwrap()));
1237 let mut buf = BytesMut::new();
1238 input
1239 .encode_binary(&ty, &mut buf)
1240 .expect("encoding 10.447 as numeric must succeed");
1241 let Value::Numeric(Numeric(binary)) = Value::decode_binary(&ty, &buf).unwrap() else {
1242 panic!("decode_binary of a numeric must yield Value::Numeric");
1243 };
1244 assert_eq!(binary, expected, "binary decode did not rescale to scale 2");
1245
1246 let Value::Numeric(Numeric(text)) = Value::decode_text(&ty, b"10.447").unwrap() else {
1248 panic!("decode_text of a numeric must yield Value::Numeric");
1249 };
1250 assert_eq!(text, expected, "text decode did not rescale to scale 2");
1251 }
1252
1253 #[mz_ore::test]
1256 #[cfg_attr(miri, ignore)] fn decode_binary_numeric_rejects_infinity() {
1258 let ty = Type::Numeric { constraints: None };
1259 let header = |sign: u16| {
1261 let mut b = Vec::new();
1262 b.extend_from_slice(&0i16.to_be_bytes());
1263 b.extend_from_slice(&0i16.to_be_bytes());
1264 b.extend_from_slice(&sign.to_be_bytes());
1265 b.extend_from_slice(&0i16.to_be_bytes());
1266 b
1267 };
1268 for sign in [0xD000, 0xF000] {
1270 let res = Value::decode_binary(&ty, &header(sign));
1271 assert_eq!(
1272 res.map(|_| ()).map_err(|e| e.to_string()).unwrap_err(),
1273 "numeric infinity is not supported",
1274 "sign {sign:#x} must be rejected",
1275 );
1276 }
1277 let Value::Numeric(Numeric(nan)) = Value::decode_binary(&ty, &header(0xC000)).unwrap()
1279 else {
1280 panic!("decode_binary of a numeric must yield Value::Numeric");
1281 };
1282 assert_eq!(nan, strconv::parse_numeric("NaN").unwrap());
1283 }
1284
1285 #[mz_ore::test]
1289 fn decode_binary_time_rejects_out_of_range() {
1290 const USECS_PER_DAY: i64 = 24 * 60 * 60 * 1_000_000;
1291 let ty = Type::Time { precision: None };
1292
1293 for usecs in [USECS_PER_DAY, USECS_PER_DAY + 1, -1, i64::MIN, i64::MAX] {
1294 let res = Value::decode_binary(&ty, &usecs.to_be_bytes());
1295 assert_eq!(
1296 res.map(|_| ()).map_err(|e| e.to_string()).unwrap_err(),
1297 "time out of range",
1298 "{usecs} microseconds must be rejected",
1299 );
1300 }
1301
1302 let Value::Time(t) = Value::decode_binary(&ty, &(USECS_PER_DAY - 1).to_be_bytes()).unwrap()
1303 else {
1304 panic!("decoding a time value must yield Value::Time");
1305 };
1306 assert_eq!(
1307 t,
1308 NaiveTime::from_hms_micro_opt(23, 59, 59, 999_999).unwrap()
1309 );
1310 }
1311
1312 #[mz_ore::test]
1314 fn decode_rejects_nul_in_strings() {
1315 const NUL_ERR: &str = "invalid byte sequence for encoding \"UTF8\": 0x00";
1316 let raw = b"foo\x00bar";
1317
1318 for ty in [
1319 Type::Text,
1320 Type::BpChar { length: None },
1321 Type::VarChar { max_length: None },
1322 Type::Name,
1323 ] {
1324 for format in [Format::Text, Format::Binary] {
1325 let res = Value::decode(format, &ty, raw);
1326 assert_eq!(
1327 res.map(|_| ()).map_err(|e| e.to_string()).unwrap_err(),
1328 NUL_ERR,
1329 "{ty:?} in {format:?} format must reject NUL",
1330 );
1331 }
1332 }
1333
1334 let res = Value::decode_text(&Type::Bytea, b"\\x00\x00");
1336 assert_eq!(
1337 res.map(|_| ()).map_err(|e| e.to_string()).unwrap_err(),
1338 NUL_ERR,
1339 );
1340
1341 let Value::Text(s) = Value::decode(Format::Binary, &Type::Text, b"foobar").unwrap() else {
1343 panic!("decoding a text value must yield Value::Text");
1344 };
1345 assert_eq!(s, "foobar");
1346 }
1347}