diesel/pg/
metadata_lookup.rs1#![allow(unused_parens)]
3#![cfg_attr(
6 any(feature = "huge-tables", feature = "large-tables"),
7 allow(deprecated)
8)]
9
10use super::backend::{FailedToLookupTypeError, InnerPgTypeMetadata};
11use super::{Pg, PgTypeMetadata};
12use crate::connection::{DefaultLoadingMode, LoadConnection};
13use crate::prelude::*;
14
15use std::borrow::Cow;
16use std::collections::HashMap;
17
18#[cfg(feature = "postgres_backend")]
24pub trait PgMetadataLookup {
25 fn lookup_type(&mut self, type_name: &str, schema: Option<&str>) -> PgTypeMetadata;
31
32 #[diesel_derives::__diesel_public_if(
38 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
39 )]
40 fn as_any<'a>(&mut self) -> &mut (dyn std::any::Any + 'a)
41 where
42 Self: 'a,
43 {
44 unimplemented!()
45 }
46}
47
48impl<T> PgMetadataLookup for T
49where
50 T: Connection<Backend = Pg> + GetPgMetadataCache + LoadConnection<DefaultLoadingMode>,
51{
52 fn lookup_type(&mut self, type_name: &str, schema: Option<&str>) -> PgTypeMetadata {
53 let cache_key = PgMetadataCacheKey {
54 schema: schema.map(Cow::Borrowed),
55 type_name: Cow::Borrowed(type_name),
56 };
57
58 {
59 let metadata_cache = self.get_metadata_cache();
60
61 if let Some(metadata) = metadata_cache.lookup_type(&cache_key) {
62 return metadata;
63 }
64 }
65
66 let r = lookup_type(&cache_key, self);
67
68 match r {
69 Ok(type_metadata) => {
70 self.get_metadata_cache()
71 .store_type(cache_key, type_metadata);
72 PgTypeMetadata(Ok(type_metadata))
73 }
74 Err(_e) => PgTypeMetadata(Err(FailedToLookupTypeError::new_internal(
75 cache_key.into_owned(),
76 ))),
77 }
78 }
79
80 fn as_any<'a>(&mut self) -> &mut (dyn std::any::Any + 'a)
81 where
82 Self: 'a,
83 {
84 self
85 }
86}
87
88#[cfg_attr(
93 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
94 cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")
95)]
96pub trait GetPgMetadataCache {
97 fn get_metadata_cache(&mut self) -> &mut PgMetadataCache;
99}
100
101fn lookup_type<T: Connection<Backend = Pg> + LoadConnection<DefaultLoadingMode>>(
102 cache_key: &PgMetadataCacheKey<'_>,
103 conn: &mut T,
104) -> QueryResult<InnerPgTypeMetadata> {
105 let metadata_query = pg_type::table.select((pg_type::oid, pg_type::typarray));
106
107 let metadata = if let Some(schema) = cache_key.schema.as_deref() {
108 metadata_query
111 .inner_join(pg_namespace::table)
112 .filter(pg_type::typname.eq(&cache_key.type_name))
113 .filter(pg_namespace::nspname.eq(schema))
114 .first(conn)?
115 } else {
116 metadata_query
122 .filter(
123 pg_type::oid.eq(crate::dsl::sql("quote_ident(")
124 .bind::<crate::sql_types::Text, _>(&cache_key.type_name)
125 .sql(")::regtype::oid")),
126 )
127 .first(conn)?
128 };
129
130 Ok(metadata)
131}
132
133#[derive(Hash, PartialEq, Eq, Debug, Clone)]
136#[cfg_attr(
137 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
138 cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")
139)]
140pub struct PgMetadataCacheKey<'a> {
141 pub(in crate::pg) schema: Option<Cow<'a, str>>,
142 pub(in crate::pg) type_name: Cow<'a, str>,
143}
144
145impl<'a> PgMetadataCacheKey<'a> {
146 pub fn new(schema: Option<Cow<'a, str>>, type_name: Cow<'a, str>) -> Self {
149 Self { schema, type_name }
150 }
151
152 pub fn into_owned(self) -> PgMetadataCacheKey<'static> {
155 let PgMetadataCacheKey { schema, type_name } = self;
156 PgMetadataCacheKey {
157 schema: schema.map(|s| Cow::Owned(s.into_owned())),
158 type_name: Cow::Owned(type_name.into_owned()),
159 }
160 }
161}
162
163#[allow(missing_debug_implementations)]
167#[derive(Default)]
168#[cfg_attr(
169 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
170 cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")
171)]
172pub struct PgMetadataCache {
173 cache: HashMap<PgMetadataCacheKey<'static>, InnerPgTypeMetadata>,
174}
175
176impl PgMetadataCache {
177 pub fn new() -> Self {
179 Default::default()
180 }
181
182 pub fn lookup_type(&self, type_name: &PgMetadataCacheKey<'_>) -> Option<PgTypeMetadata> {
184 Some(PgTypeMetadata(Ok(*self.cache.get(type_name)?)))
185 }
186
187 pub fn store_type(
189 &mut self,
190 type_name: PgMetadataCacheKey<'_>,
191 type_metadata: impl Into<InnerPgTypeMetadata>,
192 ) {
193 self.cache
194 .insert(type_name.into_owned(), type_metadata.into());
195 }
196}
197
198table! {
199 pg_type (oid) {
200 oid -> Oid,
201 typname -> Text,
202 typarray -> Oid,
203 typnamespace -> Oid,
204 }
205}
206
207table! {
208 pg_namespace (oid) {
209 oid -> Oid,
210 nspname -> Text,
211 }
212}
213
214joinable!(pg_type -> pg_namespace(typnamespace));
215allow_tables_to_appear_in_same_query!(pg_type, pg_namespace);
216
217define_sql_function! { fn pg_my_temp_schema() -> Oid; }