1pub use aggregate::entity::{AggregateTypeEntity, PgAggregateEntity};
19pub use aggregate::{
20 AggregateType, AggregateTypeList, FinalizeModify, ParallelOption, PgAggregate,
21};
22pub use control_file::ControlFile;
23pub use enrich::CodeEnrichment;
24pub use extension_sql::entity::{ExtensionSqlEntity, SqlDeclaredEntity};
25pub use extension_sql::{ExtensionSql, ExtensionSqlFile, SqlDeclared};
26pub use extern_args::{parse_extern_attributes, ExternArgs};
27pub use mapping::RustSqlMapping;
28pub use pg_extern::entity::{
29 PgCastEntity, PgExternArgumentEntity, PgExternEntity, PgExternReturnEntity,
30 PgExternReturnEntityIteratedItem, PgOperatorEntity,
31};
32pub use pg_extern::{NameMacro, PgCast, PgExtern, PgExternArgument, PgOperator};
33pub use pg_trigger::attribute::PgTriggerAttribute;
34pub use pg_trigger::entity::PgTriggerEntity;
35pub use pg_trigger::PgTrigger;
36pub use pgrx_sql::PgrxSql;
37pub use positioning_ref::PositioningRef;
38pub use postgres_enum::entity::PostgresEnumEntity;
39pub use postgres_enum::PostgresEnum;
40pub use postgres_hash::entity::PostgresHashEntity;
41pub use postgres_hash::PostgresHash;
42pub use postgres_ord::entity::PostgresOrdEntity;
43pub use postgres_ord::PostgresOrd;
44pub use postgres_type::entity::PostgresTypeEntity;
45pub use postgres_type::PostgresTypeDerive;
46pub use schema::entity::SchemaEntity;
47pub use schema::Schema;
48pub use to_sql::entity::ToSqlConfigEntity;
49pub use to_sql::{ToSql, ToSqlConfig};
50pub use used_type::{UsedType, UsedTypeEntity};
51
52pub(crate) mod aggregate;
53pub(crate) mod composite_type;
54pub(crate) mod control_file;
55pub(crate) mod enrich;
56pub(crate) mod extension_sql;
57pub(crate) mod extern_args;
58pub(crate) mod finfo;
59#[macro_use]
60pub(crate) mod fmt;
61pub mod lifetimes;
62pub(crate) mod mapping;
63pub mod metadata;
64pub(crate) mod pg_extern;
65pub(crate) mod pg_trigger;
66pub(crate) mod pgrx_attribute;
67pub(crate) mod pgrx_sql;
68pub mod positioning_ref;
69pub(crate) mod postgres_enum;
70pub(crate) mod postgres_hash;
71pub(crate) mod postgres_ord;
72pub(crate) mod postgres_type;
73pub(crate) mod schema;
74pub(crate) mod to_sql;
75pub(crate) mod used_type;
76
77pub trait SqlGraphIdentifier {
79 fn dot_identifier(&self) -> String;
85
86 fn rust_identifier(&self) -> String;
91
92 fn file(&self) -> Option<&'static str>;
93
94 fn line(&self) -> Option<u32>;
95}
96
97#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
99pub enum SqlGraphEntity {
100 ExtensionRoot(ControlFile),
101 Schema(SchemaEntity),
102 CustomSql(ExtensionSqlEntity),
103 Function(PgExternEntity),
104 Type(PostgresTypeEntity),
105 BuiltinType(String),
106 Enum(PostgresEnumEntity),
107 Ord(PostgresOrdEntity),
108 Hash(PostgresHashEntity),
109 Aggregate(PgAggregateEntity),
110 Trigger(PgTriggerEntity),
111}
112
113impl SqlGraphEntity {
114 pub fn sql_anchor_comment(&self) -> String {
115 let maybe_file_and_line = if let (Some(file), Some(line)) = (self.file(), self.line()) {
116 format!("-- {file}:{line}\n")
117 } else {
118 String::default()
119 };
120 format!(
121 "\
122 {maybe_file_and_line}\
123 -- {rust_identifier}\
124 ",
125 rust_identifier = self.rust_identifier(),
126 )
127 }
128
129 pub fn id_or_name_matches(&self, ty_id: &::core::any::TypeId, name: &str) -> bool {
130 match self {
131 SqlGraphEntity::Enum(entity) => entity.id_matches(ty_id),
132 SqlGraphEntity::Type(entity) => entity.id_matches(ty_id),
133 SqlGraphEntity::BuiltinType(string) => string == name,
134 _ => false,
135 }
136 }
137
138 pub fn type_matches(&self, arg: &dyn TypeIdentifiable) -> bool {
139 self.id_or_name_matches(arg.ty_id(), arg.ty_name())
140 }
141}
142
143pub trait TypeMatch {
144 fn id_matches(&self, arg: &core::any::TypeId) -> bool;
145}
146
147pub fn type_keyed<'a, 'b, A: TypeMatch, B>((a, b): (&'a A, &'b B)) -> (&'a dyn TypeMatch, &'b B) {
148 (a, b)
149}
150
151pub trait TypeIdentifiable {
152 fn ty_id(&self) -> &core::any::TypeId;
153 fn ty_name(&self) -> &str;
154}
155
156impl SqlGraphIdentifier for SqlGraphEntity {
157 fn dot_identifier(&self) -> String {
158 match self {
159 SqlGraphEntity::Schema(item) => item.dot_identifier(),
160 SqlGraphEntity::CustomSql(item) => item.dot_identifier(),
161 SqlGraphEntity::Function(item) => item.dot_identifier(),
162 SqlGraphEntity::Type(item) => item.dot_identifier(),
163 SqlGraphEntity::BuiltinType(item) => format!("preexisting type {item}"),
164 SqlGraphEntity::Enum(item) => item.dot_identifier(),
165 SqlGraphEntity::Ord(item) => item.dot_identifier(),
166 SqlGraphEntity::Hash(item) => item.dot_identifier(),
167 SqlGraphEntity::Aggregate(item) => item.dot_identifier(),
168 SqlGraphEntity::Trigger(item) => item.dot_identifier(),
169 SqlGraphEntity::ExtensionRoot(item) => item.dot_identifier(),
170 }
171 }
172
173 fn rust_identifier(&self) -> String {
174 match self {
175 SqlGraphEntity::Schema(item) => item.rust_identifier(),
176 SqlGraphEntity::CustomSql(item) => item.rust_identifier(),
177 SqlGraphEntity::Function(item) => item.rust_identifier(),
178 SqlGraphEntity::Type(item) => item.rust_identifier(),
179 SqlGraphEntity::BuiltinType(item) => item.to_string(),
180 SqlGraphEntity::Enum(item) => item.rust_identifier(),
181 SqlGraphEntity::Ord(item) => item.rust_identifier(),
182 SqlGraphEntity::Hash(item) => item.rust_identifier(),
183 SqlGraphEntity::Aggregate(item) => item.rust_identifier(),
184 SqlGraphEntity::Trigger(item) => item.rust_identifier(),
185 SqlGraphEntity::ExtensionRoot(item) => item.rust_identifier(),
186 }
187 }
188
189 fn file(&self) -> Option<&'static str> {
190 match self {
191 SqlGraphEntity::Schema(item) => item.file(),
192 SqlGraphEntity::CustomSql(item) => item.file(),
193 SqlGraphEntity::Function(item) => item.file(),
194 SqlGraphEntity::Type(item) => item.file(),
195 SqlGraphEntity::BuiltinType(_item) => None,
196 SqlGraphEntity::Enum(item) => item.file(),
197 SqlGraphEntity::Ord(item) => item.file(),
198 SqlGraphEntity::Hash(item) => item.file(),
199 SqlGraphEntity::Aggregate(item) => item.file(),
200 SqlGraphEntity::Trigger(item) => item.file(),
201 SqlGraphEntity::ExtensionRoot(item) => item.file(),
202 }
203 }
204
205 fn line(&self) -> Option<u32> {
206 match self {
207 SqlGraphEntity::Schema(item) => item.line(),
208 SqlGraphEntity::CustomSql(item) => item.line(),
209 SqlGraphEntity::Function(item) => item.line(),
210 SqlGraphEntity::Type(item) => item.line(),
211 SqlGraphEntity::BuiltinType(_item) => None,
212 SqlGraphEntity::Enum(item) => item.line(),
213 SqlGraphEntity::Ord(item) => item.line(),
214 SqlGraphEntity::Hash(item) => item.line(),
215 SqlGraphEntity::Aggregate(item) => item.line(),
216 SqlGraphEntity::Trigger(item) => item.line(),
217 SqlGraphEntity::ExtensionRoot(item) => item.line(),
218 }
219 }
220}
221
222impl ToSql for SqlGraphEntity {
223 fn to_sql(&self, context: &PgrxSql) -> eyre::Result<String> {
224 match self {
225 SqlGraphEntity::Schema(SchemaEntity { name: "public" | "pg_catalog", .. }) => {
226 Ok(String::default())
227 }
228 SqlGraphEntity::Schema(item) => item.to_sql(context),
229 SqlGraphEntity::CustomSql(item) => item.to_sql(context),
230 SqlGraphEntity::Function(item) => {
231 if let Some(result) = item.to_sql_config.to_sql(self, context) {
232 result
233 } else if context
234 .graph
235 .neighbors_undirected(*context.externs.get(item).unwrap())
236 .any(|neighbor| {
237 let SqlGraphEntity::Type(PostgresTypeEntity {
238 in_fn,
239 in_fn_module_path,
240 out_fn,
241 out_fn_module_path,
242 ..
243 }) = &context.graph[neighbor]
244 else {
245 return false;
246 };
247
248 let is_in_fn = item.full_path.starts_with(in_fn_module_path)
249 && item.full_path.ends_with(in_fn);
250 let is_out_fn = item.full_path.starts_with(out_fn_module_path)
251 && item.full_path.ends_with(out_fn);
252 is_in_fn || is_out_fn
253 })
254 {
255 Ok(String::default())
256 } else {
257 item.to_sql(context)
258 }
259 }
260 SqlGraphEntity::Type(item) => {
261 item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
262 }
263 SqlGraphEntity::BuiltinType(_) => Ok(String::default()),
264 SqlGraphEntity::Enum(item) => {
265 item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
266 }
267 SqlGraphEntity::Ord(item) => {
268 item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
269 }
270 SqlGraphEntity::Hash(item) => {
271 item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
272 }
273 SqlGraphEntity::Aggregate(item) => {
274 item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
275 }
276 SqlGraphEntity::Trigger(item) => {
277 item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
278 }
279 SqlGraphEntity::ExtensionRoot(item) => item.to_sql(context),
280 }
281 }
282}
283
284pub fn ident_is_acceptable_to_postgres(ident: &syn::Ident) -> Result<(), syn::Error> {
294 const POSTGRES_IDENTIFIER_MAX_LEN: usize = 64;
298
299 let len = ident.to_string().len();
300 if len >= POSTGRES_IDENTIFIER_MAX_LEN {
301 return Err(syn::Error::new(
302 ident.span(),
303 format!(
304 "Identifier `{ident}` was {len} characters long, PostgreSQL will truncate identifiers with less than \
305 {POSTGRES_IDENTIFIER_MAX_LEN} characters, opt for an identifier which Postgres won't truncate"
306 )
307 ));
308 }
309
310 Ok(())
311}