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