elefant_tools/storage/postgres/
postgres_instance_storage.rs1use crate::postgres_client_wrapper::{FromPgChar, RowEnumExt};
2use crate::quoting::AllowedKeywordUsage;
3use crate::schema_reader::SchemaReader;
4use crate::storage::postgres::parallel_copy_destination::ParallelSafePostgresInstanceCopyDestinationStorage;
5use crate::storage::postgres::parallel_copy_source::ParallelSafePostgresInstanceCopySourceStorage;
6use crate::storage::postgres::sequential_copy_destination::SequentialSafePostgresInstanceCopyDestinationStorage;
7use crate::storage::postgres::sequential_copy_source::SequentialSafePostgresInstanceCopySourceStorage;
8use crate::{
9 BaseCopyTarget, CopyDestinationFactory, CopySourceFactory, DataFormat, ElefantToolsError,
10 IdentifierQuoter, PostgresClientWrapper, PostgresDatabase, SequentialOrParallel,
11 SupportedParallelism,
12};
13use elefant_client::PostgresDataRow;
14use std::collections::HashMap;
15use std::sync::Arc;
16use tracing::instrument;
17
18pub struct PostgresInstanceStorage<'a> {
20 pub(crate) connection: &'a PostgresClientWrapper,
21 pub(crate) postgres_version: String,
22 pub(crate) identifier_quoter: Arc<IdentifierQuoter>,
23}
24
25impl<'a> PostgresInstanceStorage<'a> {
26 #[instrument(skip_all)]
27 pub async fn new(connection: &'a PostgresClientWrapper) -> crate::Result<Self> {
28 let postgres_version = connection.get_single_result("select version()").await?;
29
30 let keywords = connection
31 .get_results::<Keyword>(
32 "select word, catcode from pg_get_keywords() where catcode <> 'U'",
33 )
34 .await?;
35
36 let mut keyword_info = HashMap::new();
37
38 for keyword in keywords {
39 keyword_info.insert(
40 keyword.word,
41 AllowedKeywordUsage {
42 column_name: keyword.category == KeywordType::AllowedInColumnName,
43 type_or_function_name: keyword.category
44 == KeywordType::AllowedInTypeOrFunctionName,
45 },
46 );
47 }
48
49 let quoter = IdentifierQuoter::new(keyword_info);
50
51 Ok(PostgresInstanceStorage {
52 connection,
53 postgres_version,
54 identifier_quoter: Arc::new(quoter),
55 })
56 }
57
58 pub fn get_identifier_quoter(&self) -> Arc<IdentifierQuoter> {
59 self.identifier_quoter.clone()
60 }
61}
62
63struct Keyword {
64 word: String,
65 category: KeywordType,
66}
67
68impl<'a> elefant_client::FromSqlRow<'a> for Keyword {
69 fn from_sql_row(
70 row: &'a PostgresDataRow<'_, '_>,
71 ) -> Result<Self, elefant_client::ElefantClientError> {
72 Ok(Keyword {
73 word: row.get(0)?,
74 category: row.try_get_enum_value(1)?,
75 })
76 }
77}
78
79#[derive(Eq, PartialEq, Debug)]
80enum KeywordType {
81 Unreserved,
82 AllowedInColumnName,
83 AllowedInTypeOrFunctionName,
84 Reserved,
85}
86
87impl FromPgChar for KeywordType {
88 fn from_pg_char(c: char) -> crate::Result<Self> {
89 match c {
90 'U' => Ok(KeywordType::Unreserved),
91 'C' => Ok(KeywordType::AllowedInColumnName),
92 'T' => Ok(KeywordType::AllowedInTypeOrFunctionName),
93 'R' => Ok(KeywordType::Reserved),
94 _ => Err(ElefantToolsError::InvalidKeywordType(c.to_string())),
95 }
96 }
97}
98
99impl BaseCopyTarget for PostgresInstanceStorage<'_> {
100 async fn supported_data_format(&self) -> crate::Result<Vec<DataFormat>> {
101 Ok(vec![
102 DataFormat::Text,
103 DataFormat::PostgresBinary {
104 postgres_version: Some(self.postgres_version.clone()),
105 },
106 ])
107 }
108}
109
110impl<'a> CopySourceFactory for PostgresInstanceStorage<'a> {
111 type SequentialSource = SequentialSafePostgresInstanceCopySourceStorage<'a>;
112 type ParallelSource = ParallelSafePostgresInstanceCopySourceStorage<'a>;
113
114 async fn get_introspection(&self) -> crate::Result<PostgresDatabase> {
115 let reader = SchemaReader::new(self.connection);
116 reader.introspect_database().await
117 }
118
119 async fn create_source(
120 &self,
121 ) -> crate::Result<SequentialOrParallel<Self::SequentialSource, Self::ParallelSource>> {
122 let parallel = ParallelSafePostgresInstanceCopySourceStorage::new(self).await?;
123
124 Ok(SequentialOrParallel::Parallel(parallel))
125 }
126
127 async fn create_sequential_source(&self) -> crate::Result<Self::SequentialSource> {
128 let seq = SequentialSafePostgresInstanceCopySourceStorage::new(self).await?;
129
130 Ok(seq)
131 }
132
133 fn supported_parallelism(&self) -> SupportedParallelism {
134 SupportedParallelism::Parallel
135 }
136}
137
138impl<'a> CopyDestinationFactory<'a> for PostgresInstanceStorage<'a> {
139 type SequentialDestination = SequentialSafePostgresInstanceCopyDestinationStorage<'a>;
140 type ParallelDestination = ParallelSafePostgresInstanceCopyDestinationStorage<'a>;
141
142 async fn create_destination(
143 &'a mut self,
144 ) -> crate::Result<SequentialOrParallel<Self::SequentialDestination, Self::ParallelDestination>>
145 {
146 let par = ParallelSafePostgresInstanceCopyDestinationStorage::new(self);
147
148 Ok(SequentialOrParallel::Parallel(par))
149 }
150
151 async fn create_sequential_destination(
152 &'a mut self,
153 ) -> crate::Result<Self::SequentialDestination> {
154 let seq = SequentialSafePostgresInstanceCopyDestinationStorage::new(self).await?;
155
156 Ok(seq)
157 }
158
159 fn supported_parallelism(&self) -> SupportedParallelism {
160 SupportedParallelism::Parallel
161 }
162}