pgx_utils/sql_entity_graph/
mod.rs

1/*
2Portions Copyright 2019-2021 ZomboDB, LLC.
3Portions Copyright 2021-2022 Technology Concepts & Design, Inc. <support@tcdi.com>
4
5All rights reserved.
6
7Use of this source code is governed by the MIT license that can be found in the LICENSE file.
8*/
9/*!
10
11Rust to SQL mapping support.
12
13> Like all of the [`sql_entity_graph`][crate::sql_entity_graph] APIs, this is considered **internal**
14to the `pgx` framework and very subject to change between versions. While you may use this, please do it with caution.
15
16*/
17pub(crate) mod aggregate;
18pub(crate) mod control_file;
19pub(crate) mod extension_sql;
20pub(crate) mod mapping;
21pub mod metadata;
22pub(crate) mod pg_extern;
23pub(crate) mod pg_trigger;
24pub(crate) mod pgx_attribute;
25pub(crate) mod pgx_sql;
26pub(crate) mod positioning_ref;
27pub(crate) mod postgres_enum;
28pub(crate) mod postgres_hash;
29pub(crate) mod postgres_ord;
30pub(crate) mod postgres_type;
31pub(crate) mod schema;
32pub(crate) mod to_sql;
33pub(crate) mod used_type;
34
35pub use aggregate::entity::{AggregateTypeEntity, PgAggregateEntity};
36pub use aggregate::{
37    AggregateType, AggregateTypeList, FinalizeModify, ParallelOption, PgAggregate,
38};
39pub use control_file::ControlFile;
40pub use extension_sql::entity::{ExtensionSqlEntity, SqlDeclaredEntity};
41pub use extension_sql::{ExtensionSql, ExtensionSqlFile, SqlDeclared};
42pub use mapping::{RustSourceOnlySqlMapping, RustSqlMapping};
43pub use pg_extern::entity::{
44    PgExternArgumentEntity, PgExternEntity, PgExternReturnEntity, PgExternReturnEntityIteratedItem,
45    PgOperatorEntity,
46};
47pub use pg_extern::{NameMacro, PgExtern, PgExternArgument, PgOperator};
48pub use pg_trigger::attribute::PgTriggerAttribute;
49pub use pg_trigger::entity::PgTriggerEntity;
50pub use pg_trigger::PgTrigger;
51pub use pgx_sql::{PgxSql, RustToSqlMapping};
52pub use positioning_ref::PositioningRef;
53pub use postgres_enum::entity::PostgresEnumEntity;
54pub use postgres_enum::PostgresEnum;
55pub use postgres_hash::entity::PostgresHashEntity;
56pub use postgres_hash::PostgresHash;
57pub use postgres_ord::entity::PostgresOrdEntity;
58pub use postgres_ord::PostgresOrd;
59pub use postgres_type::entity::PostgresTypeEntity;
60pub use postgres_type::PostgresType;
61pub use schema::entity::SchemaEntity;
62pub use schema::Schema;
63pub use to_sql::entity::ToSqlConfigEntity;
64pub use to_sql::{ToSql, ToSqlConfig};
65pub use used_type::{UsedType, UsedTypeEntity};
66
67pub use crate::ExternArgs;
68
69/// Able to produce a GraphViz DOT format identifier.
70pub trait SqlGraphIdentifier {
71    /// A dot style identifier for the entity.
72    ///
73    /// Typically this is a 'archetype' prefix (eg `fn` or `type`) then result of
74    /// [`std::module_path`], [`core::any::type_name`], or some combination of [`std::file`] and
75    /// [`std::line`].
76    fn dot_identifier(&self) -> String;
77
78    /// A Rust identifier for the entity.
79    ///
80    /// Typically this is the result of [`std::module_path`], [`core::any::type_name`],
81    /// or some combination of [`std::file`] and [`std::line`].
82    fn rust_identifier(&self) -> String;
83
84    fn file(&self) -> Option<&'static str>;
85
86    fn line(&self) -> Option<u32>;
87}
88
89/// An entity corresponding to some SQL required by the extension.
90#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
91pub enum SqlGraphEntity {
92    ExtensionRoot(ControlFile),
93    Schema(SchemaEntity),
94    CustomSql(ExtensionSqlEntity),
95    Function(PgExternEntity),
96    Type(PostgresTypeEntity),
97    BuiltinType(String),
98    Enum(PostgresEnumEntity),
99    Ord(PostgresOrdEntity),
100    Hash(PostgresHashEntity),
101    Aggregate(PgAggregateEntity),
102    Trigger(PgTriggerEntity),
103}
104
105impl SqlGraphEntity {
106    pub fn sql_anchor_comment(&self) -> String {
107        let maybe_file_and_line = if let (Some(file), Some(line)) = (self.file(), self.line()) {
108            format!("-- {file}:{line}\n", file = file, line = line)
109        } else {
110            String::default()
111        };
112        format!(
113            "\
114            {maybe_file_and_line}\
115            -- {rust_identifier}\
116        ",
117            maybe_file_and_line = maybe_file_and_line,
118            rust_identifier = self.rust_identifier(),
119        )
120    }
121}
122
123impl SqlGraphIdentifier for SqlGraphEntity {
124    fn dot_identifier(&self) -> String {
125        match self {
126            SqlGraphEntity::Schema(item) => item.dot_identifier(),
127            SqlGraphEntity::CustomSql(item) => item.dot_identifier(),
128            SqlGraphEntity::Function(item) => item.dot_identifier(),
129            SqlGraphEntity::Type(item) => item.dot_identifier(),
130            SqlGraphEntity::BuiltinType(item) => format!("preexisting type {}", item),
131            SqlGraphEntity::Enum(item) => item.dot_identifier(),
132            SqlGraphEntity::Ord(item) => item.dot_identifier(),
133            SqlGraphEntity::Hash(item) => item.dot_identifier(),
134            SqlGraphEntity::Aggregate(item) => item.dot_identifier(),
135            SqlGraphEntity::Trigger(item) => item.dot_identifier(),
136            SqlGraphEntity::ExtensionRoot(item) => item.dot_identifier(),
137        }
138    }
139
140    fn rust_identifier(&self) -> String {
141        match self {
142            SqlGraphEntity::Schema(item) => item.rust_identifier(),
143            SqlGraphEntity::CustomSql(item) => item.rust_identifier(),
144            SqlGraphEntity::Function(item) => item.rust_identifier(),
145            SqlGraphEntity::Type(item) => item.rust_identifier(),
146            SqlGraphEntity::BuiltinType(item) => item.to_string(),
147            SqlGraphEntity::Enum(item) => item.rust_identifier(),
148            SqlGraphEntity::Ord(item) => item.rust_identifier(),
149            SqlGraphEntity::Hash(item) => item.rust_identifier(),
150            SqlGraphEntity::Aggregate(item) => item.rust_identifier(),
151            SqlGraphEntity::Trigger(item) => item.rust_identifier(),
152            SqlGraphEntity::ExtensionRoot(item) => item.rust_identifier(),
153        }
154    }
155
156    fn file(&self) -> Option<&'static str> {
157        match self {
158            SqlGraphEntity::Schema(item) => item.file(),
159            SqlGraphEntity::CustomSql(item) => item.file(),
160            SqlGraphEntity::Function(item) => item.file(),
161            SqlGraphEntity::Type(item) => item.file(),
162            SqlGraphEntity::BuiltinType(_item) => None,
163            SqlGraphEntity::Enum(item) => item.file(),
164            SqlGraphEntity::Ord(item) => item.file(),
165            SqlGraphEntity::Hash(item) => item.file(),
166            SqlGraphEntity::Aggregate(item) => item.file(),
167            SqlGraphEntity::Trigger(item) => item.file(),
168            SqlGraphEntity::ExtensionRoot(item) => item.file(),
169        }
170    }
171
172    fn line(&self) -> Option<u32> {
173        match self {
174            SqlGraphEntity::Schema(item) => item.line(),
175            SqlGraphEntity::CustomSql(item) => item.line(),
176            SqlGraphEntity::Function(item) => item.line(),
177            SqlGraphEntity::Type(item) => item.line(),
178            SqlGraphEntity::BuiltinType(_item) => None,
179            SqlGraphEntity::Enum(item) => item.line(),
180            SqlGraphEntity::Ord(item) => item.line(),
181            SqlGraphEntity::Hash(item) => item.line(),
182            SqlGraphEntity::Aggregate(item) => item.line(),
183            SqlGraphEntity::Trigger(item) => item.line(),
184            SqlGraphEntity::ExtensionRoot(item) => item.line(),
185        }
186    }
187}
188
189impl ToSql for SqlGraphEntity {
190    #[tracing::instrument(level = "debug", skip(self, context), fields(identifier = %self.rust_identifier()))]
191    fn to_sql(&self, context: &PgxSql) -> eyre::Result<String> {
192        match self {
193            SqlGraphEntity::Schema(item) => {
194                if item.name != "public" && item.name != "pg_catalog" {
195                    item.to_sql(context)
196                } else {
197                    Ok(String::default())
198                }
199            }
200            SqlGraphEntity::CustomSql(item) => item.to_sql(context),
201            SqlGraphEntity::Function(item) => {
202                if let Some(result) = item.to_sql_config.to_sql(self, context) {
203                    return result;
204                }
205                if context.graph.neighbors_undirected(context.externs.get(item).unwrap().clone()).any(|neighbor| {
206                    let neighbor_item = &context.graph[neighbor];
207                    match neighbor_item {
208                        SqlGraphEntity::Type(PostgresTypeEntity { in_fn, in_fn_module_path, out_fn, out_fn_module_path, .. }) => {
209                            let is_in_fn = item.full_path.starts_with(in_fn_module_path) && item.full_path.ends_with(in_fn);
210                            if is_in_fn {
211                                tracing::trace!(r#type = %neighbor_item.dot_identifier(), "Skipping, is an in_fn.");
212                            }
213                            let is_out_fn = item.full_path.starts_with(out_fn_module_path) && item.full_path.ends_with(out_fn);
214                            if is_out_fn {
215                                tracing::trace!(r#type = %neighbor_item.dot_identifier(), "Skipping, is an out_fn.");
216                            }
217                            is_in_fn || is_out_fn
218                        },
219                        _ => false,
220                    }
221                }) {
222                    Ok(String::default())
223                } else {
224                    item.to_sql(context)
225                }
226            }
227            SqlGraphEntity::Type(item) => {
228                item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
229            }
230            SqlGraphEntity::BuiltinType(_) => Ok(String::default()),
231            SqlGraphEntity::Enum(item) => {
232                item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
233            }
234            SqlGraphEntity::Ord(item) => {
235                item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
236            }
237            SqlGraphEntity::Hash(item) => {
238                item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
239            }
240            SqlGraphEntity::Aggregate(item) => {
241                item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
242            }
243            SqlGraphEntity::Trigger(item) => {
244                item.to_sql_config.to_sql(self, context).unwrap_or_else(|| item.to_sql(context))
245            }
246            SqlGraphEntity::ExtensionRoot(item) => item.to_sql(context),
247        }
248    }
249}