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, regproc};
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 RegProc(u32),
101 Record(Vec<Option<Value>>),
103 Time(NaiveTime),
105 Timestamp(CheckedTimestamp<NaiveDateTime>),
107 TimestampTz(CheckedTimestamp<DateTime<Utc>>),
109 Text(String),
111 BpChar(String),
113 VarChar(String),
115 Uuid(Uuid),
117 Int2Vector {
119 elements: Vec<Option<Value>>,
121 },
122 MzTimestamp(mz_repr::Timestamp),
124 Range(Range<Box<Value>>),
126 MzAclItem(MzAclItem),
129 AclItem(AclItem),
132}
133
134impl Value {
135 pub fn from_datum(datum: Datum, typ: &SqlScalarType) -> Option<Value> {
140 match (datum, typ) {
141 (Datum::Null, _) => None,
142 (Datum::True, SqlScalarType::Bool) => Some(Value::Bool(true)),
143 (Datum::False, SqlScalarType::Bool) => Some(Value::Bool(false)),
144 (Datum::Int16(i), SqlScalarType::Int16) => Some(Value::Int2(i)),
145 (Datum::Int32(i), SqlScalarType::Int32) => Some(Value::Int4(i)),
146 (Datum::Int64(i), SqlScalarType::Int64) => Some(Value::Int8(i)),
147 (Datum::UInt8(c), SqlScalarType::PgLegacyChar) => Some(Value::Char(c)),
148 (Datum::UInt16(u), SqlScalarType::UInt16) => Some(Value::UInt2(UInt2(u))),
149 (Datum::UInt32(oid), SqlScalarType::Oid) => Some(Value::Oid(oid)),
150 (Datum::UInt32(oid), SqlScalarType::RegProc) => Some(Value::RegProc(oid)),
151 (Datum::UInt32(oid), SqlScalarType::RegClass) => Some(Value::Oid(oid)),
156 (Datum::UInt32(oid), SqlScalarType::RegType) => Some(Value::Oid(oid)),
157 (Datum::UInt32(u), SqlScalarType::UInt32) => Some(Value::UInt4(UInt4(u))),
158 (Datum::UInt64(u), SqlScalarType::UInt64) => Some(Value::UInt8(UInt8(u))),
159 (Datum::Float32(f), SqlScalarType::Float32) => Some(Value::Float4(*f)),
160 (Datum::Float64(f), SqlScalarType::Float64) => Some(Value::Float8(*f)),
161 (Datum::Numeric(d), SqlScalarType::Numeric { .. }) => Some(Value::Numeric(Numeric(d))),
162 (Datum::MzTimestamp(t), SqlScalarType::MzTimestamp) => Some(Value::MzTimestamp(t)),
163 (Datum::MzAclItem(mai), SqlScalarType::MzAclItem) => Some(Value::MzAclItem(mai)),
164 (Datum::AclItem(ai), SqlScalarType::AclItem) => Some(Value::AclItem(ai)),
165 (Datum::Date(d), SqlScalarType::Date) => Some(Value::Date(d)),
166 (Datum::Time(t), SqlScalarType::Time) => Some(Value::Time(t)),
167 (Datum::Timestamp(ts), SqlScalarType::Timestamp { .. }) => Some(Value::Timestamp(ts)),
168 (Datum::TimestampTz(ts), SqlScalarType::TimestampTz { .. }) => {
169 Some(Value::TimestampTz(ts))
170 }
171 (Datum::Interval(iv), SqlScalarType::Interval) => Some(Value::Interval(Interval(iv))),
172 (Datum::Bytes(b), SqlScalarType::Bytes) => Some(Value::Bytea(b.to_vec())),
173 (Datum::String(s), SqlScalarType::String) => Some(Value::Text(s.to_owned())),
174 (Datum::String(s), SqlScalarType::VarChar { .. }) => Some(Value::VarChar(s.to_owned())),
175 (Datum::String(s), SqlScalarType::Char { length }) => {
176 Some(Value::BpChar(char::format_str_pad(s, *length)))
177 }
178 (Datum::String(s), SqlScalarType::PgLegacyName) => Some(Value::Name(s.into())),
179 (_, SqlScalarType::Jsonb) => {
180 Some(Value::Jsonb(Jsonb(JsonbRef::from_datum(datum).to_owned())))
181 }
182 (Datum::Uuid(u), SqlScalarType::Uuid) => Some(Value::Uuid(u)),
183 (Datum::Array(array), SqlScalarType::Array(elem_type)) => {
184 let dims = array.dims().into_iter().collect();
185 let elements = array
186 .elements()
187 .iter()
188 .map(|elem| Value::from_datum(elem, elem_type))
189 .collect();
190 Some(Value::Array { dims, elements })
191 }
192 (Datum::Array(array), SqlScalarType::Int2Vector) => {
193 assert!(
194 array.has_int2vector_dims(),
195 "int2vector must be 1 dimensional, or empty"
196 );
197 let elements = array
198 .elements()
199 .iter()
200 .map(|elem| Value::from_datum(elem, &SqlScalarType::Int16))
201 .collect();
202 Some(Value::Int2Vector { elements })
203 }
204 (Datum::List(list), SqlScalarType::List { element_type, .. }) => {
205 let elements = list
206 .iter()
207 .map(|elem| Value::from_datum(elem, element_type))
208 .collect();
209 Some(Value::List(elements))
210 }
211 (Datum::List(record), SqlScalarType::Record { fields, .. }) => {
212 let fields = record
213 .iter()
214 .zip_eq(fields)
215 .map(|(e, (_name, ty))| Value::from_datum(e, &ty.scalar_type))
216 .collect();
217 Some(Value::Record(fields))
218 }
219 (Datum::Map(dict), SqlScalarType::Map { value_type, .. }) => {
220 let entries = dict
221 .iter()
222 .map(|(k, v)| (k.to_owned(), Value::from_datum(v, value_type)))
223 .collect();
224 Some(Value::Map(entries))
225 }
226 (Datum::Range(range), SqlScalarType::Range { element_type }) => {
227 let value_range = range.into_bounds(|b| {
228 Box::new(
229 Value::from_datum(b.datum(), element_type)
230 .expect("RangeBounds never contain Datum::Null"),
231 )
232 });
233 Some(Value::Range(value_range))
234 }
235 _ => panic!("can't serialize {}::{:?}", datum, typ),
236 }
237 }
238
239 pub fn into_datum<'a>(
241 self,
242 buf: &'a RowArena,
243 typ: &Type,
244 ) -> Result<Datum<'a>, IntoDatumError> {
245 Ok(match self {
246 Value::Array { dims, elements } => {
247 let element_pg_type = match typ {
248 Type::Array(t) => &*t,
249 _ => panic!("Value::Array should have type Type::Array. Found {:?}", typ),
250 };
251 let elements: Result<Vec<_>, _> = elements
252 .into_iter()
253 .map(|element| match element {
254 Some(element) => element.into_datum(buf, element_pg_type),
255 None => Ok(Datum::Null),
256 })
257 .collect();
258 let elements = elements?;
259 buf.try_make_datum(|packer| {
260 packer
261 .try_push_array(&dims, elements)
262 .map_err(IntoDatumError::from)
263 })?
264 }
265 Value::Int2Vector { .. } => {
266 unreachable!("into_datum cannot be called on Value::Int2Vector");
269 }
270 Value::Bool(true) => Datum::True,
271 Value::Bool(false) => Datum::False,
272 Value::Bytea(b) => Datum::Bytes(buf.push_bytes(b)),
273 Value::Char(c) => Datum::UInt8(c),
274 Value::Date(d) => Datum::Date(d),
275 Value::Float4(f) => Datum::Float32(f.into()),
276 Value::Float8(f) => Datum::Float64(f.into()),
277 Value::Int2(i) => Datum::Int16(i),
278 Value::Int4(i) => Datum::Int32(i),
279 Value::Int8(i) => Datum::Int64(i),
280 Value::UInt2(u) => Datum::UInt16(u.0),
281 Value::UInt4(u) => Datum::UInt32(u.0),
282 Value::UInt8(u) => Datum::UInt64(u.0),
283 Value::Jsonb(js) => buf.push_unary_row(js.0.into_row()),
284 Value::List(elems) => {
285 let elem_pg_type = match typ {
286 Type::List(t) => &*t,
287 _ => panic!("Value::List should have type Type::List. Found {:?}", typ),
288 };
289 let elems: Result<Vec<_>, _> = elems
290 .into_iter()
291 .map(|elem| match elem {
292 Some(elem) => elem.into_datum(buf, elem_pg_type),
293 None => Ok(Datum::Null),
294 })
295 .collect();
296 let elems = elems?;
297 buf.make_datum(|packer| packer.push_list(elems))
298 }
299 Value::Map(map) => {
300 let elem_pg_type = match typ {
301 Type::Map { value_type } => &*value_type,
302 _ => panic!("Value::Map should have type Type::Map. Found {:?}", typ),
303 };
304 buf.try_make_datum(|packer| {
305 packer.try_push_dict_with(|row| {
306 for (k, v) in map {
307 row.push(Datum::String(buf.push_string(k)));
308 let datum = match v {
309 Some(elem) => elem.into_datum(buf, elem_pg_type)?,
310 None => Datum::Null,
311 };
312 row.push(datum);
313 }
314 Ok::<_, IntoDatumError>(())
315 })
316 })?
317 }
318 Value::Oid(oid) | Value::RegProc(oid) => Datum::UInt32(oid),
319 Value::Record(_) => {
320 unreachable!("into_datum cannot be called on Value::Record");
323 }
324 Value::Time(t) => Datum::Time(t),
325 Value::Timestamp(ts) => Datum::Timestamp(ts),
326 Value::TimestampTz(ts) => Datum::TimestampTz(ts),
327 Value::Interval(iv) => Datum::Interval(iv.0),
328 Value::Text(s) | Value::VarChar(s) | Value::Name(s) => {
329 Datum::String(buf.push_string(s))
330 }
331 Value::BpChar(s) => Datum::String(buf.push_string(s.trim_end().into())),
332 Value::Uuid(u) => Datum::Uuid(u),
333 Value::Numeric(n) => Datum::Numeric(n.0),
334 Value::MzTimestamp(t) => Datum::MzTimestamp(t),
335 Value::Range(range) => {
336 let elem_pg_type = match typ {
337 Type::Range { element_type } => &*element_type,
338 _ => panic!("Value::Range should have type Type::Range. Found {:?}", typ),
339 };
340 let range = range.try_into_bounds(|elem| elem.into_datum(buf, elem_pg_type))?;
341 buf.try_make_datum(|packer| packer.push_range(range).map_err(IntoDatumError::from))?
342 }
343 Value::MzAclItem(mz_acl_item) => Datum::MzAclItem(mz_acl_item),
344 Value::AclItem(acl_item) => Datum::AclItem(acl_item),
345 })
346 }
347
348 pub fn into_datum_decode_error<'a>(
353 self,
354 buf: &'a RowArena,
355 typ: &Type,
356 context: &str,
357 ) -> Result<Datum<'a>, String> {
358 self.into_datum(buf, typ)
359 .map_err(|e| format!("unable to decode {}: {}", context, e))
360 }
361
362 pub fn encode(
366 &self,
367 ty: &Type,
368 format: Format,
369 buf: &mut BytesMut,
370 settings: TextEncodeSettings,
371 ) -> Result<(), io::Error> {
372 match format {
373 Format::Text => {
374 self.encode_text(buf, settings);
375 Ok(())
376 }
377 Format::Binary => self.encode_binary(ty, buf),
378 }
379 }
380
381 pub fn encode_text(&self, buf: &mut BytesMut, settings: TextEncodeSettings) -> Nestable {
384 let extra_float_digits = settings.extra_float_digits;
385 match self {
386 Value::Array { dims, elements } => {
387 strconv::format_array(buf, dims, elements, |buf, elem| match elem {
388 None => Ok::<_, ()>(buf.write_null()),
389 Some(elem) => Ok(elem.encode_text(buf.nonnull_buffer(), settings)),
390 })
391 .expect("provided closure never fails")
392 }
393 Value::Int2Vector { elements } => {
394 strconv::format_legacy_vector(buf, elements, |buf, elem| {
395 Ok::<_, ()>(
396 elem.as_ref()
397 .expect("Int2Vector does not support NULL values")
398 .encode_text(buf.nonnull_buffer(), settings),
399 )
400 })
401 .expect("provided closure never fails")
402 }
403 Value::Bool(b) => strconv::format_bool(buf, *b),
404 Value::Bytea(b) => strconv::format_bytes(buf, b),
405 Value::Char(c) => {
406 buf.put_u8(*c);
407 Nestable::MayNeedEscaping
408 }
409 Value::Date(d) => strconv::format_date(buf, *d),
410 Value::Int2(i) => strconv::format_int16(buf, *i),
411 Value::Int4(i) => strconv::format_int32(buf, *i),
412 Value::Int8(i) => strconv::format_int64(buf, *i),
413 Value::UInt2(u) => strconv::format_uint16(buf, u.0),
414 Value::UInt4(u) => strconv::format_uint32(buf, u.0),
415 Value::UInt8(u) => strconv::format_uint64(buf, u.0),
416 Value::Interval(iv) => strconv::format_interval(buf, iv.0),
417 Value::Float4(f) if extra_float_digits > 0 => strconv::format_float32(buf, *f),
418 Value::Float4(f) => {
419 format_float_limited(buf, f64::from(*f), FLOAT4_DIGITS + extra_float_digits)
420 }
421 Value::Float8(f) if extra_float_digits > 0 => strconv::format_float64(buf, *f),
422 Value::Float8(f) => format_float_limited(buf, *f, FLOAT8_DIGITS + extra_float_digits),
423 Value::Jsonb(js) => strconv::format_jsonb(buf, js.0.as_ref()),
424 Value::List(elems) => strconv::format_list(buf, elems, |buf, elem| match elem {
425 None => Ok::<_, ()>(buf.write_null()),
426 Some(elem) => Ok(elem.encode_text(buf.nonnull_buffer(), settings)),
427 })
428 .expect("provided closure never fails"),
429 Value::Map(elems) => strconv::format_map(buf, elems, |buf, value| match value {
430 None => Ok::<_, ()>(buf.write_null()),
431 Some(elem) => Ok(elem.encode_text(buf.nonnull_buffer(), settings)),
432 })
433 .expect("provided closure never fails"),
434 Value::Oid(oid) => strconv::format_uint32(buf, *oid),
435 Value::RegProc(oid) => match *oid {
437 0 => strconv::format_string(buf, REGPROC_NULL),
438 oid => match regproc::name(oid) {
439 Some(name) => strconv::format_string(buf, name),
440 None => strconv::format_uint32(buf, oid),
441 },
442 },
443 Value::Record(elems) => strconv::format_record(buf, elems, |buf, elem| match elem {
444 None => Ok::<_, ()>(buf.write_null()),
445 Some(elem) => Ok(elem.encode_text(buf.nonnull_buffer(), settings)),
446 })
447 .expect("provided closure never fails"),
448 Value::Text(s) | Value::VarChar(s) | Value::BpChar(s) | Value::Name(s) => {
449 strconv::format_string(buf, s)
450 }
451 Value::Time(t) => strconv::format_time(buf, *t),
452 Value::Timestamp(ts) => strconv::format_timestamp(buf, ts),
453 Value::TimestampTz(ts) => strconv::format_timestamptz(buf, ts),
454 Value::Uuid(u) => strconv::format_uuid(buf, *u),
455 Value::Numeric(d) => strconv::format_numeric(buf, &d.0),
456 Value::MzTimestamp(t) => strconv::format_mz_timestamp(buf, *t),
457 Value::Range(range) => strconv::format_range(buf, range, |buf, elem| match elem {
458 Some(elem) => Ok(elem.encode_text(buf.nonnull_buffer(), settings)),
459 None => Ok::<_, ()>(buf.write_null()),
460 })
461 .expect("provided closure never fails"),
462 Value::MzAclItem(mz_acl_item) => strconv::format_mz_acl_item(buf, *mz_acl_item),
463 Value::AclItem(acl_item) => strconv::format_acl_item(buf, *acl_item),
464 }
465 }
466
467 pub fn encode_binary(&self, ty: &Type, buf: &mut BytesMut) -> Result<(), io::Error> {
470 let is_null = match self {
473 Value::Array { dims, elements } => {
474 let ndims = pg_len("number of array dimensions", dims.len())?;
475 let has_null = elements.iter().any(|e| e.is_none());
476 let elem_type = match ty {
477 Type::Array(elem_type) => elem_type,
478 _ => unreachable!(),
479 };
480 buf.put_i32(ndims);
481 buf.put_i32(has_null.into());
482 buf.put_u32(elem_type.oid());
483 for dim in dims {
484 buf.put_i32(pg_len("array dimension length", dim.length)?);
485 buf.put_i32(dim.lower_bound.try_into().map_err(|_| {
486 io::Error::new(
487 io::ErrorKind::InvalidData,
488 "array dimension lower bound does not fit into an i32",
489 )
490 })?);
491 }
492 for elem in elements {
493 encode_element(buf, elem.as_ref(), elem_type)?;
494 }
495 Ok(postgres_types::IsNull::No)
496 }
497 Value::Int2Vector { elements } => {
498 let has_null = elements.iter().any(|e| e.is_none());
501 buf.put_i32(1);
502 buf.put_i32(has_null.into());
503 buf.put_u32(TYPE_INT2_OID);
504 buf.put_i32(pg_len("int2vector dimension length", elements.len())?);
505 buf.put_i32(0);
506 for elem in elements {
507 encode_element(buf, elem.as_ref(), &Type::Int2)?;
508 }
509 Ok(postgres_types::IsNull::No)
510 }
511 Value::Bool(b) => b.to_sql(&PgType::BOOL, buf),
512 Value::Bytea(b) => b.to_sql(&PgType::BYTEA, buf),
513 Value::Char(c) => i8::reinterpret_cast(*c).to_sql(&PgType::CHAR, buf),
514 Value::Date(d) => d.pg_epoch_days().to_sql(&PgType::DATE, buf),
515 Value::Float4(f) => f.to_sql(&PgType::FLOAT4, buf),
516 Value::Float8(f) => f.to_sql(&PgType::FLOAT8, buf),
517 Value::Int2(i) => i.to_sql(&PgType::INT2, buf),
518 Value::Int4(i) => i.to_sql(&PgType::INT4, buf),
519 Value::Int8(i) => i.to_sql(&PgType::INT8, buf),
520 Value::UInt2(u) => u.to_sql(&*UINT2, buf),
521 Value::UInt4(u) => u.to_sql(&*UINT4, buf),
522 Value::UInt8(u) => u.to_sql(&*UINT8, buf),
523 Value::Interval(iv) => iv.to_sql(&PgType::INTERVAL, buf),
524 Value::Jsonb(js) => js.to_sql(&PgType::JSONB, buf),
525 Value::List(_) => {
526 Err("binary encoding of list types is not implemented".into())
557 }
558 Value::Map(_) => {
559 Err("binary encoding of map types is not implemented".into())
563 }
564 Value::Name(s) => s.to_sql(&PgType::NAME, buf),
565 Value::Oid(i) | Value::RegProc(i) => i.to_sql(&PgType::OID, buf),
568 Value::Record(fields) => {
569 let nfields = pg_len("record field length", fields.len())?;
570 buf.put_i32(nfields);
571 let field_types = match ty {
572 Type::Record(fields) => fields,
573 _ => unreachable!(),
574 };
575 for (f, ty) in fields.iter().zip_eq(field_types) {
576 buf.put_u32(ty.oid());
577 encode_element(buf, f.as_ref(), ty)?;
578 }
579 Ok(postgres_types::IsNull::No)
580 }
581 Value::Text(s) => s.to_sql(&PgType::TEXT, buf),
582 Value::BpChar(s) => s.to_sql(&PgType::BPCHAR, buf),
583 Value::VarChar(s) => s.to_sql(&PgType::VARCHAR, buf),
584 Value::Time(t) => t.to_sql(&PgType::TIME, buf),
585 Value::Timestamp(ts) => ts.to_sql(&PgType::TIMESTAMP, buf),
586 Value::TimestampTz(ts) => ts.to_sql(&PgType::TIMESTAMPTZ, buf),
587 Value::Uuid(u) => u.to_sql(&PgType::UUID, buf),
588 Value::Numeric(a) => a.to_sql(&PgType::NUMERIC, buf),
589 Value::MzTimestamp(t) => t.to_string().to_sql(&PgType::TEXT, buf),
590 Value::Range(range) => {
591 buf.put_u8(range.pg_flag_bits());
592
593 let elem_type = match ty {
594 Type::Range { element_type } => element_type,
595 _ => unreachable!(),
596 };
597
598 if let Some(RangeInner { lower, upper }) = &range.inner {
599 for bound in [&lower.bound, &upper.bound] {
600 if let Some(bound) = bound {
601 let base = buf.len();
602 buf.put_i32(0);
603 bound.encode_binary(elem_type, buf)?;
604 let len = pg_len("encoded range bound", buf.len() - base - 4)?;
605 buf[base..base + 4].copy_from_slice(&len.to_be_bytes());
606 }
607 }
608 }
609 Ok(postgres_types::IsNull::No)
610 }
611 Value::MzAclItem(mz_acl_item) => {
612 buf.extend_from_slice(&mz_acl_item.encode_binary());
613 Ok(postgres_types::IsNull::No)
614 }
615 Value::AclItem(_) => Err("aclitem has no binary encoding".into()),
616 }
617 .expect("encode_binary should never trigger a to_sql failure");
618 if let IsNull::Yes = is_null {
619 panic!("encode_binary impossibly called on a null value")
620 }
621 Ok(())
622 }
623
624 pub fn binary_encoding_error(typ: &SqlScalarType) -> Result<(), &'static str> {
638 match typ {
639 SqlScalarType::Bool => Ok(()),
640 SqlScalarType::Int16 => Ok(()),
641 SqlScalarType::Int32 => Ok(()),
642 SqlScalarType::Int64 => Ok(()),
643 SqlScalarType::PgLegacyChar => Ok(()),
644 SqlScalarType::UInt16 => Ok(()),
645 SqlScalarType::Oid => Ok(()),
646 SqlScalarType::RegClass => Ok(()),
647 SqlScalarType::RegProc => Ok(()),
648 SqlScalarType::RegType => Ok(()),
649 SqlScalarType::UInt32 => Ok(()),
650 SqlScalarType::UInt64 => Ok(()),
651 SqlScalarType::Float32 => Ok(()),
652 SqlScalarType::Float64 => Ok(()),
653 SqlScalarType::Numeric { .. } => Ok(()),
654 SqlScalarType::MzTimestamp => Ok(()),
655 SqlScalarType::MzAclItem => Ok(()),
656 SqlScalarType::AclItem => Err("no binary output function available for type aclitem"),
657 SqlScalarType::Date => Ok(()),
658 SqlScalarType::Time => Ok(()),
659 SqlScalarType::Timestamp { .. } => Ok(()),
660 SqlScalarType::TimestampTz { .. } => Ok(()),
661 SqlScalarType::Interval => Ok(()),
662 SqlScalarType::Bytes => Ok(()),
663 SqlScalarType::String => Ok(()),
664 SqlScalarType::VarChar { .. } => Ok(()),
665 SqlScalarType::Char { .. } => Ok(()),
666 SqlScalarType::PgLegacyName => Ok(()),
667 SqlScalarType::Jsonb => Ok(()),
668 SqlScalarType::Uuid => Ok(()),
669 SqlScalarType::Array(elem_type) => Self::binary_encoding_error(elem_type),
670 SqlScalarType::Int2Vector => Ok(()),
671 SqlScalarType::List { .. } => Err("no binary output function available for type list"),
672 SqlScalarType::Map { .. } => Err("no binary output function available for type map"),
673 SqlScalarType::Record { fields, .. } => fields
674 .iter()
675 .try_for_each(|(_, ty)| Self::binary_encoding_error(&ty.scalar_type)),
676 SqlScalarType::Range { element_type } => Self::binary_encoding_error(element_type),
677 }
678 }
679
680 pub fn can_encode_binary(typ: &SqlScalarType) -> bool {
684 Self::binary_encoding_error(typ).is_ok()
685 }
686
687 pub fn decode(
690 format: Format,
691 ty: &Type,
692 raw: &[u8],
693 ) -> Result<Value, Box<dyn Error + Sync + Send>> {
694 match format {
695 Format::Text => Value::decode_text(ty, raw),
696 Format::Binary => Value::decode_binary(ty, raw),
697 }
698 }
699
700 pub fn decode_text<'a>(
703 ty: &'a Type,
704 raw: &'a [u8],
705 ) -> Result<Value, Box<dyn Error + Sync + Send>> {
706 let s = str::from_utf8(raw)?;
707 reject_nul(s)?;
710 Ok(match ty {
711 Type::Array(elem_type) => {
712 let (elements, dims) = strconv::parse_array(
713 s,
714 || None,
715 |elem_text| Value::decode_text(elem_type, elem_text.as_bytes()).map(Some),
716 )?;
717 Value::Array { dims, elements }
718 }
719 Type::Int2Vector { .. } => {
720 return Err("input of Int2Vector types is not implemented".into());
721 }
722 Type::Bool => Value::Bool(strconv::parse_bool(s)?),
723 Type::Bytea => Value::Bytea(strconv::parse_bytes(s)?),
724 Type::Char => Value::Char(raw.get(0).copied().unwrap_or(0)),
725 Type::Date => Value::Date(strconv::parse_date(s)?),
726 Type::Float4 => Value::Float4(strconv::parse_float32(s)?),
727 Type::Float8 => Value::Float8(strconv::parse_float64(s)?),
728 Type::Int2 => Value::Int2(strconv::parse_int16(s)?),
729 Type::Int4 => Value::Int4(strconv::parse_int32(s)?),
730 Type::Int8 => Value::Int8(strconv::parse_int64(s)?),
731 Type::UInt2 => Value::UInt2(UInt2(strconv::parse_uint16(s)?)),
732 Type::UInt4 => Value::UInt4(UInt4(strconv::parse_uint32(s)?)),
733 Type::UInt8 => Value::UInt8(UInt8(strconv::parse_uint64(s)?)),
734 Type::Interval { .. } => Value::Interval(Interval(strconv::parse_interval(s)?)),
735 Type::Json => return Err("input of json types is not implemented".into()),
736 Type::Jsonb => Value::Jsonb(Jsonb(strconv::parse_jsonb(s)?)),
737 Type::List(elem_type) => Value::List(strconv::parse_list(
738 s,
739 matches!(**elem_type, Type::List(..)),
740 || None,
741 |elem_text| Value::decode_text(elem_type, elem_text.as_bytes()).map(Some),
742 )?),
743 Type::Map { value_type } => Value::Map(strconv::parse_map(
744 s,
745 matches!(**value_type, Type::Map { .. }),
746 |elem_text| {
747 elem_text
748 .map(|t| Value::decode_text(value_type, t.as_bytes()))
749 .transpose()
750 },
751 )?),
752 Type::Name => Value::Name(strconv::parse_pg_legacy_name(s)),
753 Type::Numeric { constraints } => Value::Numeric(Numeric(rescale_numeric(
754 strconv::parse_numeric(s)?,
755 constraints.as_ref(),
756 )?)),
757 Type::Oid | Type::RegClass | Type::RegType => Value::Oid(strconv::parse_oid(s)?),
758 Type::RegProc => Value::RegProc(parse_regproc(s)?),
759 Type::Record(_) => {
760 return Err("input of anonymous composite types is not implemented".into());
761 }
762 Type::Text => Value::Text(s.to_owned()),
763 Type::BpChar { .. } => Value::BpChar(s.to_owned()),
764 Type::VarChar { .. } => Value::VarChar(s.to_owned()),
765 Type::Time { .. } => Value::Time(strconv::parse_time(s)?),
766 Type::TimeTz { .. } => return Err("input of timetz types is not implemented".into()),
767 Type::Timestamp { .. } => Value::Timestamp(strconv::parse_timestamp(s)?),
768 Type::TimestampTz { .. } => Value::TimestampTz(strconv::parse_timestamptz(s)?),
769 Type::Uuid => Value::Uuid(strconv::parse_uuid(s)?),
770 Type::MzTimestamp => Value::MzTimestamp(strconv::parse_mz_timestamp(s)?),
771 Type::Range { element_type } => Value::Range(strconv::parse_range(s, |elem_text| {
772 Value::decode_text(element_type, elem_text.as_bytes()).map(Box::new)
773 })?),
774 Type::MzAclItem => Value::MzAclItem(strconv::parse_mz_acl_item(s)?),
775 Type::AclItem => Value::AclItem(strconv::parse_acl_item(s)?),
776 })
777 }
778
779 pub fn decode_text_into_row<'a>(
781 ty: &'a Type,
782 s: &'a str,
783 packer: &mut RowPacker,
784 ) -> Result<(), Box<dyn Error + Sync + Send>> {
785 reject_nul(s)?;
788 Ok(match ty {
789 Type::Array(elem_type) => {
790 let (elements, dims) =
791 strconv::parse_array(s, || None, |elem_text| Ok::<_, String>(Some(elem_text)))?;
792 unsafe {
794 packer.push_array_with_unchecked(&dims, |packer| {
795 let mut nelements = 0;
796 for element in elements {
797 match element {
798 Some(elem_text) => {
799 Value::decode_text_into_row(elem_type, &elem_text, packer)?
800 }
801
802 None => packer.push(Datum::Null),
803 }
804 nelements += 1;
805 }
806 Ok::<_, Box<dyn Error + Sync + Send>>(nelements)
807 })?
808 }
809 }
810 Type::Int2Vector { .. } => {
811 return Err("input of Int2Vector types is not implemented".into());
812 }
813 Type::Bool => packer.push(Datum::from(strconv::parse_bool(s)?)),
814 Type::Bytea => packer.push(Datum::Bytes(&strconv::parse_bytes(s)?)),
815 Type::Char => packer.push(Datum::UInt8(s.as_bytes().get(0).copied().unwrap_or(0))),
816 Type::Date => packer.push(Datum::Date(strconv::parse_date(s)?)),
817 Type::Float4 => packer.push(Datum::Float32(strconv::parse_float32(s)?.into())),
818 Type::Float8 => packer.push(Datum::Float64(strconv::parse_float64(s)?.into())),
819 Type::Int2 => packer.push(Datum::Int16(strconv::parse_int16(s)?)),
820 Type::Int4 => packer.push(Datum::Int32(strconv::parse_int32(s)?)),
821 Type::Int8 => packer.push(Datum::Int64(strconv::parse_int64(s)?)),
822 Type::UInt2 => packer.push(Datum::UInt16(strconv::parse_uint16(s)?)),
823 Type::UInt4 => packer.push(Datum::UInt32(strconv::parse_uint32(s)?)),
824 Type::UInt8 => packer.push(Datum::UInt64(strconv::parse_uint64(s)?)),
825 Type::Interval { .. } => packer.push(Datum::Interval(strconv::parse_interval(s)?)),
826 Type::Json => return Err("input of json types is not implemented".into()),
827 Type::Jsonb => packer.push(strconv::parse_jsonb(s)?.into_row().unpack_first()),
828 Type::List(elem_type) => {
829 let elems = strconv::parse_list(
830 s,
831 matches!(**elem_type, Type::List(..)),
832 || None,
833 |elem_text| Ok::<_, String>(Some(elem_text)),
834 )?;
835 packer.push_list_with(|packer| {
836 for elem in elems {
837 match elem {
838 Some(elem) => Value::decode_text_into_row(elem_type, &elem, packer)?,
839 None => packer.push(Datum::Null),
840 }
841 }
842 Ok::<_, Box<dyn Error + Sync + Send>>(())
843 })?;
844 }
845 Type::Map { value_type } => {
846 let map =
847 strconv::parse_map(s, matches!(**value_type, Type::Map { .. }), |elem_text| {
848 elem_text.map(Ok::<_, String>).transpose()
849 })?;
850 packer.push_dict_with(|row| {
851 for (k, v) in map {
852 row.push(Datum::String(&k));
853 match v {
854 Some(elem) => Value::decode_text_into_row(value_type, &elem, row)?,
855 None => row.push(Datum::Null),
856 }
857 }
858 Ok::<_, Box<dyn Error + Sync + Send>>(())
859 })?;
860 }
861 Type::Name => packer.push(Datum::String(&strconv::parse_pg_legacy_name(s))),
862 Type::Numeric { constraints } => packer.push(Datum::Numeric(rescale_numeric(
863 strconv::parse_numeric(s)?,
864 constraints.as_ref(),
865 )?)),
866 Type::Oid | Type::RegClass | Type::RegType => {
867 packer.push(Datum::UInt32(strconv::parse_oid(s)?))
868 }
869 Type::RegProc => packer.push(Datum::UInt32(parse_regproc(s)?)),
870 Type::Record(_) => {
871 return Err("input of anonymous composite types is not implemented".into());
872 }
873 Type::Text => packer.push(Datum::String(s)),
874 Type::BpChar { .. } => packer.push(Datum::String(s.trim_end())),
875 Type::VarChar { .. } => packer.push(Datum::String(s)),
876 Type::Time { .. } => packer.push(Datum::Time(strconv::parse_time(s)?)),
877 Type::TimeTz { .. } => return Err("input of timetz types is not implemented".into()),
878 Type::Timestamp { .. } => packer.push(Datum::Timestamp(strconv::parse_timestamp(s)?)),
879 Type::TimestampTz { .. } => {
880 packer.push(Datum::TimestampTz(strconv::parse_timestamptz(s)?))
881 }
882 Type::Uuid => packer.push(Datum::Uuid(strconv::parse_uuid(s)?)),
883 Type::MzTimestamp => packer.push(Datum::MzTimestamp(strconv::parse_mz_timestamp(s)?)),
884 Type::Range { element_type } => {
885 let range = strconv::parse_range(s, |elem_text| {
886 Value::decode_text(element_type, elem_text.as_bytes()).map(Box::new)
887 })?;
888 let buf = RowArena::new();
891 let range = range
892 .try_into_bounds(|elem| elem.into_datum(&buf, element_type))
893 .map_err(Box::<dyn Error + Sync + Send>::from)?;
894 packer
895 .push_range(range)
896 .map_err(Box::<dyn Error + Sync + Send>::from)?;
897 }
898 Type::MzAclItem => packer.push(Datum::MzAclItem(strconv::parse_mz_acl_item(s)?)),
899 Type::AclItem => packer.push(Datum::AclItem(strconv::parse_acl_item(s)?)),
900 })
901 }
902
903 pub fn decode_binary(ty: &Type, raw: &[u8]) -> Result<Value, Box<dyn Error + Sync + Send>> {
906 match ty {
907 Type::Array(_) => Err("input of array types is not implemented".into()),
908 Type::Int2Vector => Err("input of int2vector types is not implemented".into()),
909 Type::Bool => bool::from_sql(ty.inner(), raw).map(Value::Bool),
910 Type::Bytea => Vec::<u8>::from_sql(ty.inner(), raw).map(Value::Bytea),
911 Type::Char => {
912 i8::from_sql(ty.inner(), raw).map(|c| Value::Char(u8::reinterpret_cast(c)))
913 }
914 Type::Date => {
915 let days = i32::from_sql(ty.inner(), raw)?;
916 Ok(Value::Date(Date::from_pg_epoch(days)?))
917 }
918 Type::Float4 => f32::from_sql(ty.inner(), raw).map(Value::Float4),
919 Type::Float8 => f64::from_sql(ty.inner(), raw).map(Value::Float8),
920 Type::Int2 => i16::from_sql(ty.inner(), raw).map(Value::Int2),
921 Type::Int4 => i32::from_sql(ty.inner(), raw).map(Value::Int4),
922 Type::Int8 => i64::from_sql(ty.inner(), raw).map(Value::Int8),
923 Type::UInt2 => UInt2::from_sql(ty.inner(), raw).map(Value::UInt2),
924 Type::UInt4 => UInt4::from_sql(ty.inner(), raw).map(Value::UInt4),
925 Type::UInt8 => UInt8::from_sql(ty.inner(), raw).map(Value::UInt8),
926 Type::Interval { .. } => Interval::from_sql(ty.inner(), raw).map(Value::Interval),
927 Type::Json => Err("input of json types is not implemented".into()),
928 Type::Jsonb => Jsonb::from_sql(ty.inner(), raw).map(Value::Jsonb),
929 Type::List(_) => Err("binary decoding of list types is not implemented".into()),
930 Type::Map { .. } => Err("binary decoding of map types is not implemented".into()),
931 Type::Name => {
932 let s = String::from_sql(ty.inner(), raw)?;
933 reject_nul(&s)?;
934 if s.len() > NAME_MAX_BYTES {
935 return Err("identifier too long".into());
936 }
937 Ok(Value::Name(s))
938 }
939 Type::Numeric { constraints } => {
940 let n = Numeric::from_sql(ty.inner(), raw)?;
941 if n.0.0.is_infinite() {
949 return Err("numeric infinity is not supported".into());
950 }
951 Ok(Value::Numeric(Numeric(rescale_numeric(
952 n.0,
953 constraints.as_ref(),
954 )?)))
955 }
956 Type::Oid | Type::RegClass | Type::RegType => {
957 u32::from_sql(ty.inner(), raw).map(Value::Oid)
958 }
959 Type::RegProc => u32::from_sql(ty.inner(), raw).map(Value::RegProc),
960 Type::Record(_) => Err("input of anonymous composite types is not implemented".into()),
961 Type::Text => decode_binary_string(ty, raw).map(Value::Text),
962 Type::BpChar { .. } => decode_binary_string(ty, raw).map(Value::BpChar),
963 Type::VarChar { .. } => decode_binary_string(ty, raw).map(Value::VarChar),
964 Type::Time { .. } => {
965 const USECS_PER_DAY: i64 = 24 * 60 * 60 * 1_000_000;
972 let usecs = i64::from_sql(ty.inner(), raw)?;
973 if !(0..USECS_PER_DAY).contains(&usecs) {
974 return Err("time out of range".into());
975 }
976 let secs = u32::try_from(usecs / 1_000_000).expect("less than 86,400");
977 let nanos = u32::try_from(usecs % 1_000_000).expect("less than 1,000,000") * 1_000;
978 Ok(Value::Time(
979 NaiveTime::from_num_seconds_from_midnight_opt(secs, nanos)
980 .expect("validated against USECS_PER_DAY"),
981 ))
982 }
983 Type::TimeTz { .. } => Err("input of timetz types is not implemented".into()),
984 Type::Timestamp { .. } => {
985 let ts = NaiveDateTime::from_sql(ty.inner(), raw)?;
986 Ok(Value::Timestamp(CheckedTimestamp::from_timestamplike(ts)?))
987 }
988 Type::TimestampTz { .. } => {
989 let ts = DateTime::<Utc>::from_sql(ty.inner(), raw)?;
990 Ok(Value::TimestampTz(CheckedTimestamp::from_timestamplike(
991 ts,
992 )?))
993 }
994 Type::Uuid => Uuid::from_sql(ty.inner(), raw).map(Value::Uuid),
995 Type::MzTimestamp => {
996 let s = String::from_sql(ty.inner(), raw)?;
997 let t: mz_repr::Timestamp = s.parse()?;
998 Ok(Value::MzTimestamp(t))
999 }
1000 Type::Range { .. } => Err("binary decoding of range types is not implemented".into()),
1001 Type::MzAclItem => {
1002 let mz_acl_item = MzAclItem::decode_binary(raw)?;
1003 Ok(Value::MzAclItem(mz_acl_item))
1004 }
1005 Type::AclItem => Err("aclitem has no binary encoding".into()),
1006 }
1007 }
1008}
1009
1010const REGPROC_NULL: &str = "-";
1013
1014fn parse_regproc(s: &str) -> Result<u32, Box<dyn Error + Sync + Send>> {
1021 if s == REGPROC_NULL {
1022 return Ok(0);
1023 }
1024 match regproc::oid(s) {
1025 Ok(oid) => Ok(oid),
1026 Err(regproc::NameLookupError::Ambiguous) => {
1028 Err(format!("more than one function named \"{}\"", s).into())
1029 }
1030 Err(regproc::NameLookupError::NotFound) => match strconv::parse_oid(s) {
1031 Ok(oid) => Ok(oid),
1032 Err(err) if is_number_shaped(s) => Err(err.into()),
1036 Err(_) => Err(format!("function \"{}\" does not exist", s).into()),
1037 },
1038 }
1039}
1040
1041fn is_number_shaped(s: &str) -> bool {
1043 let digits = s.trim();
1044 let digits = digits.strip_prefix(['+', '-']).unwrap_or(digits);
1045 !digits.is_empty() && digits.bytes().all(|b| b.is_ascii_digit())
1046}
1047
1048#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1054pub struct TextEncodeSettings {
1055 pub extra_float_digits: i32,
1060}
1061
1062impl TextEncodeSettings {
1063 pub const STABLE: TextEncodeSettings = TextEncodeSettings {
1065 extra_float_digits: 1,
1066 };
1067}
1068
1069const FLOAT4_DIGITS: i32 = 6;
1072const FLOAT8_DIGITS: i32 = 15;
1073
1074fn format_float_limited(buf: &mut BytesMut, f: f64, ndig: i32) -> Nestable {
1079 if f.is_nan() {
1080 buf.write_str("NaN");
1081 return Nestable::Yes;
1082 }
1083 if f.is_infinite() {
1084 buf.write_str(if f < 0.0 { "-Infinity" } else { "Infinity" });
1085 return Nestable::Yes;
1086 }
1087 let ndig = ndig.max(1);
1088 let prec = usize::try_from(ndig - 1).expect("ndig is at least 1");
1092 let sci = format!("{:.prec$e}", f);
1093 let (mantissa, exp) = sci.split_once('e').expect("e format has an exponent");
1094 let exp: i32 = exp.parse().expect("valid exponent");
1095 if exp < -4 || exp >= ndig {
1096 let mantissa = mantissa.trim_end_matches('0').trim_end_matches('.');
1099 write!(buf, "{mantissa}e{exp:+03}");
1100 } else {
1101 let decimals = usize::try_from(ndig - 1 - exp).expect("exp is less than ndig");
1105 let fixed = format!("{f:.decimals$}");
1106 let fixed = match fixed.contains('.') {
1107 true => fixed.trim_end_matches('0').trim_end_matches('.'),
1108 false => &fixed,
1109 };
1110 buf.write_str(fixed);
1111 }
1112 Nestable::Yes
1113}
1114
1115fn reject_nul(s: &str) -> Result<(), Box<dyn Error + Sync + Send>> {
1118 if s.contains('\0') {
1119 Err(Box::new(NulCharacterError))
1120 } else {
1121 Ok(())
1122 }
1123}
1124
1125fn decode_binary_string(ty: &Type, raw: &[u8]) -> Result<String, Box<dyn Error + Sync + Send>> {
1127 let s = String::from_sql(ty.inner(), raw)?;
1128 reject_nul(&s)?;
1129 Ok(s)
1130}
1131
1132fn rescale_numeric(
1134 mut n: OrderedDecimal<mz_repr_numeric::Numeric>,
1135 constraints: Option<&NumericConstraints>,
1136) -> Result<OrderedDecimal<mz_repr_numeric::Numeric>, Box<dyn Error + Sync + Send>> {
1137 if let Some(constraints) = constraints {
1138 rescale(
1139 &mut n.0,
1140 NumericMaxScale::try_from(i64::from(constraints.max_scale()))?.into_u8(),
1141 )?;
1142 }
1143 Ok(n)
1144}
1145
1146fn encode_element(buf: &mut BytesMut, elem: Option<&Value>, ty: &Type) -> Result<(), io::Error> {
1147 match elem {
1148 None => buf.put_i32(-1),
1149 Some(elem) => {
1150 let base = buf.len();
1151 buf.put_i32(0);
1152 elem.encode_binary(ty, buf)?;
1153 let len = pg_len("encoded element", buf.len() - base - 4)?;
1154 buf[base..base + 4].copy_from_slice(&len.to_be_bytes());
1155 }
1156 }
1157 Ok(())
1158}
1159
1160fn pg_len(what: &str, len: usize) -> Result<i32, io::Error> {
1161 len.try_into().map_err(|_| {
1162 io::Error::new(
1163 io::ErrorKind::InvalidData,
1164 format!("{} does not fit into an i32", what),
1165 )
1166 })
1167}
1168
1169pub fn values_from_row(row: &RowRef, typ: &SqlRelationType) -> Vec<Option<Value>> {
1174 row.iter()
1175 .zip_eq(typ.column_types.iter())
1176 .map(|(col, typ)| Value::from_datum(col, &typ.scalar_type))
1177 .collect()
1178}
1179
1180#[cfg(test)]
1181mod tests {
1182 use mz_repr::arb_datum_for_scalar;
1183 use proptest::prelude::*;
1184
1185 use super::*;
1186
1187 #[mz_ore::test]
1195 #[cfg_attr(miri, ignore)] fn proptest_binary_encoding_error_matches_encode_binary() {
1197 let strat =
1198 any::<SqlScalarType>().prop_flat_map(|ty| (Just(ty.clone()), arb_datum_for_scalar(ty)));
1199 proptest!(ProptestConfig::with_cases(256), |((ty, prop_datum) in strat)| {
1200 if Value::binary_encoding_error(&ty).is_err() {
1204 return Ok(());
1205 }
1206 let datum = Datum::from(&prop_datum);
1207 let value = match Value::from_datum(datum, &ty) {
1208 Some(v) => v,
1209 None => return Ok(()),
1211 };
1212 let pg_ty = Type::from(&ty);
1213 let mut buf = BytesMut::new();
1214 value
1215 .encode_binary(&pg_ty, &mut buf)
1216 .expect("encode_binary must succeed when binary_encoding_error returns Ok");
1217 });
1218 }
1219
1220 #[mz_ore::test]
1224 fn regproc_text_encoding_resolves_names() {
1225 for (oid, expected, round_trips) in [
1229 (0, "-", true),
1230 (1242, "boolin", true),
1231 (1398, "pg_catalog.abs", false),
1232 (99999, "99999", true),
1233 (u32::MAX, "4294967295", true),
1234 ] {
1235 let mut buf = BytesMut::new();
1236 Value::from_datum(Datum::UInt32(oid), &SqlScalarType::RegProc)
1237 .expect("a non-null datum encodes")
1238 .encode_text(&mut buf, TextEncodeSettings::STABLE);
1239 assert_eq!(str::from_utf8(&buf).unwrap(), expected, "regproc {oid}");
1240
1241 if round_trips {
1242 let Value::RegProc(decoded) = Value::decode_text(&Type::RegProc, &buf).unwrap()
1243 else {
1244 panic!("decoding a regproc must yield Value::RegProc");
1245 };
1246 assert_eq!(decoded, oid, "text rendering {expected} did not round trip");
1247 }
1248
1249 let mut binary = BytesMut::new();
1250 Value::RegProc(oid)
1251 .encode_binary(&Type::RegProc, &mut binary)
1252 .expect("regproc has a binary encoding");
1253 assert_eq!(&binary[..], &oid.to_be_bytes()[..], "regproc {oid} binary");
1254 }
1255 }
1256
1257 #[mz_ore::test]
1260 fn regproc_text_decoding_rejects_ambiguous_names() {
1261 assert_eq!(
1262 Value::decode_text(&Type::RegProc, b"pg_catalog.abs")
1263 .map(|_| ())
1264 .map_err(|e| e.to_string())
1265 .unwrap_err(),
1266 "more than one function named \"pg_catalog.abs\"",
1267 );
1268 assert_eq!(
1269 Value::decode_text(&Type::RegProc, b"no_such_function")
1270 .map(|_| ())
1271 .map_err(|e| e.to_string())
1272 .unwrap_err(),
1273 "function \"no_such_function\" does not exist",
1274 );
1275 let out_of_range = Value::decode_text(&Type::RegProc, b"99999999999999")
1276 .map(|_| ())
1277 .map_err(|e| e.to_string())
1278 .unwrap_err();
1279 assert!(
1280 out_of_range.starts_with("invalid input syntax for type oid"),
1281 "unexpected error for an out of range OID: {out_of_range}",
1282 );
1283 }
1284
1285 #[mz_ore::test]
1289 fn format_float_limited_matches_printf_g() {
1290 for (f, ndig, expected) in [
1291 (0.1_f64 + 0.2_f64, 15, "0.3"),
1292 (0.1_f64 + 0.2_f64, 1, "0.3"),
1293 (f64::from(123.45679_f32), 6, "123.457"),
1294 (f64::from(123.45679_f32), 3, "123"),
1295 (1e15, 15, "1e+15"),
1296 (-123456.0, 3, "-1.23e+05"),
1297 (999.999, 3, "1e+03"),
1298 (0.0001, 15, "0.0001"),
1299 (-0.00001, 15, "-1e-05"),
1300 (0.0, 15, "0"),
1301 (-0.0, 15, "-0"),
1302 (100.0, 15, "100"),
1303 (1.23456789012345, 12, "1.23456789012"),
1304 (f64::NAN, 15, "NaN"),
1305 (f64::INFINITY, 15, "Infinity"),
1306 (f64::NEG_INFINITY, -100, "-Infinity"),
1307 (0.1_f64 + 0.2_f64, -5, "0.3"),
1309 ] {
1310 let mut buf = BytesMut::new();
1311 format_float_limited(&mut buf, f, ndig);
1312 assert_eq!(
1313 str::from_utf8(&buf).unwrap(),
1314 expected,
1315 "{f} with {ndig} digits"
1316 );
1317 }
1318 }
1319
1320 #[mz_ore::test]
1322 fn decode_text_error_smoke_test() {
1323 let bool_array = Value::Array {
1324 dims: vec![ArrayDimension {
1325 lower_bound: 0,
1326 length: 1,
1327 }],
1328 elements: vec![Some(Value::Bool(true))],
1329 };
1330
1331 let mut buf = BytesMut::new();
1332 bool_array.encode_text(&mut buf, TextEncodeSettings::STABLE);
1333 let buf = buf.to_vec();
1334
1335 let int_array_tpe = Type::Array(Box::new(Type::Int4));
1336 let decoded_int_array = Value::decode_text(&int_array_tpe, &buf);
1337
1338 assert_eq!(
1339 decoded_int_array.map_err(|e| e.to_string()).unwrap_err(),
1340 "invalid input syntax for type array: Specifying array lower bounds is not supported: \"[0:0]={t}\"".to_string()
1341 );
1342 }
1343
1344 #[mz_ore::test]
1350 #[cfg_attr(miri, ignore)] fn decode_numeric_applies_destination_scale() {
1352 let ty = Type::from(&SqlScalarType::Numeric {
1354 max_scale: Some(NumericMaxScale::try_from(2_i64).unwrap()),
1355 });
1356 let expected = strconv::parse_numeric("10.45").unwrap();
1357
1358 let input = Value::Numeric(Numeric(strconv::parse_numeric("10.447").unwrap()));
1361 let mut buf = BytesMut::new();
1362 input
1363 .encode_binary(&ty, &mut buf)
1364 .expect("encoding 10.447 as numeric must succeed");
1365 let Value::Numeric(Numeric(binary)) = Value::decode_binary(&ty, &buf).unwrap() else {
1366 panic!("decode_binary of a numeric must yield Value::Numeric");
1367 };
1368 assert_eq!(binary, expected, "binary decode did not rescale to scale 2");
1369
1370 let Value::Numeric(Numeric(text)) = Value::decode_text(&ty, b"10.447").unwrap() else {
1372 panic!("decode_text of a numeric must yield Value::Numeric");
1373 };
1374 assert_eq!(text, expected, "text decode did not rescale to scale 2");
1375 }
1376
1377 #[mz_ore::test]
1380 #[cfg_attr(miri, ignore)] fn decode_binary_numeric_rejects_infinity() {
1382 let ty = Type::Numeric { constraints: None };
1383 let header = |sign: u16| {
1385 let mut b = Vec::new();
1386 b.extend_from_slice(&0i16.to_be_bytes());
1387 b.extend_from_slice(&0i16.to_be_bytes());
1388 b.extend_from_slice(&sign.to_be_bytes());
1389 b.extend_from_slice(&0i16.to_be_bytes());
1390 b
1391 };
1392 for sign in [0xD000, 0xF000] {
1394 let res = Value::decode_binary(&ty, &header(sign));
1395 assert_eq!(
1396 res.map(|_| ()).map_err(|e| e.to_string()).unwrap_err(),
1397 "numeric infinity is not supported",
1398 "sign {sign:#x} must be rejected",
1399 );
1400 }
1401 let Value::Numeric(Numeric(nan)) = Value::decode_binary(&ty, &header(0xC000)).unwrap()
1403 else {
1404 panic!("decode_binary of a numeric must yield Value::Numeric");
1405 };
1406 assert_eq!(nan, strconv::parse_numeric("NaN").unwrap());
1407 }
1408
1409 #[mz_ore::test]
1413 fn decode_binary_time_rejects_out_of_range() {
1414 const USECS_PER_DAY: i64 = 24 * 60 * 60 * 1_000_000;
1415 let ty = Type::Time { precision: None };
1416
1417 for usecs in [USECS_PER_DAY, USECS_PER_DAY + 1, -1, i64::MIN, i64::MAX] {
1418 let res = Value::decode_binary(&ty, &usecs.to_be_bytes());
1419 assert_eq!(
1420 res.map(|_| ()).map_err(|e| e.to_string()).unwrap_err(),
1421 "time out of range",
1422 "{usecs} microseconds must be rejected",
1423 );
1424 }
1425
1426 let Value::Time(t) = Value::decode_binary(&ty, &(USECS_PER_DAY - 1).to_be_bytes()).unwrap()
1427 else {
1428 panic!("decoding a time value must yield Value::Time");
1429 };
1430 assert_eq!(
1431 t,
1432 NaiveTime::from_hms_micro_opt(23, 59, 59, 999_999).unwrap()
1433 );
1434 }
1435
1436 #[mz_ore::test]
1438 fn decode_rejects_nul_in_strings() {
1439 const NUL_ERR: &str = "invalid byte sequence for encoding \"UTF8\": 0x00";
1440 let raw = b"foo\x00bar";
1441
1442 for ty in [
1443 Type::Text,
1444 Type::BpChar { length: None },
1445 Type::VarChar { max_length: None },
1446 Type::Name,
1447 ] {
1448 for format in [Format::Text, Format::Binary] {
1449 let res = Value::decode(format, &ty, raw);
1450 assert_eq!(
1451 res.map(|_| ()).map_err(|e| e.to_string()).unwrap_err(),
1452 NUL_ERR,
1453 "{ty:?} in {format:?} format must reject NUL",
1454 );
1455 }
1456 }
1457
1458 let res = Value::decode_text(&Type::Bytea, b"\\x00\x00");
1460 assert_eq!(
1461 res.map(|_| ()).map_err(|e| e.to_string()).unwrap_err(),
1462 NUL_ERR,
1463 );
1464
1465 let Value::Text(s) = Value::decode(Format::Binary, &Type::Text, b"foobar").unwrap() else {
1467 panic!("decoding a text value must yield Value::Text");
1468 };
1469 assert_eq!(s, "foobar");
1470 }
1471}