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
97pub use postgres_type::Alignment;
98
99#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
101pub enum SqlGraphEntity {
102 ExtensionRoot(ControlFile),
103 Schema(SchemaEntity),
104 CustomSql(ExtensionSqlEntity),
105 Function(PgExternEntity),
106 Type(PostgresTypeEntity),
107 BuiltinType(String),
108 Enum(PostgresEnumEntity),
109 Ord(PostgresOrdEntity),
110 Hash(PostgresHashEntity),
111 Aggregate(PgAggregateEntity),
112 Trigger(PgTriggerEntity),
113}
114
115impl SqlGraphEntity {
116 pub fn sql_anchor_comment(&self) -> String {
117 let maybe_file_and_line = if let (Some(file), Some(line)) = (self.file(), self.line()) {
118 format!("-- {file}:{line}\n")
119 } else {
120 String::default()
121 };
122 format!(
123 "\
124 {maybe_file_and_line}\
125 -- {rust_identifier}\
126 ",
127 rust_identifier = self.rust_identifier(),
128 )
129 }
130
131 pub fn id_or_name_matches(&self, ty_id: &::core::any::TypeId, name: &str) -> bool {
132 match self {
133 SqlGraphEntity::Enum(entity) => entity.id_matches(ty_id),
134 SqlGraphEntity::Type(entity) => entity.id_matches(ty_id),
135 SqlGraphEntity::BuiltinType(string) => string == name,
136 _ => false,
137 }
138 }
139
140 pub fn type_matches(&self, arg: &dyn TypeIdentifiable) -> bool {
141 self.id_or_name_matches(arg.ty_id(), arg.ty_name())
142 }
143}
144
145pub trait TypeMatch {
146 fn id_matches(&self, arg: &core::any::TypeId) -> bool;
147}
148
149pub fn type_keyed<'a, 'b, A: TypeMatch, B>((a, b): (&'a A, &'b B)) -> (&'a dyn TypeMatch, &'b B) {
150 (a, b)
151}
152
153pub trait TypeIdentifiable {
154 fn ty_id(&self) -> &core::any::TypeId;
155 fn ty_name(&self) -> &str;
156}
157
158impl SqlGraphIdentifier for SqlGraphEntity {
159 fn dot_identifier(&self) -> String {
160 match self {
161 SqlGraphEntity::Schema(item) => item.dot_identifier(),
162 SqlGraphEntity::CustomSql(item) => item.dot_identifier(),
163 SqlGraphEntity::Function(item) => item.dot_identifier(),
164 SqlGraphEntity::Type(item) => item.dot_identifier(),
165 SqlGraphEntity::BuiltinType(item) => format!("preexisting type {item}"),
166 SqlGraphEntity::Enum(item) => item.dot_identifier(),
167 SqlGraphEntity::Ord(item) => item.dot_identifier(),
168 SqlGraphEntity::Hash(item) => item.dot_identifier(),
169 SqlGraphEntity::Aggregate(item) => item.dot_identifier(),
170 SqlGraphEntity::Trigger(item) => item.dot_identifier(),
171 SqlGraphEntity::ExtensionRoot(item) => item.dot_identifier(),
172 }
173 }
174
175 fn rust_identifier(&self) -> String {
176 match self {
177 SqlGraphEntity::Schema(item) => item.rust_identifier(),
178 SqlGraphEntity::CustomSql(item) => item.rust_identifier(),
179 SqlGraphEntity::Function(item) => item.rust_identifier(),
180 SqlGraphEntity::Type(item) => item.rust_identifier(),
181 SqlGraphEntity::BuiltinType(item) => item.to_string(),
182 SqlGraphEntity::Enum(item) => item.rust_identifier(),
183 SqlGraphEntity::Ord(item) => item.rust_identifier(),
184 SqlGraphEntity::Hash(item) => item.rust_identifier(),
185 SqlGraphEntity::Aggregate(item) => item.rust_identifier(),
186 SqlGraphEntity::Trigger(item) => item.rust_identifier(),
187 SqlGraphEntity::ExtensionRoot(item) => item.rust_identifier(),
188 }
189 }
190
191 fn file(&self) -> Option<&'static str> {
192 match self {
193 SqlGraphEntity::Schema(item) => item.file(),
194 SqlGraphEntity::CustomSql(item) => item.file(),
195 SqlGraphEntity::Function(item) => item.file(),
196 SqlGraphEntity::Type(item) => item.file(),
197 SqlGraphEntity::BuiltinType(_item) => None,
198 SqlGraphEntity::Enum(item) => item.file(),
199 SqlGraphEntity::Ord(item) => item.file(),
200 SqlGraphEntity::Hash(item) => item.file(),
201 SqlGraphEntity::Aggregate(item) => item.file(),
202 SqlGraphEntity::Trigger(item) => item.file(),
203 SqlGraphEntity::ExtensionRoot(item) => item.file(),
204 }
205 }
206
207 fn line(&self) -> Option<u32> {
208 match self {
209 SqlGraphEntity::Schema(item) => item.line(),
210 SqlGraphEntity::CustomSql(item) => item.line(),
211 SqlGraphEntity::Function(item) => item.line(),
212 SqlGraphEntity::Type(item) => item.line(),
213 SqlGraphEntity::BuiltinType(_item) => None,
214 SqlGraphEntity::Enum(item) => item.line(),
215 SqlGraphEntity::Ord(item) => item.line(),
216 SqlGraphEntity::Hash(item) => item.line(),
217 SqlGraphEntity::Aggregate(item) => item.line(),
218 SqlGraphEntity::Trigger(item) => item.line(),
219 SqlGraphEntity::ExtensionRoot(item) => item.line(),
220 }
221 }
222}
223
224impl ToSql for SqlGraphEntity {
225 fn to_sql(&self, context: &PgrxSql) -> eyre::Result<String> {
226 match self {
227 SqlGraphEntity::Schema(SchemaEntity { name: "public" | "pg_catalog", .. }) => {
228 Ok(String::default())
229 }
230 SqlGraphEntity::Schema(item) => item.to_sql(context),
231 SqlGraphEntity::CustomSql(item) => item.to_sql(context),
232 SqlGraphEntity::Function(item) => {
233 if let Some(result) = item.to_sql_config.to_sql(self, context) {
234 result
235 } else if context
236 .graph
237 .neighbors_undirected(*context.externs.get(item).unwrap())
238 .any(|neighbor| {
239 let SqlGraphEntity::Type(PostgresTypeEntity {
240 in_fn,
241 in_fn_module_path,
242 out_fn,
243 out_fn_module_path,
244 ..
245 }) = &context.graph[neighbor]
246 else {
247 return false;
248 };
249
250 let is_in_fn = item.full_path.starts_with(in_fn_module_path)
251 && item.full_path.ends_with(in_fn);
252 let is_out_fn = item.full_path.starts_with(out_fn_module_path)
253 && item.full_path.ends_with(out_fn);
254 is_in_fn || is_out_fn
255 })
256 {
257 Ok(String::default())
258 } else {
259 item.to_sql(context)
260 }
261 }
262 SqlGraphEntity::Type(item) => {
263 item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
264 }
265 SqlGraphEntity::BuiltinType(_) => Ok(String::default()),
266 SqlGraphEntity::Enum(item) => {
267 item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
268 }
269 SqlGraphEntity::Ord(item) => {
270 item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
271 }
272 SqlGraphEntity::Hash(item) => {
273 item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
274 }
275 SqlGraphEntity::Aggregate(item) => {
276 item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
277 }
278 SqlGraphEntity::Trigger(item) => {
279 item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
280 }
281 SqlGraphEntity::ExtensionRoot(item) => item.to_sql(context),
282 }
283 }
284}
285
286pub fn ident_is_acceptable_to_postgres(ident: &syn::Ident) -> Result<(), syn::Error> {
296 const POSTGRES_IDENTIFIER_MAX_LEN: usize = 64;
300
301 let len = ident.to_string().len();
302 if len >= POSTGRES_IDENTIFIER_MAX_LEN {
303 return Err(syn::Error::new(
304 ident.span(),
305 format!(
306 "Identifier `{ident}` was {len} characters long, PostgreSQL will truncate identifiers with less than \
307 {POSTGRES_IDENTIFIER_MAX_LEN} characters, opt for an identifier which Postgres won't truncate"
308 )
309 ));
310 }
311
312 Ok(())
313}