sqlx_postgres/types/
citext.rs

1use crate::types::array_compatible;
2use crate::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueRef, Postgres};
3use sqlx_core::decode::Decode;
4use sqlx_core::encode::{Encode, IsNull};
5use sqlx_core::error::BoxDynError;
6use sqlx_core::types::Type;
7use std::fmt;
8use std::fmt::{Debug, Display, Formatter};
9use std::ops::Deref;
10use std::str::FromStr;
11
12/// Case-insensitive text (`citext`) support for Postgres.
13///
14/// Note that SQLx considers the `citext` type to be compatible with `String`
15/// and its various derivatives, so direct usage of this type is generally unnecessary.
16///
17/// However, it may be needed, for example, when binding a `citext[]` array,
18/// as Postgres will generally not accept a `text[]` array (mapped from `Vec<String>`) in its place.
19///
20/// See [the Postgres manual, Appendix F, Section 10][PG.F.10] for details on using `citext`.
21///
22/// [PG.F.10]: https://www.postgresql.org/docs/current/citext.html
23///
24/// ### Note: Extension Required
25/// The `citext` extension is not enabled by default in Postgres. You will need to do so explicitly:
26///
27/// ```ignore
28/// CREATE EXTENSION IF NOT EXISTS "citext";
29/// ```
30///
31/// ### Note: `PartialEq` is Case-Sensitive
32/// This type derives `PartialEq` which forwards to the implementation on `String`, which
33/// is case-sensitive. This impl exists mainly for testing.
34///
35/// To properly emulate the case-insensitivity of `citext` would require use of locale-aware
36/// functions in `libc`, and even then would require querying the locale of the database server
37/// and setting it locally, which is unsafe.
38#[derive(Clone, Debug, Default, PartialEq)]
39pub struct PgCiText(pub String);
40
41impl Type<Postgres> for PgCiText {
42    fn type_info() -> PgTypeInfo {
43        // Since `citext` is enabled by an extension, it does not have a stable OID.
44        PgTypeInfo::with_name("citext")
45    }
46
47    fn compatible(ty: &PgTypeInfo) -> bool {
48        <&str as Type<Postgres>>::compatible(ty)
49    }
50}
51
52impl Deref for PgCiText {
53    type Target = str;
54
55    fn deref(&self) -> &Self::Target {
56        self.0.as_str()
57    }
58}
59
60impl From<String> for PgCiText {
61    fn from(value: String) -> Self {
62        Self(value)
63    }
64}
65
66impl From<PgCiText> for String {
67    fn from(value: PgCiText) -> Self {
68        value.0
69    }
70}
71
72impl FromStr for PgCiText {
73    type Err = core::convert::Infallible;
74
75    fn from_str(s: &str) -> Result<Self, Self::Err> {
76        Ok(PgCiText(s.parse()?))
77    }
78}
79
80impl Display for PgCiText {
81    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
82        f.write_str(&self.0)
83    }
84}
85
86impl PgHasArrayType for PgCiText {
87    fn array_type_info() -> PgTypeInfo {
88        PgTypeInfo::with_name("_citext")
89    }
90
91    fn array_compatible(ty: &PgTypeInfo) -> bool {
92        array_compatible::<&str>(ty)
93    }
94}
95
96impl Encode<'_, Postgres> for PgCiText {
97    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
98        <&str as Encode<Postgres>>::encode(&**self, buf)
99    }
100}
101
102impl Decode<'_, Postgres> for PgCiText {
103    fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
104        Ok(PgCiText(value.as_str()?.to_owned()))
105    }
106}