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