1use 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#[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 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 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 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 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 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 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 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
534pub 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 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 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 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}