Skip to main content

rupg_func/
io.rs

1//! The input, output, receive and send functions of the types, as `typinput`, `typoutput`, `typreceive` and `typsend` of `pg_type` give them.
2
3use rupg_common::{Error, Result, SqlState};
4use rupg_pgcatalog::builtin;
5use rupg_types::{
6    self as types, Array, DateTimeInput, Record, Recv, RegKind, Value, ZoneAbbrevs, oid, tz,
7};
8
9use crate::{Call, Session, bad_value, not_yet, reg, type_error};
10
11/// The OID of `xid`.
12const XID: u32 = 28;
13/// The OID of `cid`.
14const CID: u32 = 29;
15/// The category of the array types in `pg_type`.
16const ARRAY_CATEGORY: u8 = b'A';
17
18/// The type that holds the values of a type: the base type of a domain, or the type itself.
19pub fn base_type(ty: u32) -> u32 {
20    let mut ty = ty;
21    while let Some(row) = builtin::type_by_oid(ty)
22        && row.kind == b'd'
23    {
24        ty = row.base;
25    }
26    ty
27}
28
29/// The element type and the delimiter of an array type, or `None` for a type that is not an array. `int2vector` and `oidvector` are not arrays here, because they have their own functions.
30pub(crate) fn array_of(ty: u32) -> Option<(u32, u8)> {
31    if ty == oid::INT2VECTOR || ty == oid::OIDVECTOR {
32        return None;
33    }
34    let row = builtin::type_by_oid(ty)?;
35    // `record[]` is a pseudo-type, so its category is not the one of the arrays.
36    if (row.category != ARRAY_CATEGORY && ty != oid::RECORD_ARRAY) || row.elem == 0 {
37        return None;
38    }
39    let delim = builtin::type_by_oid(row.elem).map_or(b',', |e| e.delim);
40    Some((row.elem, delim))
41}
42
43/// True when the engine has the output and the send function of the type.
44pub fn output_supported(ty: u32) -> bool {
45    let ty = base_type(ty);
46    if let Some((elem, _)) = array_of(ty) {
47        return output_supported(elem);
48    }
49    if let Some(sub) = builtin::range_subtype(ty) {
50        return output_supported(sub);
51    }
52    if let Some(range) = builtin::multirange_range(ty) {
53        return output_supported(range);
54    }
55    matches!(
56        ty,
57        oid::BOOL
58            | oid::BYTEA
59            | oid::CHAR
60            | oid::NAME
61            | oid::INT8
62            | oid::INT2
63            | oid::INT4
64            | oid::INT2VECTOR
65            | oid::TEXT
66            | oid::OID
67            | XID
68            | CID
69            | oid::OIDVECTOR
70            | oid::JSON
71            | oid::FLOAT4
72            | oid::FLOAT8
73            | oid::UNKNOWN
74            | oid::BPCHAR
75            | oid::VARCHAR
76            | oid::DATE
77            | oid::TIME
78            | oid::TIMESTAMP
79            | oid::TIMESTAMPTZ
80            | oid::INTERVAL
81            | oid::TIMETZ
82            | oid::NUMERIC
83            | oid::CSTRING
84            | oid::UUID
85            | oid::INET
86            | oid::CIDR
87            | oid::JSONB
88            | oid::PG_NODE_TREE
89            | oid::PG_NDISTINCT
90            | oid::PG_DEPENDENCIES
91            | oid::PG_MCV_LIST
92            | oid::ACLITEM
93            | oid::ANYARRAY
94            | oid::PG_LSN
95            | oid::VOID
96            | oid::TRIGGER
97            | oid::EVENT_TRIGGER
98            | oid::MONEY
99            | oid::TID
100            | oid::XID8
101            | oid::OID8
102            | oid::REFCURSOR
103            | oid::MACADDR
104            | oid::MACADDR8
105            | oid::BIT
106            | oid::VARBIT
107            | oid::POINT
108            | oid::LSEG
109            | oid::PATH
110            | oid::BOX
111            | oid::POLYGON
112            | oid::LINE
113            | oid::CIRCLE
114            | oid::PG_SNAPSHOT
115            | oid::TXID_SNAPSHOT
116            | oid::TSVECTOR
117            | oid::TSQUERY
118            | oid::JSONPATH
119            | oid::XML
120    ) || RegKind::from_oid(ty).is_some()
121        || is_row_type(ty)
122        || is_enum(ty)
123}
124
125/// True for an enum type.
126pub(crate) fn is_enum(ty: u32) -> bool {
127    builtin::type_by_oid(ty).is_some_and(|row| row.kind == b'e')
128}
129
130/// `enum_out`: the name of the label with this OID of the enum type.
131fn enum_label(ty: u32, label: u32) -> Result<&'static str> {
132    builtin::enum_labels(ty)
133        .and_then(|labels| labels.iter().find(|(oid, _)| *oid == label))
134        .map(|(_, name)| name.as_str())
135        .ok_or_else(|| {
136            Error::new(
137                SqlState::INVALID_BINARY_REPRESENTATION,
138                format!("invalid internal value for enum: {label}"),
139            )
140        })
141}
142
143/// `enum_in`: the OID of the label with this name of the enum type.
144fn enum_in(ty: u32, text: &str) -> std::result::Result<u32, Error> {
145    builtin::enum_labels(ty)
146        .and_then(|labels| labels.iter().find(|(_, name)| name == text))
147        .map(|(oid, _)| *oid)
148        .ok_or_else(|| {
149            Error::new(
150                SqlState::INVALID_TEXT_REPRESENTATION,
151                format!("invalid input value for enum {}: \"{text}\"", type_name(ty)),
152            )
153        })
154}
155
156/// `check_safe_enum_use`: an error for a label that the open transaction added to an enum type that an earlier transaction made, as an index can have the label only after the commit.
157pub(crate) fn check_safe_enum_use(ty: u32, oid: u32, label: &str) -> Result<()> {
158    if !builtin::enum_label_is_uncommitted(ty, oid) {
159        return Ok(());
160    }
161    Err(Error::new(
162        SqlState::UNSAFE_NEW_ENUM_VALUE_USAGE,
163        format!("unsafe use of new value \"{label}\" of enum type {}", type_name(ty)),
164    )
165    .with_hint("New enum values must be committed before they can be used."))
166}
167
168/// True for `record` and for the row type of a system catalog, which `record_out` and `record_send` show. The output of each field needs the output of its type, which the engine checks when it shows the row.
169pub fn is_row_type(ty: u32) -> bool {
170    ty == oid::RECORD || builtin::type_by_oid(ty).is_some_and(|row| row.kind == b'c')
171}
172
173/// True when the engine has the text input function of the type.
174pub fn input_supported(ty: u32) -> bool {
175    let ty = base_type(ty);
176    if let Some((elem, _)) = array_of(ty) {
177        return input_supported(elem);
178    }
179    if let Some(sub) = builtin::range_subtype(ty) {
180        return input_supported(sub);
181    }
182    if let Some(range) = builtin::multirange_range(ty) {
183        return input_supported(range);
184    }
185    output_supported(ty) && ty != oid::ANYARRAY && ty != oid::RECORD
186}
187
188/// The name of a type for an error. A type of the catalog of the session has its name in its row.
189pub(crate) fn type_name(ty: u32) -> String {
190    let name = types::format_type(ty);
191    if name != "???" {
192        return name.into_owned();
193    }
194    match builtin::type_by_oid(ty) {
195        Some(row) if row.category == ARRAY_CATEGORY && row.elem != 0 => {
196            format!("{}[]", type_name(row.elem))
197        }
198        Some(row) => row.name.to_owned(),
199        None => name.into_owned(),
200    }
201}
202
203/// The state that the input of the date and time types reads.
204pub(crate) fn datetime_input<'a>(
205    session: &'a dyn Session,
206    zone: &'a dyn types::TimeZone,
207) -> DateTimeInput<'a> {
208    DateTimeInput {
209        order: session.date_format().order,
210        zone,
211        zones: &tz::TzDatabase,
212        abbrevs: ZoneAbbrevs::postgres_default(),
213        now: session.transaction_start(),
214    }
215}
216
217/// The text output function of the type.
218///
219/// # Errors
220///
221/// `0A000` for a type that has no output function here, and the error of the output function, such as a timestamp out of range.
222pub fn output(ty: u32, value: &Value, session: &dyn Session, out: &mut Vec<u8>) -> Result<()> {
223    let ty = base_type(ty);
224    if is_enum(ty)
225        && let Value::Oid(label) = value
226    {
227        out.extend_from_slice(enum_label(ty, *label)?.as_bytes());
228        return Ok(());
229    }
230    if let Some((elem, delim)) = array_of(ty) {
231        let Value::Array(array) = value else { return Err(bad_value()) };
232        let mut failed = None;
233        types::array_out(array, delim, out, |item, out| {
234            if failed.is_none()
235                && let Err(error) = output(elem, item, session, out)
236            {
237                failed = Some(error);
238            }
239        });
240        return failed.map_or(Ok(()), Err);
241    }
242    if let (Some(kind), Value::Oid(v)) = (RegKind::from_oid(ty), value) {
243        reg::output(kind, *v, session, out);
244        return Ok(());
245    }
246    if let Value::Range(range) = value {
247        return range_out(ty, range, session, out);
248    }
249    if let Value::Multirange(ranges) = value {
250        return multirange_out(ty, ranges, session, out);
251    }
252    if let Value::Record(record) = value {
253        return record_out(record, session, out);
254    }
255    if let Value::TsVector(lexemes) = value {
256        types::tsvector_deparse(lexemes, out);
257        return Ok(());
258    }
259    if let Value::TsQuery(items) = value {
260        types::tsquery_deparse(items, out);
261        return Ok(());
262    }
263    if let Value::JsonPath(path) = value {
264        types::jsonpath_deparse(path, out);
265        return Ok(());
266    }
267    if let (Some(kind), Value::Bytea(v)) = (types::GeoKind::from_oid(ty), value) {
268        types::geo_out(kind, v, session.extra_float_digits(), out);
269        return Ok(());
270    }
271    match (ty, value) {
272        (oid::MONEY, Value::Int8(v)) => types::cash_out(*v, out),
273        (oid::PG_LSN, Value::Int8(v)) => types::pg_lsn_out(v.cast_unsigned(), out),
274        (oid::PG_SNAPSHOT | oid::TXID_SNAPSHOT, Value::Bytea(v)) => {
275            let snapshot = types::Snapshot::from_bytes(v).ok_or_else(bad_value)?;
276            types::snapshot_out(&snapshot, out);
277        }
278        (oid::MACADDR, Value::Int8(v)) => types::macaddr_out(v.cast_unsigned(), out),
279        (oid::MACADDR8, Value::Int8(v)) => types::macaddr8_out(v.cast_unsigned(), out),
280        (oid::XID8 | oid::OID8, Value::Int8(v)) => types::u64_out(v.cast_unsigned(), out),
281        (oid::TID, Value::Int8(v)) => {
282            let (block, offset) = tid_parts(*v)?;
283            types::tid_out(block, offset, out);
284        }
285        (_, Value::Bool(v)) => types::bool_out(*v, out),
286        (_, Value::Int2(v)) => types::int_out(i64::from(*v), out),
287        (_, Value::Int4(v)) => types::int_out(i64::from(*v), out),
288        (_, Value::Int8(v)) => types::int_out(*v, out),
289        (_, Value::Float4(v)) => types::float4_out(*v, session.extra_float_digits(), out),
290        (_, Value::Float8(v)) => types::float8_out(*v, session.extra_float_digits(), out),
291        (_, Value::Numeric(v)) => types::numeric_out(v, out),
292        (oid::OID | XID | CID, Value::Oid(v)) => types::oid_out(*v, out),
293        (_, Value::Char(v)) => types::char_out(*v, out),
294        (oid::XML, Value::Text(v)) => {
295            if let Some(warning) = types::xml_out(v, out) {
296                session.warn(type_error(warning));
297            }
298        }
299        (_, Value::Text(v)) => out.extend_from_slice(v.as_bytes()),
300        (oid::PG_NDISTINCT, Value::Bytea(v)) => {
301            types::pg_ndistinct_out(v, out).map_err(type_error)?
302        }
303        (oid::PG_DEPENDENCIES, Value::Bytea(v)) => {
304            types::pg_dependencies_out(v, out).map_err(type_error)?;
305        }
306        (_, Value::Bytea(v)) => types::bytea_out(v, session.bytea_output(), out),
307        (_, Value::Uuid(v)) => types::uuid_out(v, out),
308        (_, Value::Inet(v)) => types::inet_out(v, ty == oid::CIDR, out),
309        (_, Value::Date(v)) => types::date_out(*v, session.date_format(), out),
310        (_, Value::Time(v)) => types::time_out(*v, out),
311        (_, Value::TimeTz(time, zone)) => types::timetz_out(*time, *zone, out),
312        (_, Value::Timestamp(v)) => {
313            types::timestamp_out(*v, session.date_format(), out).map_err(type_error)?;
314        }
315        (_, Value::TimestampTz(v)) => {
316            types::timestamptz_out(*v, session.date_format(), &*session.zone()?, out)
317                .map_err(type_error)?;
318        }
319        (_, Value::Interval(v)) => types::interval_out(v, session.interval_style(), out),
320        (oid::INT2VECTOR, Value::Array(array)) => {
321            let values = vector(array, |v| match v {
322                Value::Int2(n) => Some(*n),
323                _ => None,
324            })?;
325            types::int2vector_out(&values, out);
326        }
327        (oid::OIDVECTOR, Value::Array(array)) => {
328            let values = vector(array, Value::as_oid)?;
329            types::oidvector_out(&values, out);
330        }
331        (_, Value::Null) => return Err(Error::internal("the output function got a null value")),
332        _ => return Err(not_yet(format!("output of type {}", type_name(ty)))),
333    }
334    Ok(())
335}
336
337/// A `tid` value: the block number and the offset in a `bigint`, so that the order of the values is the order of `tid`.
338pub(crate) fn tid_value(block: u32, offset: u16) -> Value {
339    Value::Int8(i64::from(block) << 16 | i64::from(offset))
340}
341
342/// The block number and the offset of a `tid` value.
343pub(crate) fn tid_parts(value: i64) -> Result<(u32, u16)> {
344    let block = u32::try_from(value >> 16).map_err(|_| bad_value())?;
345    let offset = u16::try_from(value & 0xFFFF).map_err(|_| bad_value())?;
346    Ok((block, offset))
347}
348
349/// `range_out`: the output function of the subtype for each bound that the range has, in the text of the range.
350fn range_out(
351    ty: u32,
352    range: &types::Range,
353    session: &dyn Session,
354    out: &mut Vec<u8>,
355) -> Result<()> {
356    let sub = crate::range::subtype(ty);
357    let (mut lower, mut upper) = (Vec::new(), Vec::new());
358    if range.has_lower() {
359        output(sub, &range.lower, session, &mut lower)?;
360    }
361    if range.has_upper() {
362        output(sub, &range.upper, session, &mut upper)?;
363    }
364    types::range_deparse(
365        range.flags,
366        range.has_lower().then_some(&lower[..]),
367        range.has_upper().then_some(&upper[..]),
368        out,
369    );
370    Ok(())
371}
372
373/// `range_send`: the send function of the subtype for each bound that the range has, in the binary form of the range.
374fn range_send(ty: u32, range: &types::Range, out: &mut Vec<u8>) -> Result<()> {
375    let sub = crate::range::subtype(ty);
376    let (mut lower, mut upper) = (Vec::new(), Vec::new());
377    if range.has_lower() {
378        send(sub, &range.lower, &mut lower)?;
379    }
380    if range.has_upper() {
381        send(sub, &range.upper, &mut upper)?;
382    }
383    types::range_send(
384        range.flags,
385        range.has_lower().then_some(&lower[..]),
386        range.has_upper().then_some(&upper[..]),
387        out,
388    );
389    Ok(())
390}
391
392/// `multirange_out`: the output function of the range type for each range, in braces.
393fn multirange_out(
394    ty: u32,
395    ranges: &[types::Range],
396    session: &dyn Session,
397    out: &mut Vec<u8>,
398) -> Result<()> {
399    let range_ty = crate::multirange::range_of(ty);
400    let mut texts = Vec::with_capacity(ranges.len());
401    for range in ranges {
402        let mut text = Vec::new();
403        range_out(range_ty, range, session, &mut text)?;
404        texts.push(text);
405    }
406    types::multirange_deparse(texts.iter().map(Vec::as_slice), out);
407    Ok(())
408}
409
410/// `multirange_send`: the send function of the range type for each range, in the binary form of the multirange.
411fn multirange_send(ty: u32, ranges: &[types::Range], out: &mut Vec<u8>) -> Result<()> {
412    let range_ty = crate::multirange::range_of(ty);
413    let mut bytes = Vec::with_capacity(ranges.len());
414    for range in ranges {
415        let mut one = Vec::new();
416        range_send(range_ty, range, &mut one)?;
417        bytes.push(one);
418    }
419    types::multirange_send(&bytes.iter().map(Vec::as_slice).collect::<Vec<_>>(), out);
420    Ok(())
421}
422
423/// `record_out`: the fields in parentheses with a comma between them. A null field is empty. A field that is empty or has a quote, a backslash, a parenthesis, a comma or white space is in double quotes, with each quote and each backslash doubled.
424fn record_out(record: &Record, session: &dyn Session, out: &mut Vec<u8>) -> Result<()> {
425    out.push(b'(');
426    let mut field = Vec::new();
427    for (i, (ty, value)) in record.types.iter().zip(&record.values).enumerate() {
428        if i > 0 {
429            out.push(b',');
430        }
431        if value.is_null() {
432            continue;
433        }
434        field.clear();
435        output(*ty, value, session, &mut field)?;
436        let quote = field.is_empty()
437            || field.iter().any(|b| matches!(b, b'"' | b'\\' | b'(' | b')' | b',') || is_space(*b));
438        if !quote {
439            out.extend_from_slice(&field);
440            continue;
441        }
442        out.push(b'"');
443        for &b in &field {
444            if matches!(b, b'"' | b'\\') {
445                out.push(b);
446            }
447            out.push(b);
448        }
449        out.push(b'"');
450    }
451    out.push(b')');
452    Ok(())
453}
454
455/// The type and the typmod of each column of the relation of a row type: a system catalog or a relation of the catalog of the session.
456fn row_columns(ty: u32, session: &dyn Session) -> Option<Vec<(u32, i32)>> {
457    let relid = builtin::type_by_oid(ty).filter(|row| row.relid != 0)?.relid;
458    if let Some(catalog) = rupg_pgcatalog::catalog_by_oid(relid) {
459        return Some(catalog.columns.iter().map(|c| (c.type_oid, -1)).collect());
460    }
461    let rel = session.catalog()?.relation(relid)?;
462    Some(rel.columns.iter().filter(|c| !c.dropped).map(|c| (c.ty, c.typmod)).collect())
463}
464
465/// `record_in` of a composite type: the fields in parentheses with a comma between them, as [`record_out`] writes them. An empty field is null. A field can have double quotes and backslashes, as in `record_out`.
466fn record_in(
467    ty: u32,
468    text: &str,
469    session: &dyn Session,
470    noisy: bool,
471) -> std::result::Result<Value, Failed> {
472    if ty == oid::RECORD {
473        return Err(Failed::Hard(Error::new(
474            SqlState::FEATURE_NOT_SUPPORTED,
475            "input of anonymous composite types is not implemented",
476        )));
477    }
478    let columns = row_columns(ty, session).ok_or_else(|| Failed::Hard(no_input(ty)))?;
479    let malformed = |detail: &str| {
480        Failed::Soft(
481            Error::new(
482                SqlState::INVALID_TEXT_REPRESENTATION,
483                format!("malformed record literal: \"{text}\""),
484            )
485            .with_detail(detail),
486        )
487    };
488    let bytes = text.as_bytes();
489    let mut at = bytes.iter().take_while(|&&b| is_space(b)).count();
490    if bytes.get(at) != Some(&b'(') {
491        return Err(malformed("Missing left parenthesis."));
492    }
493    at += 1;
494    let mut types = Vec::with_capacity(columns.len());
495    let mut values = Vec::with_capacity(columns.len());
496    let mut field = Vec::new();
497    for (i, &(column, typmod)) in columns.iter().enumerate() {
498        if i > 0 {
499            if bytes.get(at) != Some(&b',') {
500                return Err(malformed("Too few columns."));
501            }
502            at += 1;
503        }
504        types.push(column);
505        if matches!(bytes.get(at), Some(b',' | b')')) {
506            values.push(Value::Null);
507            continue;
508        }
509        field.clear();
510        let mut quoted = false;
511        while quoted || !matches!(bytes.get(at), Some(b',' | b')')) {
512            let Some(&b) = bytes.get(at) else {
513                return Err(malformed("Unexpected end of input."));
514            };
515            at += 1;
516            match b {
517                b'\\' => {
518                    let Some(&next) = bytes.get(at) else {
519                        return Err(malformed("Unexpected end of input."));
520                    };
521                    field.push(next);
522                    at += 1;
523                }
524                b'"' if quoted && bytes.get(at) == Some(&b'"') => {
525                    field.push(b'"');
526                    at += 1;
527                }
528                b'"' => quoted = !quoted,
529                _ => field.push(b),
530            }
531        }
532        // The bytes of the field come from the text and split it only at ASCII bytes, so they are UTF-8.
533        let item = String::from_utf8_lossy(&field);
534        values.push(input_value(column, &item, typmod, session, noisy)?);
535    }
536    if bytes.get(at) != Some(&b')') {
537        return Err(malformed("Too many columns."));
538    }
539    at += 1;
540    if bytes[at..].iter().any(|&b| !is_space(b)) {
541        return Err(malformed("Junk after right parenthesis."));
542    }
543    Ok(Value::Record(Box::new(Record::new(types, values))))
544}
545
546/// `isspace` of C in the C locale.
547fn is_space(b: u8) -> bool {
548    matches!(b, b' ' | b'\t' | b'\n' | b'\r' | 0x0B | 0x0C)
549}
550
551/// `record_send`: the number of fields, then for each field the OID of its type and the length and the bytes of its value, with the length -1 for a null.
552fn record_send(record: &Record, out: &mut Vec<u8>) -> Result<()> {
553    let count = i32::try_from(record.values.len()).map_err(|_| bad_value())?;
554    out.extend_from_slice(&count.to_be_bytes());
555    let mut field = Vec::new();
556    for (ty, value) in record.types.iter().zip(&record.values) {
557        out.extend_from_slice(&ty.to_be_bytes());
558        if value.is_null() {
559            out.extend_from_slice(&(-1i32).to_be_bytes());
560            continue;
561        }
562        field.clear();
563        send(*ty, value, &mut field)?;
564        let len = i32::try_from(field.len()).map_err(|_| bad_value())?;
565        out.extend_from_slice(&len.to_be_bytes());
566        out.extend_from_slice(&field);
567    }
568    Ok(())
569}
570
571/// The elements of an `int2vector` or an `oidvector`, which have no null element.
572fn vector<T>(array: &Array<Value>, get: impl Fn(&Value) -> Option<T>) -> Result<Vec<T>> {
573    array.values.iter().map(|v| v.as_ref().and_then(&get).ok_or_else(bad_value)).collect()
574}
575
576/// The cast of a value to `text`: the output function, with the casts of `pg_cast` that differ from it. `bool` gives `true` and `false`, and `bpchar` loses its trailing spaces.
577///
578/// # Errors
579///
580/// The errors of [`output`].
581pub fn to_text(ty: u32, value: &Value, session: &dyn Session) -> Result<String> {
582    match (base_type(ty), value) {
583        (oid::BOOL, Value::Bool(v)) => Ok(if *v { "true" } else { "false" }.to_string()),
584        (oid::BPCHAR, Value::Text(v)) => Ok(v.trim_end_matches(' ').to_string()),
585        (_, Value::Text(v)) => Ok(v.clone()),
586        _ => {
587            let mut out = Vec::new();
588            output(ty, value, session, &mut out)?;
589            String::from_utf8(out)
590                .map_err(|_| Error::internal("an output function gave bytes that are not UTF-8"))
591        }
592    }
593}
594
595/// The text input function of the type, with the typmod of the target.
596///
597/// # Errors
598///
599/// `0A000` for a type that has no input function here, and the error of the input function for a bad value.
600pub fn input(ty: u32, text: &str, typmod: i32, session: &dyn Session) -> Result<Value> {
601    match input_value(ty, text, typmod, session, true) {
602        Ok(value) => Ok(value),
603        Err(Failed::Soft(error) | Failed::Hard(error)) => Err(error),
604    }
605}
606
607/// The text input function of the type, as `InputFunctionCallSafe` calls it. The outer error is a hard error, such as a type that has no input function. The inner error is a soft error of the input function for a bad value, which `pg_input_is_valid` reports as false. The checks of a domain are not part of the input.
608///
609/// # Errors
610///
611/// `0A000` for a type that has no input function here.
612pub fn input_soft(
613    ty: u32,
614    text: &str,
615    typmod: i32,
616    session: &dyn Session,
617) -> Result<Result<Value>> {
618    match input_value(ty, text, typmod, session, false) {
619        Ok(value) => Ok(Ok(value)),
620        Err(Failed::Soft(error)) => Ok(Err(error)),
621        Err(Failed::Hard(error)) => Err(error),
622    }
623}
624
625/// The error of an input function.
626enum Failed {
627    /// An error that the input function reports with `ereturn`, for a bad value.
628    Soft(Error),
629    /// An error that the input function reports with `ereport`.
630    Hard(Error),
631}
632
633impl From<types::TypeError> for Failed {
634    fn from(error: types::TypeError) -> Failed {
635        Failed::Soft(type_error(error))
636    }
637}
638
639impl From<Error> for Failed {
640    fn from(error: Error) -> Failed {
641        Failed::Hard(error)
642    }
643}
644
645/// `tsqueryin`, with the notice of PostgreSQL for a query with no lexemes when `noisy` is true. A full stack of operators and a query that is too deep are errors that `pg_input_is_valid` does not catch.
646fn tsquery_in(
647    text: &str,
648    session: &dyn Session,
649    noisy: bool,
650) -> std::result::Result<Vec<types::QueryItem>, Failed> {
651    let items = types::tsquery_parse(text).map_err(|error| {
652        if error.sqlstate == SqlState::INTERNAL_ERROR
653            || error.sqlstate == SqlState::STATEMENT_TOO_COMPLEX
654        {
655            Failed::Hard(type_error(error))
656        } else {
657            Failed::from(error)
658        }
659    })?;
660    if noisy && items.is_empty() {
661        session.warn(crate::tsquery::no_lexemes(text));
662    }
663    Ok(items)
664}
665
666/// `xml_in` with the `xmloption` of the session. PostgreSQL reports each warning of libxml2 as it parses, so the warnings come before the error, and they come in the soft input too.
667fn xml_in(text: &[u8], session: &dyn Session) -> std::result::Result<(), types::TypeError> {
668    let document = session.setting("xmloption").is_some_and(|v| v.eq_ignore_ascii_case("document"));
669    types::xml_in(text, document, &mut |warning| {
670        session.warn(Error::new(SqlState::WARNING, warning))
671    })
672}
673
674/// `xml_recv`: the bytes are in the encoding of the XML declaration, or in UTF-8 if it has none. Only UTF-8 and `SQL_ASCII` need no conversion, so the other encodings are not supported yet. The check of the encoding comes after the check of the value, as in PostgreSQL.
675fn xml_recv<'a>(
676    recv: &mut Recv<'a>,
677    session: &dyn Session,
678) -> std::result::Result<&'a str, Received> {
679    let bytes = recv.rest();
680    if let Some(name) = types::xml_encoding(bytes) {
681        match crate::encoding::encoding(&name) {
682            Some("UTF8" | "SQL_ASCII") => {}
683            Some(other) => {
684                return Err(Received::Engine(not_yet(format!(
685                    "binary input of type xml in the encoding {other}"
686                ))));
687            }
688            None => {
689                return Err(Received::Engine(Error::new(
690                    SqlState::INVALID_PARAMETER_VALUE,
691                    format!("invalid encoding name \"{name}\""),
692                )));
693            }
694        }
695    }
696    xml_in(bytes, session)?;
697    Ok(Recv::new(bytes).text()?)
698}
699
700/// `jsonpath_in`. A number that is too large and a path that is too deep are errors that `pg_input_is_valid` does not catch. The error for a code point that is too large is not the same in the soft input, as in PostgreSQL.
701fn jsonpath_in(text: &str, noisy: bool) -> std::result::Result<types::JsonPath, Failed> {
702    types::jsonpath_parse(text, !noisy, &crate::regex::validate).map_err(|error| {
703        if error.sqlstate == SqlState::NUMERIC_VALUE_OUT_OF_RANGE
704            || error.sqlstate == SqlState::STATEMENT_TOO_COMPLEX
705        {
706            Failed::Hard(type_error(error))
707        } else {
708            Failed::from(error)
709        }
710    })
711}
712
713/// The text input function of the type. `noisy` is false for the soft input of `pg_input_is_valid`, which gives no notices, as `escontext` does in PostgreSQL.
714fn input_value(
715    ty: u32,
716    text: &str,
717    typmod: i32,
718    session: &dyn Session,
719    noisy: bool,
720) -> std::result::Result<Value, Failed> {
721    // `domain_in` reads the value with the typmod of the domain.
722    let typmod = match builtin::type_by_oid(ty) {
723        Some(row) if row.kind == b'd' => row.typmod,
724        _ => typmod,
725    };
726    let ty = base_type(ty);
727    if let Some((elem, delim)) = array_of(ty) {
728        // A hard error of an element stops the input of the array as a hard error.
729        let mut hard = None;
730        let array = types::array_in(text, delim, session.array_nulls(), |item| {
731            match input_value(elem, item, typmod, session, noisy) {
732                Ok(value) => Ok(value),
733                Err(Failed::Soft(e)) => Err(types_error(&e)),
734                Err(Failed::Hard(e)) => {
735                    let error = types_error(&e);
736                    hard = Some(e);
737                    Err(error)
738                }
739            }
740        });
741        return match (array, hard) {
742            (_, Some(error)) => Err(Failed::Hard(error)),
743            (array, None) => Ok(Value::Array(Box::new(array?))),
744        };
745    }
746    if let Some(sub) = builtin::range_subtype(ty) {
747        return range_in(ty, sub, text, session, noisy);
748    }
749    if let Some(range) = builtin::multirange_range(ty) {
750        return multirange_in(range, text, session, noisy);
751    }
752    if is_row_type(ty) {
753        return record_in(ty, text, session, noisy);
754    }
755    if is_enum(ty) {
756        let oid = enum_in(ty, text).map_err(Failed::Soft)?;
757        check_safe_enum_use(ty, oid, text).map_err(Failed::Hard)?;
758        return Ok(Value::Oid(oid));
759    }
760    if let Some(kind) = RegKind::from_oid(ty) {
761        return match reg::input(kind, text, session)? {
762            Ok(oid) => Ok(Value::Oid(oid)),
763            Err(error) => Err(Failed::Soft(error)),
764        };
765    }
766    if let Some(kind) = types::GeoKind::from_oid(ty) {
767        return Ok(Value::Bytea(types::geo_in(kind, text)?));
768    }
769    let value = match ty {
770        oid::BOOL => Value::Bool(types::bool_in(text)?),
771        oid::BYTEA => Value::Bytea(types::bytea_in(text)?),
772        oid::CHAR => Value::Char(types::char_in(text)),
773        oid::NAME => Value::text(types::name_in(text)),
774        oid::INT2 => Value::Int2(types::int2_in(text)?),
775        oid::INT4 => Value::Int4(types::int4_in(text)?),
776        oid::TSVECTOR => Value::TsVector(types::tsvector_parse(text)?),
777        oid::TSQUERY => Value::TsQuery(tsquery_in(text, session, noisy)?),
778        oid::JSONPATH => Value::JsonPath(Box::new(jsonpath_in(text, noisy)?)),
779        oid::XML => {
780            xml_in(text.as_bytes(), session)?;
781            Value::text(text)
782        }
783        oid::INT8 => Value::Int8(types::int8_in(text)?),
784        oid::OID => Value::Oid(types::oid_in(text)?),
785        oid::MONEY => Value::Int8(types::cash_in(text)?),
786        oid::MACADDR => Value::Int8(types::macaddr_in(text)?.cast_signed()),
787        oid::MACADDR8 => Value::Int8(types::macaddr8_in(text)?.cast_signed()),
788        oid::BIT => Value::Text(types::bit_in(text, typmod, false)?),
789        oid::VARBIT => Value::Text(types::bit_in(text, typmod, true)?),
790        oid::XID8 => Value::Int8(types::uint64_in(text, "xid8")?.cast_signed()),
791        oid::OID8 => Value::Int8(types::uint64_in(text, "oid8")?.cast_signed()),
792        oid::PG_LSN => Value::Int8(types::pg_lsn_in(text)?.cast_signed()),
793        oid::PG_SNAPSHOT | oid::TXID_SNAPSHOT => Value::Bytea(types::snapshot_in(text)?.to_bytes()),
794        oid::TID => {
795            let (block, offset) = types::tid_in(text)?;
796            tid_value(block, offset)
797        }
798        XID => Value::Oid(types::uint32_in(text, "xid")?),
799        CID => Value::Oid(types::uint32_in(text, "cid")?),
800        oid::ACLITEM => Value::Text(crate::acl::aclitem_in(text, session).map_err(Failed::Soft)?),
801        oid::TEXT | oid::UNKNOWN | oid::CSTRING | oid::REFCURSOR => Value::text(text),
802        // `void_in` takes any text. A `void` value is an empty text, which `void_out` and `void_send` give.
803        oid::VOID => Value::Text(String::new()),
804        oid::VARCHAR => Value::text(types::varchar_in(text, typmod)?),
805        oid::BPCHAR => Value::text(types::bpchar_in(text, typmod)?),
806        oid::JSON => Value::text(types::json_in(text)?),
807        oid::JSONB => Value::Text(types::jsonb_in(text)?),
808        oid::FLOAT4 => Value::Float4(types::float4_in(text)?),
809        oid::FLOAT8 => Value::Float8(types::float8_in(text)?),
810        oid::NUMERIC => Value::Numeric(types::numeric_in(text, typmod)?),
811        oid::UUID => Value::Uuid(types::uuid_in(text)?),
812        oid::INET | oid::CIDR => Value::Inet(types::inet_in(text, ty == oid::CIDR)?),
813        oid::INT2VECTOR => {
814            let values = types::int2vector_in(text)?;
815            Value::Array(Box::new(Array::vector(
816                values.into_iter().map(|v| Some(Value::Int2(v))).collect(),
817            )))
818        }
819        oid::OIDVECTOR => {
820            let values = types::oidvector_in(text)?;
821            Value::Array(Box::new(Array::vector(
822                values.into_iter().map(|v| Some(Value::Oid(v))).collect(),
823            )))
824        }
825        oid::DATE | oid::TIME | oid::TIMETZ | oid::TIMESTAMP | oid::TIMESTAMPTZ => {
826            let zone = session.zone()?;
827            let cx = datetime_input(session, &*zone);
828            match ty {
829                oid::DATE => Value::Date(types::date_in(text, &cx)?),
830                oid::TIME => Value::Time(types::time_in(text, typmod, &cx)?),
831                oid::TIMETZ => {
832                    let (time, zone) = types::timetz_in(text, typmod, &cx)?;
833                    Value::TimeTz(time, zone)
834                }
835                oid::TIMESTAMP => Value::Timestamp(types::timestamp_in(text, typmod, &cx)?),
836                _ => Value::TimestampTz(types::timestamptz_in(text, typmod, &cx)?),
837            }
838        }
839        oid::INTERVAL => {
840            Value::Interval(types::interval_in(text, typmod, session.interval_style())?)
841        }
842        // `pg_node_tree_in`: a stored node tree comes only from the server.
843        oid::PG_NODE_TREE => return Err(Failed::Hard(no_input(ty))),
844        oid::PG_NDISTINCT => Value::Bytea(types::pg_ndistinct_in(text)?),
845        oid::PG_DEPENDENCIES => Value::Bytea(types::pg_dependencies_in(text)?),
846        // `pg_mcv_list_in`: a list of the most common values comes only from `ANALYZE`.
847        oid::PG_MCV_LIST => return Err(Failed::Hard(no_input(ty))),
848        // `gtsvector_in` and `brin_bloom_summary_in`: these values come only from an index.
849        oid::GTSVECTOR | oid::PG_BRIN_BLOOM_SUMMARY => return Err(Failed::Hard(no_input(ty))),
850        // `brin_minmax_multi_summary_in` names the type with no `pg_` prefix.
851        oid::PG_BRIN_MINMAX_MULTI_SUMMARY => {
852            return Err(Failed::Hard(Error::new(
853                SqlState::FEATURE_NOT_SUPPORTED,
854                "cannot accept a value of type brin_minmax_multi_summary".to_string(),
855            )));
856        }
857        // `PSEUDOTYPE_DUMMY_INPUT_FUNC`: a pseudo-type such as `anyelement` has no values. `void` and `record` have input functions.
858        _ if builtin::type_by_oid(ty).is_some_and(|row| row.kind == b'p')
859            && !matches!(ty, oid::VOID | oid::RECORD) =>
860        {
861            return Err(Failed::Hard(no_input(ty)));
862        }
863        _ => return Err(Failed::Hard(not_yet(format!("input of type {}", type_name(ty))))),
864    };
865    Ok(value)
866}
867
868/// `range_in`: the text of the range, then the input function of the subtype for each bound that is not infinite. The error of a bound that is above the other bound is a soft error.
869fn range_in(
870    ty: u32,
871    sub: u32,
872    text: &str,
873    session: &dyn Session,
874    noisy: bool,
875) -> std::result::Result<Value, Failed> {
876    let parsed = types::range_parse(text)?;
877    let bound = |text: Option<&String>| match text {
878        Some(text) => input_value(sub, text, -1, session, noisy),
879        None => Ok(Value::Null),
880    };
881    let lower = bound(parsed.lower.as_ref())?;
882    let upper = bound(parsed.upper.as_ref())?;
883    let range = crate::range::make_range(ty, parsed.flags, lower, upper).map_err(Failed::Soft)?;
884    Ok(Value::Range(Box::new(range)))
885}
886
887/// `multirange_in`: the text of the multirange, then the input function of the range type for each range. The empty ranges are not kept.
888fn multirange_in(
889    range_ty: u32,
890    text: &str,
891    session: &dyn Session,
892    noisy: bool,
893) -> std::result::Result<Value, Failed> {
894    let mut ranges = Vec::new();
895    types::multirange_parse(text, |text| match input_value(range_ty, text, -1, session, noisy)? {
896        Value::Range(range) => {
897            if !range.is_empty() {
898                ranges.push(*range);
899            }
900            Ok(())
901        }
902        _ => Err(Failed::Hard(bad_value())),
903    })?;
904    let ranges = crate::multirange::make_multirange(range_ty, ranges).map_err(Failed::Soft)?;
905    Ok(Value::Multirange(ranges))
906}
907
908/// `range_recv`: the binary form of the range, then the receive function of the subtype for the bytes of each bound. The receive function of a bound does not have to read all its bytes.
909fn range_recv(
910    ty: u32,
911    sub: u32,
912    recv: &mut Recv<'_>,
913    session: &dyn Session,
914) -> std::result::Result<Value, Received> {
915    let (flags, lower, upper) = types::range_recv(recv)?;
916    let bound = |bytes: Option<&[u8]>| match bytes {
917        Some(bytes) => receive_from(sub, &mut Recv::new(bytes), -1, session),
918        None => Ok(Value::Null),
919    };
920    let lower = bound(lower)?;
921    let upper = bound(upper)?;
922    let range = crate::range::make_range(ty, flags, lower, upper).map_err(Received::Engine)?;
923    Ok(Value::Range(Box::new(range)))
924}
925
926/// `multirange_recv`: the binary form of the multirange, then the receive function of the range type for the bytes of each range.
927fn multirange_recv(
928    range_ty: u32,
929    recv: &mut Recv<'_>,
930    session: &dyn Session,
931) -> std::result::Result<Value, Received> {
932    let mut ranges = Vec::new();
933    types::multirange_recv(recv, |bytes| {
934        match receive_from(range_ty, &mut Recv::new(bytes), -1, session)? {
935            Value::Range(range) => {
936                ranges.push(*range);
937                Ok(())
938            }
939            _ => Err(Received::Engine(bad_value())),
940        }
941    })?;
942    let ranges = crate::multirange::make_multirange(range_ty, ranges).map_err(Received::Engine)?;
943    Ok(Value::Multirange(ranges))
944}
945
946/// The error of the input and the receive functions of a type whose values come only from the server.
947fn no_input(ty: u32) -> Error {
948    Error::new(
949        SqlState::FEATURE_NOT_SUPPORTED,
950        format!("cannot accept a value of type {}", type_name(ty)),
951    )
952}
953
954/// `getTypeBinaryInputInfo` and `getTypeBinaryOutputInfo`: the error of a type that has no receive or no send function, such as `aclitem`.
955fn no_binary(ty: u32, what: &str) -> Error {
956    Error::new(
957        SqlState::UNDEFINED_FUNCTION,
958        format!("no binary {what} function available for type {}", type_name(ty)),
959    )
960}
961
962/// An error of the engine as the error of a type function, for an element of an array.
963fn types_error(error: &Error) -> types::TypeError {
964    types::TypeError {
965        sqlstate: error.state(),
966        message: error.message().to_string(),
967        detail: error.detail().map(str::to_string),
968        hint: error.hint().map(str::to_string),
969        context: None,
970    }
971}
972
973/// The binary input function of the type. `number` is the number of the parameter, from 1, for the error of bytes that are left over.
974///
975/// # Errors
976///
977/// `0A000` for a type that has no receive function here, and the error of the receive function for bad data.
978pub fn receive(
979    ty: u32,
980    data: &[u8],
981    typmod: i32,
982    number: usize,
983    session: &dyn Session,
984) -> Result<Value> {
985    let mut recv = Recv::new(data);
986    let value = receive_from(ty, &mut recv, typmod, session).map_err(|e| match e {
987        Received::Type(e) => type_error(e),
988        Received::Engine(e) => e,
989    })?;
990    recv.finish(number).map_err(type_error)?;
991    Ok(value)
992}
993
994/// The error of a receive function.
995enum Received {
996    Type(types::TypeError),
997    Engine(Error),
998}
999
1000impl From<types::TypeError> for Received {
1001    fn from(error: types::TypeError) -> Received {
1002        Received::Type(error)
1003    }
1004}
1005
1006/// The receive function of the type, which reads the bytes of one value.
1007fn receive_from(
1008    ty: u32,
1009    recv: &mut Recv<'_>,
1010    typmod: i32,
1011    session: &dyn Session,
1012) -> std::result::Result<Value, Received> {
1013    let ty = base_type(ty);
1014    if let Some((elem, _)) = array_of(ty) {
1015        let mut failed = None;
1016        let array =
1017            types::array_recv(recv, elem, |item| match receive_from(elem, item, typmod, session) {
1018                Ok(value) => Ok(value),
1019                Err(Received::Type(e)) => Err(e),
1020                Err(Received::Engine(e)) => {
1021                    let text = types_error(&e);
1022                    failed = Some(e);
1023                    Err(text)
1024                }
1025            });
1026        if let Some(error) = failed {
1027            return Err(Received::Engine(error));
1028        }
1029        return Ok(Value::Array(Box::new(array?)));
1030    }
1031    if let Some(sub) = builtin::range_subtype(ty) {
1032        return range_recv(ty, sub, recv, session);
1033    }
1034    if let Some(range) = builtin::multirange_range(ty) {
1035        return multirange_recv(range, recv, session);
1036    }
1037    if let Some(kind) = types::GeoKind::from_oid(ty) {
1038        return Ok(Value::Bytea(types::geo_recv(kind, recv)?));
1039    }
1040    if is_enum(ty) {
1041        let text = recv.text()?;
1042        let oid = enum_in(ty, text).map_err(Received::Engine)?;
1043        check_safe_enum_use(ty, oid, text).map_err(Received::Engine)?;
1044        return Ok(Value::Oid(oid));
1045    }
1046    let value = match ty {
1047        oid::BOOL => Value::Bool(recv.bool()?),
1048        oid::BYTEA => Value::Bytea(recv.rest().to_vec()),
1049        oid::CHAR => Value::Char(recv.byte()?),
1050        oid::NAME => Value::text(types::name_recv(recv.text()?)?),
1051        oid::INT2 => Value::Int2(recv.i16()?),
1052        oid::INT4 => Value::Int4(recv.i32()?),
1053        oid::INT8 | oid::MONEY | oid::XID8 | oid::OID8 | oid::PG_LSN => Value::Int8(recv.i64()?),
1054        // `void_recv` reads no bytes, so a value with bytes is an error.
1055        oid::VOID => Value::Text(String::new()),
1056        XID | CID => Value::Oid(recv.u32()?),
1057        oid::PG_SNAPSHOT | oid::TXID_SNAPSHOT => {
1058            Value::Bytea(types::snapshot_recv(recv)?.to_bytes())
1059        }
1060        oid::MACADDR => Value::Int8(types::macaddr_recv(recv)?.cast_signed()),
1061        oid::MACADDR8 => Value::Int8(types::macaddr8_recv(recv)?.cast_signed()),
1062        oid::BIT => Value::Text(types::bit_recv(recv, typmod, false)?),
1063        oid::VARBIT => Value::Text(types::bit_recv(recv, typmod, true)?),
1064        oid::TID => {
1065            let block = recv.u32()?;
1066            tid_value(block, recv.i16()?.cast_unsigned())
1067        }
1068        oid::TEXT | oid::UNKNOWN | oid::CSTRING | oid::REFCURSOR => Value::text(recv.text()?),
1069        oid::VARCHAR => Value::text(types::varchar_in(recv.text()?, typmod)?),
1070        oid::BPCHAR => Value::text(types::bpchar_in(recv.text()?, typmod)?),
1071        oid::OID => Value::Oid(recv.u32()?),
1072        oid::TSVECTOR => Value::TsVector(types::tsvector_recv(recv)?),
1073        oid::TSQUERY => Value::TsQuery(types::tsquery_recv(recv)?),
1074        oid::JSONPATH => {
1075            Value::JsonPath(Box::new(types::jsonpath_recv(recv, &crate::regex::validate)?))
1076        }
1077        ty if RegKind::from_oid(ty).is_some() => Value::Oid(recv.u32()?),
1078        oid::JSON => Value::text(types::json_in(recv.text()?)?),
1079        oid::XML => Value::text(xml_recv(recv, session)?),
1080        oid::JSONB => Value::Text(types::jsonb_recv(recv.rest())?),
1081        oid::FLOAT4 => Value::Float4(recv.f32()?),
1082        oid::FLOAT8 => Value::Float8(recv.f64()?),
1083        oid::NUMERIC => Value::Numeric(types::numeric_recv(recv, typmod)?),
1084        oid::UUID => Value::Uuid(recv.uuid()?),
1085        oid::INET | oid::CIDR => Value::Inet(types::inet_recv(recv, ty == oid::CIDR)?),
1086        oid::DATE => Value::Date(types::date_recv(recv)?),
1087        oid::TIME => Value::Time(types::time_recv(recv, typmod)?),
1088        oid::TIMETZ => {
1089            let (time, zone) = types::timetz_recv(recv, typmod)?;
1090            Value::TimeTz(time, zone)
1091        }
1092        oid::TIMESTAMP => Value::Timestamp(types::timestamp_recv(recv, typmod)?),
1093        oid::TIMESTAMPTZ => Value::TimestampTz(types::timestamp_recv(recv, typmod)?),
1094        oid::INTERVAL => Value::Interval(types::interval_recv(recv, typmod)?),
1095        oid::INT2VECTOR => {
1096            let values = types::int2vector_recv(recv)?;
1097            Value::Array(Box::new(Array::vector(
1098                values.into_iter().map(|v| Some(Value::Int2(v))).collect(),
1099            )))
1100        }
1101        oid::OIDVECTOR => {
1102            let values = types::oidvector_recv(recv)?;
1103            Value::Array(Box::new(Array::vector(
1104                values.into_iter().map(|v| Some(Value::Oid(v))).collect(),
1105            )))
1106        }
1107        oid::PG_NDISTINCT | oid::PG_DEPENDENCIES | oid::PG_MCV_LIST => {
1108            return Err(Received::Engine(no_input(ty)));
1109        }
1110        oid::ACLITEM => return Err(Received::Engine(no_binary(ty, "input"))),
1111        _ => {
1112            return Err(Received::Engine(not_yet(format!(
1113                "binary input of type {}",
1114                type_name(ty)
1115            ))));
1116        }
1117    };
1118    Ok(value)
1119}
1120
1121/// The kernel of each text input function of a type, such as `int4in` or `numeric_in`, called from SQL: the value of the text for the type of the result, with the typmod of the third argument.
1122pub(crate) fn input_kernel(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1123    let text = args.first().and_then(Value::as_str).ok_or_else(bad_value)?;
1124    let typmod = match args.get(2) {
1125        Some(Value::Int4(typmod)) => *typmod,
1126        _ => -1,
1127    };
1128    input(call.ret, text, typmod, call.session)
1129}
1130
1131/// The kernel of each text output function of a type, such as `int4out` or `array_out`, called from SQL: the text of the value for the type of the argument.
1132pub(crate) fn output_kernel(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1133    let ([ty], [value]) = (call.args, args) else { return Err(bad_value()) };
1134    let mut out = Vec::new();
1135    output(*ty, value, call.session, &mut out)?;
1136    Ok(Value::text(String::from_utf8(out).map_err(|_| bad_value())?))
1137}
1138
1139/// The kernel of each send function of a type, such as `int4send` or `array_send`, called from SQL: the binary output of the value for the type of the argument.
1140pub(crate) fn send_kernel(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1141    let ([ty], [value]) = (call.args, args) else { return Err(bad_value()) };
1142    let mut out = Vec::new();
1143    send(*ty, value, &mut out)?;
1144    Ok(Value::Bytea(out))
1145}
1146
1147/// The binary output function of the type.
1148///
1149/// # Errors
1150///
1151/// `0A000` for a type that has no send function here.
1152pub fn send(ty: u32, value: &Value, out: &mut Vec<u8>) -> Result<()> {
1153    let ty = base_type(ty);
1154    if let Some((elem, _)) = array_of(ty) {
1155        let Value::Array(array) = value else { return Err(bad_value()) };
1156        let mut failed = None;
1157        types::array_send(array, elem, out, |item, out| {
1158            if failed.is_none()
1159                && let Err(error) = send(elem, item, out)
1160            {
1161                failed = Some(error);
1162            }
1163        });
1164        return failed.map_or(Ok(()), Err);
1165    }
1166    if is_enum(ty)
1167        && let Value::Oid(label) = value
1168    {
1169        out.extend_from_slice(enum_label(ty, *label)?.as_bytes());
1170        return Ok(());
1171    }
1172    match (ty, value) {
1173        (oid::ACLITEM, _) => return Err(no_binary(ty, "output")),
1174        (oid::ANYARRAY, _) => {
1175            return Err(not_yet(format!("binary output of type {}", type_name(ty))));
1176        }
1177        (oid::MACADDR, Value::Int8(v)) => out.extend_from_slice(&v.to_be_bytes()[2..]),
1178        (oid::TID, Value::Int8(v)) => {
1179            let (block, offset) = tid_parts(*v)?;
1180            out.extend_from_slice(&block.to_be_bytes());
1181            out.extend_from_slice(&offset.to_be_bytes());
1182        }
1183        (_, Value::Bool(v)) => out.push(u8::from(*v)),
1184        (_, Value::Int2(v)) => out.extend_from_slice(&v.to_be_bytes()),
1185        (_, Value::Int4(v) | Value::Date(v)) => out.extend_from_slice(&v.to_be_bytes()),
1186        (_, Value::Int8(v) | Value::Time(v) | Value::Timestamp(v) | Value::TimestampTz(v)) => {
1187            out.extend_from_slice(&v.to_be_bytes());
1188        }
1189        (_, Value::Float4(v)) => out.extend_from_slice(&v.to_be_bytes()),
1190        (_, Value::Float8(v)) => out.extend_from_slice(&v.to_be_bytes()),
1191        (_, Value::Numeric(v)) => types::numeric_send(v, out),
1192        (_, Value::Oid(v)) => out.extend_from_slice(&v.to_be_bytes()),
1193        (_, Value::Char(v)) => out.push(*v),
1194        (oid::JSONB, Value::Text(v)) => types::jsonb_send(v, out),
1195        (oid::BIT | oid::VARBIT, Value::Text(v)) => types::bit_send(v, out),
1196        // `xml_send` gives the warning of `xml_out` for a declaration that does not parse, but each stored value has a declaration that parses.
1197        (oid::XML, Value::Text(v)) => {
1198            let _ = types::xml_out(v, out);
1199        }
1200        (_, Value::Text(v)) => out.extend_from_slice(v.as_bytes()),
1201        (_, Value::Bytea(v)) => out.extend_from_slice(v),
1202        (_, Value::Uuid(v)) => out.extend_from_slice(v),
1203        (_, Value::Inet(v)) => types::inet_send(v, ty == oid::CIDR, out),
1204        (_, Value::TimeTz(time, zone)) => {
1205            out.extend_from_slice(&time.to_be_bytes());
1206            out.extend_from_slice(&zone.to_be_bytes());
1207        }
1208        (_, Value::Interval(v)) => types::interval_send(v, out),
1209        (oid::INT2VECTOR, Value::Array(array)) => {
1210            let values = vector(array, |v| match v {
1211                Value::Int2(n) => Some(*n),
1212                _ => None,
1213            })?;
1214            types::int2vector_send(&values, out);
1215        }
1216        (oid::OIDVECTOR, Value::Array(array)) => {
1217            let values = vector(array, Value::as_oid)?;
1218            types::oidvector_send(&values, out);
1219        }
1220        (_, Value::Record(record)) => record_send(record, out)?,
1221        (_, Value::Range(range)) => range_send(ty, range, out)?,
1222        (_, Value::Multirange(ranges)) => multirange_send(ty, ranges, out)?,
1223        (_, Value::TsVector(lexemes)) => types::tsvector_send(lexemes, out),
1224        (_, Value::TsQuery(items)) => types::tsquery_send(items, out),
1225        (_, Value::JsonPath(path)) => types::jsonpath_send(path, out),
1226        (_, Value::Null) => return Err(Error::internal("the send function got a null value")),
1227        _ => return Err(not_yet(format!("binary output of type {}", type_name(ty)))),
1228    }
1229    Ok(())
1230}