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