pgx_sql_entity_graph/
pgx_sql.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::pgx_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*/
17
18use eyre::eyre;
19use petgraph::dot::Dot;
20use petgraph::graph::NodeIndex;
21use petgraph::stable_graph::StableGraph;
22use std::any::TypeId;
23use std::collections::HashMap;
24use std::fmt::Debug;
25use std::path::Path;
26
27use crate::aggregate::entity::PgAggregateEntity;
28use crate::control_file::ControlFile;
29use crate::extension_sql::entity::{ExtensionSqlEntity, SqlDeclaredEntity};
30use crate::extension_sql::SqlDeclared;
31use crate::pg_extern::entity::PgExternEntity;
32use crate::pg_trigger::entity::PgTriggerEntity;
33use crate::positioning_ref::PositioningRef;
34use crate::postgres_enum::entity::PostgresEnumEntity;
35use crate::postgres_hash::entity::PostgresHashEntity;
36use crate::postgres_ord::entity::PostgresOrdEntity;
37use crate::postgres_type::entity::PostgresTypeEntity;
38use crate::schema::entity::SchemaEntity;
39use crate::to_sql::ToSql;
40use crate::{SqlGraphEntity, SqlGraphIdentifier};
41
42use super::{PgExternReturnEntity, PgExternReturnEntityIteratedItem};
43
44#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
45pub enum SqlGraphRelationship {
46    RequiredBy,
47    RequiredByArg,
48    RequiredByReturn,
49}
50
51/// A generator for SQL.
52///
53/// Consumes a base mapping of types (typically `pgx::DEFAULT_TYPEID_SQL_MAPPING`), a
54/// [`ControlFile`], and collections of each SQL entity.
55///
56/// During construction, a Directed Acyclic Graph is formed out the dependencies. For example,
57/// an item `detect_dog(x: &[u8]) -> animals::Dog` would have have a relationship with
58/// `animals::Dog`.
59///
60/// Typically, [`PgxSql`] types are constructed in a `pgx::pg_binary_magic!()` call in a binary
61/// out of entities collected during a `pgx::pg_module_magic!()` call in a library.
62#[derive(Debug, Clone)]
63pub struct PgxSql {
64    pub control: ControlFile,
65    pub graph: StableGraph<SqlGraphEntity, SqlGraphRelationship>,
66    pub graph_root: NodeIndex,
67    pub graph_bootstrap: Option<NodeIndex>,
68    pub graph_finalize: Option<NodeIndex>,
69    pub schemas: HashMap<SchemaEntity, NodeIndex>,
70    pub extension_sqls: HashMap<ExtensionSqlEntity, NodeIndex>,
71    pub externs: HashMap<PgExternEntity, NodeIndex>,
72    pub types: HashMap<PostgresTypeEntity, NodeIndex>,
73    pub builtin_types: HashMap<String, NodeIndex>,
74    pub enums: HashMap<PostgresEnumEntity, NodeIndex>,
75    pub ords: HashMap<PostgresOrdEntity, NodeIndex>,
76    pub hashes: HashMap<PostgresHashEntity, NodeIndex>,
77    pub aggregates: HashMap<PgAggregateEntity, NodeIndex>,
78    pub triggers: HashMap<PgTriggerEntity, NodeIndex>,
79    pub extension_name: String,
80    pub versioned_so: bool,
81}
82
83impl PgxSql {
84    pub fn build(
85        entities: impl Iterator<Item = SqlGraphEntity>,
86        extension_name: String,
87        versioned_so: bool,
88    ) -> eyre::Result<Self> {
89        let mut graph = StableGraph::new();
90
91        let mut entities = entities.collect::<Vec<_>>();
92        entities.sort();
93        // Split up things into their specific types:
94        let mut control: Option<ControlFile> = None;
95        let mut schemas: Vec<SchemaEntity> = Vec::default();
96        let mut extension_sqls: Vec<ExtensionSqlEntity> = Vec::default();
97        let mut externs: Vec<PgExternEntity> = Vec::default();
98        let mut types: Vec<PostgresTypeEntity> = Vec::default();
99        let mut enums: Vec<PostgresEnumEntity> = Vec::default();
100        let mut ords: Vec<PostgresOrdEntity> = Vec::default();
101        let mut hashes: Vec<PostgresHashEntity> = Vec::default();
102        let mut aggregates: Vec<PgAggregateEntity> = Vec::default();
103        let mut triggers: Vec<PgTriggerEntity> = Vec::default();
104        for entity in entities {
105            match entity {
106                SqlGraphEntity::ExtensionRoot(input_control) => {
107                    control = Some(input_control);
108                }
109                SqlGraphEntity::Schema(input_schema) => {
110                    schemas.push(input_schema);
111                }
112                SqlGraphEntity::CustomSql(input_sql) => {
113                    extension_sqls.push(input_sql);
114                }
115                SqlGraphEntity::Function(input_function) => {
116                    externs.push(input_function);
117                }
118                SqlGraphEntity::Type(input_type) => {
119                    types.push(input_type);
120                }
121                SqlGraphEntity::BuiltinType(_) => (),
122                SqlGraphEntity::Enum(input_enum) => {
123                    enums.push(input_enum);
124                }
125                SqlGraphEntity::Ord(input_ord) => {
126                    ords.push(input_ord);
127                }
128                SqlGraphEntity::Hash(input_hash) => {
129                    hashes.push(input_hash);
130                }
131                SqlGraphEntity::Aggregate(input_aggregate) => {
132                    aggregates.push(input_aggregate);
133                }
134                SqlGraphEntity::Trigger(input_trigger) => {
135                    triggers.push(input_trigger);
136                }
137            }
138        }
139
140        let control: ControlFile = control.expect("No control file found");
141        let root = graph.add_node(SqlGraphEntity::ExtensionRoot(control.clone()));
142
143        // The initial build phase.
144        //
145        // Notably, we do not set non-root edges here. We do that in a second step. This is
146        // primarily because externs, types, operators, and the like tend to intertwine. If we tried
147        // to do it here, we'd find ourselves trying to create edges to non-existing entities.
148
149        // Both of these must be unique, so we can only hold one.
150        // Populate nodes, but don't build edges until we know if there is a bootstrap/finalize.
151        let (mapped_extension_sqls, bootstrap, finalize) =
152            initialize_extension_sqls(&mut graph, root, extension_sqls)?;
153        let mapped_schemas = initialize_schemas(&mut graph, bootstrap, finalize, schemas)?;
154        let mapped_enums = initialize_enums(&mut graph, root, bootstrap, finalize, enums)?;
155        let mapped_types = initialize_types(&mut graph, root, bootstrap, finalize, types)?;
156        let (mapped_externs, mut mapped_builtin_types) = initialize_externs(
157            &mut graph,
158            root,
159            bootstrap,
160            finalize,
161            externs,
162            &mapped_types,
163            &mapped_enums,
164        )?;
165        let mapped_ords = initialize_ords(&mut graph, root, bootstrap, finalize, ords)?;
166        let mapped_hashes = initialize_hashes(&mut graph, root, bootstrap, finalize, hashes)?;
167        let mapped_aggregates = initialize_aggregates(
168            &mut graph,
169            root,
170            bootstrap,
171            finalize,
172            aggregates,
173            &mut mapped_builtin_types,
174            &mapped_enums,
175            &mapped_types,
176        )?;
177        let mapped_triggers = initialize_triggers(&mut graph, root, bootstrap, finalize, triggers)?;
178
179        // Now we can circle back and build up the edge sets.
180        connect_schemas(&mut graph, &mapped_schemas, root);
181        connect_extension_sqls(
182            &mut graph,
183            &mapped_extension_sqls,
184            &mapped_schemas,
185            &mapped_types,
186            &mapped_enums,
187            &mapped_externs,
188            &mapped_triggers,
189        )?;
190        connect_enums(&mut graph, &mapped_enums, &mapped_schemas);
191        connect_types(&mut graph, &mapped_types, &mapped_schemas);
192        connect_externs(
193            &mut graph,
194            &mapped_externs,
195            &mapped_hashes,
196            &mapped_schemas,
197            &mapped_types,
198            &mapped_enums,
199            &mapped_builtin_types,
200            &mapped_extension_sqls,
201            &mapped_triggers,
202        )?;
203        connect_ords(
204            &mut graph,
205            &mapped_ords,
206            &mapped_schemas,
207            &mapped_types,
208            &mapped_enums,
209            &mapped_externs,
210        );
211        connect_hashes(
212            &mut graph,
213            &mapped_hashes,
214            &mapped_schemas,
215            &mapped_types,
216            &mapped_enums,
217            &mapped_externs,
218        );
219        connect_aggregates(
220            &mut graph,
221            &mapped_aggregates,
222            &mapped_schemas,
223            &mapped_types,
224            &mapped_enums,
225            &mapped_builtin_types,
226            &mapped_externs,
227        )?;
228        connect_triggers(&mut graph, &mapped_triggers, &mapped_schemas);
229
230        let this = Self {
231            control: control,
232            schemas: mapped_schemas,
233            extension_sqls: mapped_extension_sqls,
234            externs: mapped_externs,
235            types: mapped_types,
236            builtin_types: mapped_builtin_types,
237            enums: mapped_enums,
238            ords: mapped_ords,
239            hashes: mapped_hashes,
240            aggregates: mapped_aggregates,
241            triggers: mapped_triggers,
242            graph: graph,
243            graph_root: root,
244            graph_bootstrap: bootstrap,
245            graph_finalize: finalize,
246            extension_name: extension_name,
247            versioned_so,
248        };
249        Ok(this)
250    }
251
252    pub fn to_file(&self, file: impl AsRef<Path> + Debug) -> eyre::Result<()> {
253        use std::fs::{create_dir_all, File};
254        use std::io::Write;
255        let generated = self.to_sql()?;
256        let path = Path::new(file.as_ref());
257
258        let parent = path.parent();
259        if let Some(parent) = parent {
260            create_dir_all(parent)?;
261        }
262        let mut out = File::create(path)?;
263        write!(out, "{}", generated)?;
264        Ok(())
265    }
266
267    pub fn write(&self, out: &mut impl std::io::Write) -> eyre::Result<()> {
268        let generated = self.to_sql()?;
269
270        #[cfg(feature = "syntax-highlighting")]
271        {
272            if atty::is(atty::Stream::Stdout) {
273                self.write_highlighted(out, &generated)?;
274            } else {
275                write!(*out, "{}", generated)?;
276            }
277        }
278
279        #[cfg(not(feature = "syntax-highlighting"))]
280        {
281            write!(*out, "{}", generated)?;
282        }
283
284        Ok(())
285    }
286
287    #[cfg(feature = "syntax-highlighting")]
288    fn write_highlighted(&self, out: &mut dyn std::io::Write, generated: &str) -> eyre::Result<()> {
289        use eyre::WrapErr as _;
290        use owo_colors::{OwoColorize, XtermColors};
291        use syntect::easy::HighlightLines;
292        use syntect::highlighting::{Style, ThemeSet};
293        use syntect::parsing::SyntaxSet;
294        use syntect::util::LinesWithEndings;
295        let ps = SyntaxSet::load_defaults_newlines();
296        let theme_bytes = include_str!("../assets/ansi.tmTheme").as_bytes();
297        let mut theme_reader = std::io::Cursor::new(theme_bytes);
298        let theme = ThemeSet::load_from_reader(&mut theme_reader)
299            .wrap_err("Couldn't parse theme for SQL highlighting, try piping to a file")?;
300
301        if let Some(syntax) = ps.find_syntax_by_extension("sql") {
302            let mut h = HighlightLines::new(syntax, &theme);
303            for line in LinesWithEndings::from(&generated) {
304                let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps)?;
305                // Concept from https://github.com/sharkdp/bat/blob/1b030dc03b906aa345f44b8266bffeea77d763fe/src/terminal.rs#L6
306                for (style, content) in ranges {
307                    if style.foreground.a == 0x01 {
308                        write!(*out, "{}", content)?;
309                    } else {
310                        write!(*out, "{}", content.color(XtermColors::from(style.foreground.r)))?;
311                    }
312                }
313                write!(*out, "\x1b[0m")?;
314            }
315        } else {
316            write!(*out, "{}", generated)?;
317        }
318        Ok(())
319    }
320
321    pub fn to_dot(&self, file: impl AsRef<Path> + Debug) -> eyre::Result<()> {
322        use std::fs::{create_dir_all, File};
323        use std::io::Write;
324        let generated = Dot::with_attr_getters(
325            &self.graph,
326            &[petgraph::dot::Config::EdgeNoLabel, petgraph::dot::Config::NodeNoLabel],
327            &|_graph, edge| match edge.weight() {
328                SqlGraphRelationship::RequiredBy => format!(r#"color = "gray""#),
329                SqlGraphRelationship::RequiredByArg => format!(r#"color = "black""#),
330                SqlGraphRelationship::RequiredByReturn => {
331                    format!(r#"dir = "back", color = "black""#)
332                }
333            },
334            &|_graph, (_index, node)| {
335                match node {
336                    // Colors derived from https://www.schemecolor.com/touch-of-creativity.php
337                    SqlGraphEntity::Schema(_item) => format!(
338                        "label = \"{}\", weight = 6, shape = \"tab\"",
339                        node.dot_identifier()
340                    ),
341                    SqlGraphEntity::Function(_item) => format!(
342                        "label = \"{}\", penwidth = 0, style = \"filled\", fillcolor = \"#ADC7C6\", weight = 4, shape = \"box\"",
343                        node.dot_identifier()
344                    ),
345                    SqlGraphEntity::Type(_item) => format!(
346                        "label = \"{}\", penwidth = 0, style = \"filled\", fillcolor = \"#AE9BBD\", weight = 5, shape = \"oval\"",
347                        node.dot_identifier()
348                    ),
349                    SqlGraphEntity::BuiltinType(_item) => format!(
350                        "label = \"{}\", shape = \"plain\"",
351                        node.dot_identifier()
352                    ),
353                    SqlGraphEntity::Enum(_item) => format!(
354                        "label = \"{}\", penwidth = 0, style = \"filled\", fillcolor = \"#C9A7C8\", weight = 5, shape = \"oval\"",
355                        node.dot_identifier()
356                    ),
357                    SqlGraphEntity::Ord(_item) => format!(
358                        "label = \"{}\", penwidth = 0, style = \"filled\", fillcolor = \"#FFCFD3\", weight = 5, shape = \"diamond\"",
359                        node.dot_identifier()
360                    ),
361                    SqlGraphEntity::Hash(_item) => format!(
362                        "label = \"{}\", penwidth = 0, style = \"filled\", fillcolor = \"#FFE4E0\", weight = 5, shape = \"diamond\"",
363                        node.dot_identifier()
364                    ),
365                    SqlGraphEntity::Aggregate(_item) => format!(
366                        "label = \"{}\", penwidth = 0, style = \"filled\", fillcolor = \"#FFE4E0\", weight = 5, shape = \"diamond\"",
367                        node.dot_identifier()
368                    ),
369                    SqlGraphEntity::Trigger(_item) => format!(
370                        "label = \"{}\", penwidth = 0, style = \"filled\", fillcolor = \"#FFE4E0\", weight = 5, shape = \"diamond\"",
371                        node.dot_identifier()
372                    ),
373                    SqlGraphEntity::CustomSql(_item) => format!(
374                        "label = \"{}\", weight = 3, shape = \"signature\"",
375                        node.dot_identifier()
376                    ),
377                    SqlGraphEntity::ExtensionRoot(_item) => format!(
378                        "label = \"{}\", shape = \"cylinder\"",
379                        node.dot_identifier()
380                    ),
381                }
382            },
383        );
384        let path = Path::new(file.as_ref());
385
386        let parent = path.parent();
387        if let Some(parent) = parent {
388            create_dir_all(parent)?;
389        }
390        let mut out = File::create(path)?;
391        write!(out, "{:?}", generated)?;
392        Ok(())
393    }
394
395    pub fn schema_alias_of(&self, item_index: &NodeIndex) -> Option<String> {
396        self.graph
397            .neighbors_undirected(*item_index)
398            .flat_map(|neighbor_index| match &self.graph[neighbor_index] {
399                SqlGraphEntity::Schema(s) => Some(String::from(s.name)),
400                SqlGraphEntity::ExtensionRoot(control) => {
401                    if !control.relocatable {
402                        control.schema.clone()
403                    } else {
404                        Some(String::from("@extname@"))
405                    }
406                }
407                _ => None,
408            })
409            .next()
410    }
411
412    pub fn schema_prefix_for(&self, target: &NodeIndex) -> String {
413        self.schema_alias_of(target)
414            .map(|v| (v + ".").to_string())
415            .unwrap_or_else(|| "".to_string())
416    }
417
418    pub fn to_sql(&self) -> eyre::Result<String> {
419        let mut full_sql = String::new();
420        for step_id in petgraph::algo::toposort(&self.graph, None).map_err(|e| {
421            eyre!("Failed to toposort SQL entities, node with cycle: {:?}", self.graph[e.node_id()])
422        })? {
423            let step = &self.graph[step_id];
424
425            let sql = step.to_sql(self)?;
426
427            if !sql.is_empty() {
428                full_sql.push_str(&sql);
429                full_sql.push('\n');
430            }
431        }
432        Ok(full_sql)
433    }
434
435    pub fn has_sql_declared_entity(&self, identifier: &SqlDeclared) -> Option<&SqlDeclaredEntity> {
436        self.extension_sqls.iter().find_map(|(item, _index)| {
437            let retval = item.creates.iter().find_map(|create_entity| {
438                if create_entity.has_sql_declared_entity(identifier) {
439                    Some(create_entity)
440                } else {
441                    None
442                }
443            });
444            retval
445        })
446    }
447
448    pub fn source_only_to_sql_type(&self, _ty_source: &str) -> Option<String> {
449        // HACK for `Result<T, E>`
450        // ...well, actually, nothing!
451        None
452    }
453
454    pub fn get_module_pathname(&self) -> String {
455        return if self.versioned_so {
456            let extname = &self.extension_name;
457            let extver = &self.control.default_version;
458            // Note: versioned so-name format must agree with cargo pgx
459            format!("$libdir/{}-{}", extname, extver)
460        } else {
461            String::from("MODULE_PATHNAME")
462        };
463    }
464}
465
466fn build_base_edges(
467    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
468    index: NodeIndex,
469    root: NodeIndex,
470    bootstrap: Option<NodeIndex>,
471    finalize: Option<NodeIndex>,
472) {
473    graph.add_edge(root, index, SqlGraphRelationship::RequiredBy);
474    if let Some(bootstrap) = bootstrap {
475        graph.add_edge(bootstrap, index, SqlGraphRelationship::RequiredBy);
476    }
477    if let Some(finalize) = finalize {
478        graph.add_edge(index, finalize, SqlGraphRelationship::RequiredBy);
479    }
480}
481
482fn initialize_extension_sqls<'a>(
483    graph: &'a mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
484    root: NodeIndex,
485    extension_sqls: Vec<ExtensionSqlEntity>,
486) -> eyre::Result<(HashMap<ExtensionSqlEntity, NodeIndex>, Option<NodeIndex>, Option<NodeIndex>)> {
487    let mut bootstrap = None;
488    let mut finalize = None;
489    let mut mapped_extension_sqls = HashMap::default();
490    for item in extension_sqls {
491        let entity: SqlGraphEntity = item.clone().into();
492        let index = graph.add_node(entity);
493        mapped_extension_sqls.insert(item.clone(), index);
494
495        if item.bootstrap {
496            if let Some(exiting_index) = bootstrap {
497                let existing: &SqlGraphEntity = &graph[exiting_index];
498                return Err(eyre!(
499                    "Cannot have multiple `extension_sql!()` with `bootstrap` positioning, found `{}`, other was `{}`",
500                    item.rust_identifier(),
501                    existing.rust_identifier(),
502                ));
503            }
504            bootstrap = Some(index)
505        }
506        if item.finalize {
507            if let Some(exiting_index) = finalize {
508                let existing: &SqlGraphEntity = &graph[exiting_index];
509                return Err(eyre!(
510                    "Cannot have multiple `extension_sql!()` with `finalize` positioning, found `{}`, other was `{}`",
511                    item.rust_identifier(),
512                    existing.rust_identifier(),
513                ));
514            }
515            finalize = Some(index)
516        }
517    }
518    for (item, index) in &mapped_extension_sqls {
519        graph.add_edge(root, *index, SqlGraphRelationship::RequiredBy);
520        if !item.bootstrap {
521            if let Some(bootstrap) = bootstrap {
522                graph.add_edge(bootstrap, *index, SqlGraphRelationship::RequiredBy);
523            }
524        }
525        if !item.finalize {
526            if let Some(finalize) = finalize {
527                graph.add_edge(*index, finalize, SqlGraphRelationship::RequiredBy);
528            }
529        }
530    }
531    Ok((mapped_extension_sqls, bootstrap, finalize))
532}
533
534/// A best effort attempt to find the related [`NodeIndex`] for some [`PositioningRef`].
535pub fn find_positioning_ref_target<'a>(
536    positioning_ref: &'a PositioningRef,
537    types: &'a HashMap<PostgresTypeEntity, NodeIndex>,
538    enums: &'a HashMap<PostgresEnumEntity, NodeIndex>,
539    externs: &'a HashMap<PgExternEntity, NodeIndex>,
540    schemas: &'a HashMap<SchemaEntity, NodeIndex>,
541    extension_sqls: &'a HashMap<ExtensionSqlEntity, NodeIndex>,
542    triggers: &'a HashMap<PgTriggerEntity, NodeIndex>,
543) -> Option<&'a NodeIndex> {
544    match positioning_ref {
545        PositioningRef::FullPath(path) => {
546            // The best we can do here is a fuzzy search.
547            let segments = path.split("::").collect::<Vec<_>>();
548            let last_segment = segments.last().expect("Expected at least one segment.");
549            let rest = &segments[..segments.len() - 1];
550            let module_path = rest.join("::");
551
552            for (other, other_index) in types {
553                if *last_segment == other.name && other.module_path.ends_with(&module_path) {
554                    return Some(&other_index);
555                }
556            }
557            for (other, other_index) in enums {
558                if last_segment == &other.name && other.module_path.ends_with(&module_path) {
559                    return Some(&other_index);
560                }
561            }
562            for (other, other_index) in externs {
563                if *last_segment == other.unaliased_name
564                    && other.module_path.ends_with(&module_path)
565                {
566                    return Some(&other_index);
567                }
568            }
569            for (other, other_index) in schemas {
570                if other.module_path.ends_with(path) {
571                    return Some(&other_index);
572                }
573            }
574
575            for (other, other_index) in triggers {
576                if last_segment == &other.function_name && other.module_path.ends_with(&module_path)
577                {
578                    return Some(&other_index);
579                }
580            }
581        }
582        PositioningRef::Name(name) => {
583            for (other, other_index) in extension_sqls {
584                if other.name == *name {
585                    return Some(&other_index);
586                }
587            }
588        }
589    };
590    None
591}
592
593fn connect_extension_sqls(
594    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
595    extension_sqls: &HashMap<ExtensionSqlEntity, NodeIndex>,
596    schemas: &HashMap<SchemaEntity, NodeIndex>,
597    types: &HashMap<PostgresTypeEntity, NodeIndex>,
598    enums: &HashMap<PostgresEnumEntity, NodeIndex>,
599    externs: &HashMap<PgExternEntity, NodeIndex>,
600    triggers: &HashMap<PgTriggerEntity, NodeIndex>,
601) -> eyre::Result<()> {
602    for (item, &index) in extension_sqls {
603        make_schema_connection(
604            graph,
605            "Extension SQL",
606            index,
607            &item.rust_identifier(),
608            item.module_path,
609            schemas,
610        );
611
612        for requires in &item.requires {
613            if let Some(target) = find_positioning_ref_target(
614                requires,
615                types,
616                enums,
617                externs,
618                schemas,
619                extension_sqls,
620                triggers,
621            ) {
622                graph.add_edge(*target, index, SqlGraphRelationship::RequiredBy);
623            } else {
624                return Err(eyre!(
625                    "Could not find `requires` target of `{}`{}: {}",
626                    item.rust_identifier(),
627                    if let (Some(file), Some(line)) = (item.file(), item.line()) {
628                        format!(" ({}:{})", file, line)
629                    } else {
630                        "".to_string()
631                    },
632                    match requires {
633                        PositioningRef::FullPath(path) => path.to_string(),
634                        PositioningRef::Name(name) => format!(r#""{}""#, name),
635                    },
636                ));
637            }
638        }
639    }
640    Ok(())
641}
642
643fn initialize_schemas(
644    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
645    bootstrap: Option<NodeIndex>,
646    finalize: Option<NodeIndex>,
647    schemas: Vec<SchemaEntity>,
648) -> eyre::Result<HashMap<SchemaEntity, NodeIndex>> {
649    let mut mapped_schemas = HashMap::default();
650    for item in schemas {
651        let entity = item.clone().into();
652        let index = graph.add_node(entity);
653        mapped_schemas.insert(item, index);
654        if let Some(bootstrap) = bootstrap {
655            graph.add_edge(bootstrap, index, SqlGraphRelationship::RequiredBy);
656        }
657        if let Some(finalize) = finalize {
658            graph.add_edge(index, finalize, SqlGraphRelationship::RequiredBy);
659        }
660    }
661    Ok(mapped_schemas)
662}
663
664fn connect_schemas(
665    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
666    schemas: &HashMap<SchemaEntity, NodeIndex>,
667    root: NodeIndex,
668) {
669    for (_item, &index) in schemas {
670        graph.add_edge(root, index, SqlGraphRelationship::RequiredBy);
671    }
672}
673
674fn initialize_enums(
675    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
676    root: NodeIndex,
677    bootstrap: Option<NodeIndex>,
678    finalize: Option<NodeIndex>,
679    enums: Vec<PostgresEnumEntity>,
680) -> eyre::Result<HashMap<PostgresEnumEntity, NodeIndex>> {
681    let mut mapped_enums = HashMap::default();
682    for item in enums {
683        let entity: SqlGraphEntity = item.clone().into();
684        let index = graph.add_node(entity);
685        mapped_enums.insert(item, index);
686        build_base_edges(graph, index, root, bootstrap, finalize);
687    }
688    Ok(mapped_enums)
689}
690
691fn connect_enums(
692    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
693    enums: &HashMap<PostgresEnumEntity, NodeIndex>,
694    schemas: &HashMap<SchemaEntity, NodeIndex>,
695) {
696    for (item, &index) in enums {
697        make_schema_connection(
698            graph,
699            "Enum",
700            index,
701            &item.rust_identifier(),
702            item.module_path,
703            schemas,
704        );
705    }
706}
707
708fn initialize_types(
709    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
710    root: NodeIndex,
711    bootstrap: Option<NodeIndex>,
712    finalize: Option<NodeIndex>,
713    types: Vec<PostgresTypeEntity>,
714) -> eyre::Result<HashMap<PostgresTypeEntity, NodeIndex>> {
715    let mut mapped_types = HashMap::default();
716    for item in types {
717        let entity = item.clone().into();
718        let index = graph.add_node(entity);
719        mapped_types.insert(item, index);
720        build_base_edges(graph, index, root, bootstrap, finalize);
721    }
722    Ok(mapped_types)
723}
724
725fn connect_types(
726    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
727    types: &HashMap<PostgresTypeEntity, NodeIndex>,
728    schemas: &HashMap<SchemaEntity, NodeIndex>,
729) {
730    for (item, &index) in types {
731        make_schema_connection(
732            graph,
733            "Type",
734            index,
735            &item.rust_identifier(),
736            item.module_path,
737            schemas,
738        );
739    }
740}
741
742fn initialize_externs(
743    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
744    root: NodeIndex,
745    bootstrap: Option<NodeIndex>,
746    finalize: Option<NodeIndex>,
747    externs: Vec<PgExternEntity>,
748    mapped_types: &HashMap<PostgresTypeEntity, NodeIndex>,
749    mapped_enums: &HashMap<PostgresEnumEntity, NodeIndex>,
750) -> eyre::Result<(HashMap<PgExternEntity, NodeIndex>, HashMap<String, NodeIndex>)> {
751    let mut mapped_externs = HashMap::default();
752    let mut mapped_builtin_types = HashMap::default();
753    for item in externs {
754        let entity: SqlGraphEntity = item.clone().into();
755        let index = graph.add_node(entity.clone());
756        mapped_externs.insert(item.clone(), index);
757        build_base_edges(graph, index, root, bootstrap, finalize);
758
759        for arg in &item.fn_args {
760            let mut found = false;
761            for (ty_item, &_ty_index) in mapped_types {
762                if ty_item.id_matches(&arg.used_ty.ty_id) {
763                    found = true;
764                    break;
765                }
766            }
767            for (ty_item, &_ty_index) in mapped_enums {
768                if ty_item.id_matches(&arg.used_ty.ty_id) {
769                    found = true;
770                    break;
771                }
772            }
773            if !found {
774                mapped_builtin_types.entry(arg.used_ty.full_path.to_string()).or_insert_with(
775                    || {
776                        graph.add_node(SqlGraphEntity::BuiltinType(
777                            arg.used_ty.full_path.to_string(),
778                        ))
779                    },
780                );
781            }
782        }
783
784        match &item.fn_return {
785            PgExternReturnEntity::None | PgExternReturnEntity::Trigger => (),
786            PgExternReturnEntity::Type { ty, .. } | PgExternReturnEntity::SetOf { ty, .. } => {
787                let mut found = false;
788                for (ty_item, &_ty_index) in mapped_types {
789                    if ty_item.id_matches(&ty.ty_id) {
790                        found = true;
791                        break;
792                    }
793                }
794                for (ty_item, &_ty_index) in mapped_enums {
795                    if ty_item.id_matches(&ty.ty_id) {
796                        found = true;
797                        break;
798                    }
799                }
800                if !found {
801                    mapped_builtin_types.entry(ty.full_path.to_string()).or_insert_with(|| {
802                        graph.add_node(SqlGraphEntity::BuiltinType(ty.full_path.to_string()))
803                    });
804                }
805            }
806            PgExternReturnEntity::Iterated { tys: iterated_returns, optional: _, result: _ } => {
807                for PgExternReturnEntityIteratedItem { ty: return_ty_entity, .. } in
808                    iterated_returns
809                {
810                    let mut found = false;
811                    for (ty_item, &_ty_index) in mapped_types {
812                        if ty_item.id_matches(&return_ty_entity.ty_id) {
813                            found = true;
814                            break;
815                        }
816                    }
817                    for (ty_item, &_ty_index) in mapped_enums {
818                        if ty_item.id_matches(&return_ty_entity.ty_id) {
819                            found = true;
820                            break;
821                        }
822                    }
823                    if !found {
824                        mapped_builtin_types
825                            .entry(return_ty_entity.ty_source.to_string())
826                            .or_insert_with(|| {
827                                graph.add_node(SqlGraphEntity::BuiltinType(
828                                    return_ty_entity.ty_source.to_string(),
829                                ))
830                            });
831                    }
832                }
833            }
834        }
835    }
836    Ok((mapped_externs, mapped_builtin_types))
837}
838
839fn connect_externs(
840    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
841    externs: &HashMap<PgExternEntity, NodeIndex>,
842    hashes: &HashMap<PostgresHashEntity, NodeIndex>,
843    schemas: &HashMap<SchemaEntity, NodeIndex>,
844    types: &HashMap<PostgresTypeEntity, NodeIndex>,
845    enums: &HashMap<PostgresEnumEntity, NodeIndex>,
846    builtin_types: &HashMap<String, NodeIndex>,
847    extension_sqls: &HashMap<ExtensionSqlEntity, NodeIndex>,
848    triggers: &HashMap<PgTriggerEntity, NodeIndex>,
849) -> eyre::Result<()> {
850    for (item, &index) in externs {
851        let mut found_schema_declaration = false;
852        for extern_attr in &item.extern_attrs {
853            match extern_attr {
854                crate::ExternArgs::Requires(requirements) => {
855                    for requires in requirements {
856                        if let Some(target) = find_positioning_ref_target(
857                            requires,
858                            types,
859                            enums,
860                            externs,
861                            schemas,
862                            extension_sqls,
863                            triggers,
864                        ) {
865                            graph.add_edge(*target, index, SqlGraphRelationship::RequiredBy);
866                        } else {
867                            return Err(eyre!("Could not find `requires` target: {:?}", requires));
868                        }
869                    }
870                }
871                crate::ExternArgs::Schema(declared_schema_name) => {
872                    for (schema, schema_index) in schemas {
873                        if schema.name == declared_schema_name {
874                            graph.add_edge(*schema_index, index, SqlGraphRelationship::RequiredBy);
875                            found_schema_declaration = true;
876                        }
877                    }
878                    if !found_schema_declaration {
879                        return Err(eyre!("Got manual `schema = \"{declared_schema_name}\"` setting, but that schema did not exist."));
880                    }
881                }
882                _ => (),
883            }
884        }
885
886        if !found_schema_declaration {
887            make_schema_connection(
888                graph,
889                "Extern",
890                index,
891                &item.rust_identifier(),
892                item.module_path,
893                schemas,
894            );
895        }
896
897        // The hash function must be defined after the {typename}_eq function.
898        for (hash_item, &hash_index) in hashes {
899            if item.module_path == hash_item.module_path
900                && item.name == hash_item.name.to_lowercase() + "_eq"
901            {
902                graph.add_edge(index, hash_index, SqlGraphRelationship::RequiredBy);
903            }
904        }
905
906        for arg in &item.fn_args {
907            let mut found = false;
908
909            for (ty_item, &ty_index) in types {
910                if ty_item.id_matches(&arg.used_ty.ty_id) {
911                    graph.add_edge(ty_index, index, SqlGraphRelationship::RequiredByArg);
912                    found = true;
913                    break;
914                }
915            }
916            if !found {
917                for (enum_item, &enum_index) in enums {
918                    if enum_item.id_matches(&arg.used_ty.ty_id) {
919                        graph.add_edge(enum_index, index, SqlGraphRelationship::RequiredByArg);
920                        found = true;
921                        break;
922                    }
923                }
924            }
925            if !found {
926                let builtin_index = builtin_types
927                    .get(arg.used_ty.full_path)
928                    .expect(&format!("Could not fetch Builtin Type {}.", arg.used_ty.full_path));
929                graph.add_edge(*builtin_index, index, SqlGraphRelationship::RequiredByArg);
930            }
931            if !found {
932                for (ext_item, ext_index) in extension_sqls {
933                    if let Some(_) = ext_item.has_sql_declared_entity(&SqlDeclared::Type(
934                        arg.used_ty.full_path.to_string(),
935                    )) {
936                        graph.add_edge(*ext_index, index, SqlGraphRelationship::RequiredByArg);
937                    } else if let Some(_) = ext_item.has_sql_declared_entity(&SqlDeclared::Enum(
938                        arg.used_ty.full_path.to_string(),
939                    )) {
940                        graph.add_edge(*ext_index, index, SqlGraphRelationship::RequiredByArg);
941                    }
942                }
943            }
944        }
945        match &item.fn_return {
946            PgExternReturnEntity::None | PgExternReturnEntity::Trigger => (),
947            PgExternReturnEntity::Type { ty, .. } | PgExternReturnEntity::SetOf { ty, .. } => {
948                let mut found = false;
949                for (ty_item, &ty_index) in types {
950                    if ty_item.id_matches(&ty.ty_id) {
951                        graph.add_edge(ty_index, index, SqlGraphRelationship::RequiredByReturn);
952                        found = true;
953                        break;
954                    }
955                }
956                if !found {
957                    for (ty_item, &ty_index) in enums {
958                        if ty_item.id_matches(&ty.ty_id) {
959                            graph.add_edge(ty_index, index, SqlGraphRelationship::RequiredByReturn);
960                            found = true;
961                            break;
962                        }
963                    }
964                }
965                if !found {
966                    let builtin_index = builtin_types
967                        .get(&ty.full_path.to_string())
968                        .expect(&format!("Could not fetch Builtin Type {}.", ty.full_path));
969                    graph.add_edge(*builtin_index, index, SqlGraphRelationship::RequiredByReturn);
970                }
971                if !found {
972                    for (ext_item, ext_index) in extension_sqls {
973                        if let Some(_) = ext_item
974                            .has_sql_declared_entity(&SqlDeclared::Type(ty.full_path.to_string()))
975                        {
976                            graph.add_edge(*ext_index, index, SqlGraphRelationship::RequiredByArg);
977                        } else if let Some(_) = ext_item
978                            .has_sql_declared_entity(&SqlDeclared::Enum(ty.full_path.to_string()))
979                        {
980                            graph.add_edge(*ext_index, index, SqlGraphRelationship::RequiredByArg);
981                        }
982                    }
983                }
984            }
985            PgExternReturnEntity::Iterated { tys: iterated_returns, optional: _, result: _ } => {
986                for PgExternReturnEntityIteratedItem { ty: type_entity, .. } in iterated_returns {
987                    let mut found = false;
988                    for (ty_item, &ty_index) in types {
989                        if ty_item.id_matches(&type_entity.ty_id) {
990                            graph.add_edge(ty_index, index, SqlGraphRelationship::RequiredByReturn);
991                            found = true;
992                            break;
993                        }
994                    }
995                    if !found {
996                        for (ty_item, &ty_index) in enums {
997                            if ty_item.id_matches(&type_entity.ty_id) {
998                                graph.add_edge(
999                                    ty_index,
1000                                    index,
1001                                    SqlGraphRelationship::RequiredByReturn,
1002                                );
1003                                found = true;
1004                                break;
1005                            }
1006                        }
1007                    }
1008                    if !found {
1009                        let builtin_index =
1010                            builtin_types.get(&type_entity.ty_source.to_string()).expect(&format!(
1011                                "Could not fetch Builtin Type {}.",
1012                                type_entity.ty_source,
1013                            ));
1014                        graph.add_edge(
1015                            *builtin_index,
1016                            index,
1017                            SqlGraphRelationship::RequiredByReturn,
1018                        );
1019                    }
1020                    if !found {
1021                        for (ext_item, ext_index) in extension_sqls {
1022                            if let Some(_) = ext_item.has_sql_declared_entity(&SqlDeclared::Type(
1023                                type_entity.ty_source.to_string(),
1024                            )) {
1025                                graph.add_edge(
1026                                    *ext_index,
1027                                    index,
1028                                    SqlGraphRelationship::RequiredByArg,
1029                                );
1030                            } else if let Some(_) = ext_item.has_sql_declared_entity(
1031                                &SqlDeclared::Enum(type_entity.ty_source.to_string()),
1032                            ) {
1033                                graph.add_edge(
1034                                    *ext_index,
1035                                    index,
1036                                    SqlGraphRelationship::RequiredByArg,
1037                                );
1038                            }
1039                        }
1040                    }
1041                }
1042            }
1043        }
1044    }
1045    Ok(())
1046}
1047
1048fn initialize_ords(
1049    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
1050    root: NodeIndex,
1051    bootstrap: Option<NodeIndex>,
1052    finalize: Option<NodeIndex>,
1053    ords: Vec<PostgresOrdEntity>,
1054) -> eyre::Result<HashMap<PostgresOrdEntity, NodeIndex>> {
1055    let mut mapped_ords = HashMap::default();
1056    for item in ords {
1057        let entity = item.clone().into();
1058        let index = graph.add_node(entity);
1059        mapped_ords.insert(item.clone(), index);
1060        build_base_edges(graph, index, root, bootstrap, finalize);
1061    }
1062    Ok(mapped_ords)
1063}
1064
1065fn connect_ords(
1066    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
1067    ords: &HashMap<PostgresOrdEntity, NodeIndex>,
1068    schemas: &HashMap<SchemaEntity, NodeIndex>,
1069    types: &HashMap<PostgresTypeEntity, NodeIndex>,
1070    enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1071    externs: &HashMap<PgExternEntity, NodeIndex>,
1072) {
1073    for (item, &index) in ords {
1074        make_schema_connection(
1075            graph,
1076            "Ord",
1077            index,
1078            &item.rust_identifier(),
1079            item.module_path,
1080            schemas,
1081        );
1082
1083        make_type_or_enum_connection(
1084            graph,
1085            "Ord",
1086            index,
1087            &item.rust_identifier(),
1088            &item.id,
1089            types,
1090            enums,
1091        );
1092
1093        // Make PostgresOrdEntities (which will be translated into `CREATE OPERATOR CLASS` statements) depend
1094        // on the operators which they will reference. For example, a pgx-defined Postgres type `parakeet`
1095        // which has `#[derive(PostgresOrd)]` will emit a `parakeet_btree_ops` operator class, which references
1096        // a definition of a < operator (among others) on the `parakeet` type. This code should ensure that the
1097        // < operator (along with all the others) is emitted before the `OPERATOR CLASS` itself.
1098
1099        for (extern_item, &extern_index) in externs {
1100            let fn_matches = |fn_name| {
1101                item.module_path == extern_item.module_path && extern_item.name == fn_name
1102            };
1103            let cmp_fn_matches = fn_matches(item.cmp_fn_name());
1104            let lt_fn_matches = fn_matches(item.lt_fn_name());
1105            let lte_fn_matches = fn_matches(item.le_fn_name());
1106            let eq_fn_matches = fn_matches(item.eq_fn_name());
1107            let gt_fn_matches = fn_matches(item.gt_fn_name());
1108            let gte_fn_matches = fn_matches(item.ge_fn_name());
1109            if cmp_fn_matches
1110                || lt_fn_matches
1111                || lte_fn_matches
1112                || eq_fn_matches
1113                || gt_fn_matches
1114                || gte_fn_matches
1115            {
1116                graph.add_edge(extern_index, index, SqlGraphRelationship::RequiredBy);
1117            }
1118        }
1119    }
1120}
1121
1122fn initialize_hashes(
1123    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
1124    root: NodeIndex,
1125    bootstrap: Option<NodeIndex>,
1126    finalize: Option<NodeIndex>,
1127    hashes: Vec<PostgresHashEntity>,
1128) -> eyre::Result<HashMap<PostgresHashEntity, NodeIndex>> {
1129    let mut mapped_hashes = HashMap::default();
1130    for item in hashes {
1131        let entity: SqlGraphEntity = item.clone().into();
1132        let index = graph.add_node(entity);
1133        mapped_hashes.insert(item, index);
1134        build_base_edges(graph, index, root, bootstrap, finalize);
1135    }
1136    Ok(mapped_hashes)
1137}
1138
1139fn connect_hashes(
1140    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
1141    hashes: &HashMap<PostgresHashEntity, NodeIndex>,
1142    schemas: &HashMap<SchemaEntity, NodeIndex>,
1143    types: &HashMap<PostgresTypeEntity, NodeIndex>,
1144    enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1145    externs: &HashMap<PgExternEntity, NodeIndex>,
1146) {
1147    for (item, &index) in hashes {
1148        make_schema_connection(
1149            graph,
1150            "Hash",
1151            index,
1152            &item.rust_identifier(),
1153            item.module_path,
1154            schemas,
1155        );
1156
1157        make_type_or_enum_connection(
1158            graph,
1159            "Hash",
1160            index,
1161            &item.rust_identifier(),
1162            &item.id,
1163            types,
1164            enums,
1165        );
1166
1167        for (extern_item, &extern_index) in externs {
1168            let hash_fn_name = item.fn_name();
1169            let hash_fn_matches =
1170                item.module_path == extern_item.module_path && extern_item.name == hash_fn_name;
1171
1172            if hash_fn_matches {
1173                graph.add_edge(extern_index, index, SqlGraphRelationship::RequiredBy);
1174                break;
1175            }
1176        }
1177    }
1178}
1179
1180fn initialize_aggregates(
1181    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
1182    root: NodeIndex,
1183    bootstrap: Option<NodeIndex>,
1184    finalize: Option<NodeIndex>,
1185    aggregates: Vec<PgAggregateEntity>,
1186    mapped_builtin_types: &mut HashMap<String, NodeIndex>,
1187    mapped_enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1188    mapped_types: &HashMap<PostgresTypeEntity, NodeIndex>,
1189) -> eyre::Result<HashMap<PgAggregateEntity, NodeIndex>> {
1190    let mut mapped_aggregates = HashMap::default();
1191    for item in aggregates {
1192        let entity: SqlGraphEntity = item.clone().into();
1193        let index = graph.add_node(entity);
1194
1195        for arg in &item.args {
1196            let mut found = false;
1197            for (ty_item, &_ty_index) in mapped_types {
1198                if ty_item.id_matches(&arg.used_ty.ty_id) {
1199                    found = true;
1200                    break;
1201                }
1202            }
1203            for (ty_item, &_ty_index) in mapped_enums {
1204                if ty_item.id_matches(&arg.used_ty.ty_id) {
1205                    found = true;
1206                    break;
1207                }
1208            }
1209            if !found {
1210                mapped_builtin_types.entry(arg.used_ty.full_path.to_string()).or_insert_with(
1211                    || {
1212                        graph.add_node(SqlGraphEntity::BuiltinType(
1213                            arg.used_ty.full_path.to_string(),
1214                        ))
1215                    },
1216                );
1217            }
1218        }
1219
1220        mapped_aggregates.insert(item, index);
1221        build_base_edges(graph, index, root, bootstrap, finalize);
1222    }
1223    Ok(mapped_aggregates)
1224}
1225
1226fn connect_aggregate(
1227    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
1228    item: &PgAggregateEntity,
1229    index: NodeIndex,
1230    schemas: &HashMap<SchemaEntity, NodeIndex>,
1231    types: &HashMap<PostgresTypeEntity, NodeIndex>,
1232    enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1233    builtin_types: &HashMap<String, NodeIndex>,
1234    externs: &HashMap<PgExternEntity, NodeIndex>,
1235) -> eyre::Result<()> {
1236    make_schema_connection(
1237        graph,
1238        "Aggregate",
1239        index,
1240        &item.rust_identifier(),
1241        item.module_path,
1242        schemas,
1243    );
1244
1245    make_type_or_enum_connection(
1246        graph,
1247        "Aggregate",
1248        index,
1249        &item.rust_identifier(),
1250        &item.ty_id,
1251        types,
1252        enums,
1253    );
1254
1255    for arg in &item.args {
1256        let found = make_type_or_enum_connection(
1257            graph,
1258            "Aggregate",
1259            index,
1260            &item.rust_identifier(),
1261            &arg.used_ty.ty_id,
1262            types,
1263            enums,
1264        );
1265        if !found {
1266            let builtin_index = builtin_types
1267                .get(arg.used_ty.full_path)
1268                .expect(&format!("Could not fetch Builtin Type {}.", arg.used_ty.full_path));
1269            graph.add_edge(*builtin_index, index, SqlGraphRelationship::RequiredByArg);
1270        }
1271    }
1272
1273    for arg in item.direct_args.as_ref().unwrap_or(&vec![]) {
1274        let found = make_type_or_enum_connection(
1275            graph,
1276            "Aggregate",
1277            index,
1278            &item.rust_identifier(),
1279            &arg.used_ty.ty_id,
1280            types,
1281            enums,
1282        );
1283        if !found {
1284            let builtin_index = builtin_types
1285                .get(arg.used_ty.full_path)
1286                .expect(&format!("Could not fetch Builtin Type {}.", arg.used_ty.full_path));
1287            graph.add_edge(*builtin_index, index, SqlGraphRelationship::RequiredByArg);
1288        }
1289    }
1290
1291    if let Some(arg) = &item.mstype {
1292        let found = make_type_or_enum_connection(
1293            graph,
1294            "Aggregate",
1295            index,
1296            &item.rust_identifier(),
1297            &arg.ty_id,
1298            types,
1299            enums,
1300        );
1301        if !found {
1302            let builtin_index = builtin_types
1303                .get(arg.full_path)
1304                .expect(&format!("Could not fetch Builtin Type {}.", arg.full_path));
1305            graph.add_edge(*builtin_index, index, SqlGraphRelationship::RequiredByArg);
1306        }
1307    }
1308
1309    make_extern_connection(
1310        graph,
1311        "Aggregate",
1312        index,
1313        &item.rust_identifier(),
1314        &(item.module_path.to_string() + "::" + item.sfunc),
1315        externs,
1316    )?;
1317
1318    if let Some(value) = item.finalfunc {
1319        make_extern_connection(
1320            graph,
1321            "Aggregate",
1322            index,
1323            &item.rust_identifier(),
1324            &(item.module_path.to_string() + "::" + value),
1325            externs,
1326        )?;
1327    }
1328    if let Some(value) = item.combinefunc {
1329        make_extern_connection(
1330            graph,
1331            "Aggregate",
1332            index,
1333            &item.rust_identifier(),
1334            &(item.module_path.to_string() + "::" + value),
1335            externs,
1336        )?;
1337    }
1338    if let Some(value) = item.serialfunc {
1339        make_extern_connection(
1340            graph,
1341            "Aggregate",
1342            index,
1343            &item.rust_identifier(),
1344            &(item.module_path.to_string() + "::" + value),
1345            externs,
1346        )?;
1347    }
1348    if let Some(value) = item.deserialfunc {
1349        make_extern_connection(
1350            graph,
1351            "Aggregate",
1352            index,
1353            &item.rust_identifier(),
1354            &(item.module_path.to_string() + "::" + value),
1355            externs,
1356        )?;
1357    }
1358    if let Some(value) = item.msfunc {
1359        make_extern_connection(
1360            graph,
1361            "Aggregate",
1362            index,
1363            &item.rust_identifier(),
1364            &(item.module_path.to_string() + "::" + value),
1365            externs,
1366        )?;
1367    }
1368    if let Some(value) = item.minvfunc {
1369        make_extern_connection(
1370            graph,
1371            "Aggregate",
1372            index,
1373            &item.rust_identifier(),
1374            &(item.module_path.to_string() + "::" + value),
1375            externs,
1376        )?;
1377    }
1378    if let Some(value) = item.mfinalfunc {
1379        make_extern_connection(
1380            graph,
1381            "Aggregate",
1382            index,
1383            &item.rust_identifier(),
1384            &(item.module_path.to_string() + "::" + value),
1385            externs,
1386        )?;
1387    }
1388    if let Some(value) = item.sortop {
1389        make_extern_connection(
1390            graph,
1391            "Aggregate",
1392            index,
1393            &item.rust_identifier(),
1394            &(item.module_path.to_string() + "::" + value),
1395            externs,
1396        )?;
1397    }
1398    Ok(())
1399}
1400
1401fn connect_aggregates(
1402    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
1403    aggregates: &HashMap<PgAggregateEntity, NodeIndex>,
1404    schemas: &HashMap<SchemaEntity, NodeIndex>,
1405    types: &HashMap<PostgresTypeEntity, NodeIndex>,
1406    enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1407    builtin_types: &HashMap<String, NodeIndex>,
1408    externs: &HashMap<PgExternEntity, NodeIndex>,
1409) -> eyre::Result<()> {
1410    for (item, &index) in aggregates {
1411        connect_aggregate(graph, item, index, schemas, types, enums, builtin_types, externs)?
1412    }
1413    Ok(())
1414}
1415
1416fn initialize_triggers(
1417    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
1418    root: NodeIndex,
1419    bootstrap: Option<NodeIndex>,
1420    finalize: Option<NodeIndex>,
1421    triggers: Vec<PgTriggerEntity>,
1422) -> eyre::Result<HashMap<PgTriggerEntity, NodeIndex>> {
1423    let mut mapped_triggers = HashMap::default();
1424    for item in triggers {
1425        let entity: SqlGraphEntity = item.clone().into();
1426        let index = graph.add_node(entity);
1427
1428        mapped_triggers.insert(item, index);
1429        build_base_edges(graph, index, root, bootstrap, finalize);
1430    }
1431    Ok(mapped_triggers)
1432}
1433
1434fn connect_triggers(
1435    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
1436    triggers: &HashMap<PgTriggerEntity, NodeIndex>,
1437    schemas: &HashMap<SchemaEntity, NodeIndex>,
1438) {
1439    for (item, &index) in triggers {
1440        make_schema_connection(
1441            graph,
1442            "Trigger",
1443            index,
1444            &item.rust_identifier(),
1445            item.module_path,
1446            schemas,
1447        );
1448    }
1449}
1450
1451fn make_schema_connection(
1452    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
1453    _kind: &str,
1454    index: NodeIndex,
1455    _rust_identifier: &str,
1456    module_path: &str,
1457    schemas: &HashMap<SchemaEntity, NodeIndex>,
1458) -> bool {
1459    let mut found = false;
1460    for (schema_item, &schema_index) in schemas {
1461        if module_path == schema_item.module_path {
1462            graph.add_edge(schema_index, index, SqlGraphRelationship::RequiredBy);
1463            found = true;
1464            break;
1465        }
1466    }
1467    found
1468}
1469
1470fn make_extern_connection(
1471    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
1472    _kind: &str,
1473    index: NodeIndex,
1474    _rust_identifier: &str,
1475    full_path: &str,
1476    externs: &HashMap<PgExternEntity, NodeIndex>,
1477) -> eyre::Result<()> {
1478    let mut found = false;
1479    for (extern_item, &extern_index) in externs {
1480        if full_path == extern_item.full_path {
1481            graph.add_edge(extern_index, index, SqlGraphRelationship::RequiredBy);
1482            found = true;
1483            break;
1484        }
1485    }
1486    match found {
1487        true => Ok(()),
1488        false => Err(eyre!("Did not find connection `{full_path}` in {:#?}", {
1489            let mut paths = externs.iter().map(|(v, _)| v.full_path).collect::<Vec<_>>();
1490            paths.sort();
1491            paths
1492        })),
1493    }
1494}
1495
1496fn make_type_or_enum_connection(
1497    graph: &mut StableGraph<SqlGraphEntity, SqlGraphRelationship>,
1498    _kind: &str,
1499    index: NodeIndex,
1500    _rust_identifier: &str,
1501    ty_id: &TypeId,
1502    types: &HashMap<PostgresTypeEntity, NodeIndex>,
1503    enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1504) -> bool {
1505    let mut found = false;
1506    for (ty_item, &ty_index) in types {
1507        if ty_item.id_matches(ty_id) {
1508            graph.add_edge(ty_index, index, SqlGraphRelationship::RequiredBy);
1509            found = true;
1510            break;
1511        }
1512    }
1513    for (ty_item, &ty_index) in enums {
1514        if ty_item.id_matches(ty_id) {
1515            graph.add_edge(ty_index, index, SqlGraphRelationship::RequiredBy);
1516            found = true;
1517            break;
1518        }
1519    }
1520
1521    found
1522}