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::entity::{ExtensionSqlEntity, SqlDeclaredEntity};
31use crate::extension_sql::SqlDeclared;
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<()> {
255 use std::fs::{create_dir_all, File};
256 use std::io::Write;
257 let generated = self.to_sql()?;
258 let path = Path::new(file.as_ref());
259
260 let parent = path.parent();
261 if let Some(parent) = parent {
262 create_dir_all(parent)?;
263 }
264 let mut out = File::create(path)?;
265 write!(out, "{generated}")?;
266 Ok(())
267 }
268
269 pub fn write(&self, out: &mut impl std::io::Write) -> eyre::Result<()> {
270 let generated = self.to_sql()?;
271
272 #[cfg(feature = "syntax-highlighting")]
273 {
274 use std::io::{stdout, IsTerminal};
275 if stdout().is_terminal() {
276 self.write_highlighted(out, &generated)?;
277 } else {
278 write!(*out, "{}", generated)?;
279 }
280 }
281
282 #[cfg(not(feature = "syntax-highlighting"))]
283 {
284 write!(*out, "{generated}")?;
285 }
286
287 Ok(())
288 }
289
290 #[cfg(feature = "syntax-highlighting")]
291 fn write_highlighted(&self, out: &mut dyn std::io::Write, generated: &str) -> eyre::Result<()> {
292 use eyre::WrapErr as _;
293 use owo_colors::{OwoColorize, XtermColors};
294 use syntect::easy::HighlightLines;
295 use syntect::highlighting::{Style, ThemeSet};
296 use syntect::parsing::SyntaxSet;
297 use syntect::util::LinesWithEndings;
298 let ps = SyntaxSet::load_defaults_newlines();
299 let theme_bytes = include_str!("../assets/ansi.tmTheme").as_bytes();
300 let mut theme_reader = std::io::Cursor::new(theme_bytes);
301 let theme = ThemeSet::load_from_reader(&mut theme_reader)
302 .wrap_err("Couldn't parse theme for SQL highlighting, try piping to a file")?;
303
304 if let Some(syntax) = ps.find_syntax_by_extension("sql") {
305 let mut h = HighlightLines::new(syntax, &theme);
306 for line in LinesWithEndings::from(&generated) {
307 let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps)?;
308 for (style, content) in ranges {
310 if style.foreground.a == 0x01 {
311 write!(*out, "{}", content)?;
312 } else {
313 write!(*out, "{}", content.color(XtermColors::from(style.foreground.r)))?;
314 }
315 }
316 write!(*out, "\x1b[0m")?;
317 }
318 } else {
319 write!(*out, "{}", generated)?;
320 }
321 Ok(())
322 }
323
324 pub fn to_dot(&self, file: impl AsRef<Path> + Debug) -> eyre::Result<()> {
325 use std::fs::{create_dir_all, File};
326 use std::io::Write;
327 let generated = Dot::with_attr_getters(
328 &self.graph,
329 &[petgraph::dot::Config::EdgeNoLabel, petgraph::dot::Config::NodeNoLabel],
330 &|_graph, edge| {
331 match edge.weight() {
332 SqlGraphRequires::By => r#"color = "gray""#,
333 SqlGraphRequires::ByArg => r#"color = "black""#,
334 SqlGraphRequires::ByReturn => r#"dir = "back", color = "black""#,
335 }
336 .to_owned()
337 },
338 &|_graph, (_index, node)| {
339 let dot_id = node.dot_identifier();
340 match node {
341 SqlGraphEntity::Schema(_item) => format!(
343 "label = \"{dot_id}\", weight = 6, shape = \"tab\""
344 ),
345 SqlGraphEntity::Function(_item) => format!(
346 "label = \"{dot_id}\", penwidth = 0, style = \"filled\", fillcolor = \"#ADC7C6\", weight = 4, shape = \"box\"",
347 ),
348 SqlGraphEntity::Type(_item) => format!(
349 "label = \"{dot_id}\", penwidth = 0, style = \"filled\", fillcolor = \"#AE9BBD\", weight = 5, shape = \"oval\"",
350 ),
351 SqlGraphEntity::BuiltinType(_item) => format!(
352 "label = \"{dot_id}\", shape = \"plain\""
353 ),
354 SqlGraphEntity::Enum(_item) => format!(
355 "label = \"{dot_id}\", penwidth = 0, style = \"filled\", fillcolor = \"#C9A7C8\", weight = 5, shape = \"oval\""
356 ),
357 SqlGraphEntity::Ord(_item) => format!(
358 "label = \"{dot_id}\", penwidth = 0, style = \"filled\", fillcolor = \"#FFCFD3\", weight = 5, shape = \"diamond\""
359 ),
360 SqlGraphEntity::Hash(_item) => format!(
361 "label = \"{dot_id}\", penwidth = 0, style = \"filled\", fillcolor = \"#FFE4E0\", weight = 5, shape = \"diamond\""
362 ),
363 SqlGraphEntity::Aggregate(_item) => format!(
364 "label = \"{dot_id}\", penwidth = 0, style = \"filled\", fillcolor = \"#FFE4E0\", weight = 5, shape = \"diamond\""
365 ),
366 SqlGraphEntity::Trigger(_item) => format!(
367 "label = \"{dot_id}\", penwidth = 0, style = \"filled\", fillcolor = \"#FFE4E0\", weight = 5, shape = \"diamond\""
368 ),
369 SqlGraphEntity::CustomSql(_item) => format!(
370 "label = \"{dot_id}\", weight = 3, shape = \"signature\""
371 ),
372 SqlGraphEntity::ExtensionRoot(_item) => format!(
373 "label = \"{dot_id}\", shape = \"cylinder\""
374 ),
375 }
376 },
377 );
378 let path = Path::new(file.as_ref());
379
380 let parent = path.parent();
381 if let Some(parent) = parent {
382 create_dir_all(parent)?;
383 }
384 let mut out = File::create(path)?;
385 write!(out, "{generated:?}")?;
386 Ok(())
387 }
388
389 pub fn schema_alias_of(&self, item_index: &NodeIndex) -> Option<String> {
390 self.graph
391 .neighbors_undirected(*item_index)
392 .flat_map(|neighbor_index| match &self.graph[neighbor_index] {
393 SqlGraphEntity::Schema(s) => Some(String::from(s.name)),
394 SqlGraphEntity::ExtensionRoot(_control) => None,
395 _ => None,
396 })
397 .next()
398 }
399
400 pub fn schema_prefix_for(&self, target: &NodeIndex) -> String {
401 self.schema_alias_of(target).map(|v| (v + ".").to_string()).unwrap_or_default()
402 }
403
404 pub fn to_sql(&self) -> eyre::Result<String> {
405 let mut full_sql = String::new();
406
407 for nodes in petgraph::algo::tarjan_scc(&self.graph).iter().rev() {
424 let mut inner_sql = Vec::with_capacity(nodes.len());
425
426 for node in nodes {
427 let step = &self.graph[*node];
428 let sql = step.to_sql(self)?;
429
430 let trimmed = sql.trim();
431 if !trimmed.is_empty() {
432 inner_sql.push(format!("{trimmed}\n"))
433 }
434 }
435
436 if !inner_sql.is_empty() {
437 full_sql.push_str("/* <begin connected objects> */\n");
438 full_sql.push_str(&inner_sql.join("\n\n"));
439 full_sql.push_str("/* </end connected objects> */\n\n");
440 }
441 }
442
443 Ok(full_sql)
444 }
445
446 pub fn has_sql_declared_entity(&self, identifier: &SqlDeclared) -> Option<&SqlDeclaredEntity> {
447 self.extension_sqls.iter().find_map(|(item, _index)| {
448 item.creates
449 .iter()
450 .find(|create_entity| create_entity.has_sql_declared_entity(identifier))
451 })
452 }
453
454 pub fn get_module_pathname(&self) -> String {
455 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, SqlGraphRequires>,
468 index: NodeIndex,
469 root: NodeIndex,
470 bootstrap: Option<NodeIndex>,
471 finalize: Option<NodeIndex>,
472) {
473 graph.add_edge(root, index, SqlGraphRequires::By);
474 if let Some(bootstrap) = bootstrap {
475 graph.add_edge(bootstrap, index, SqlGraphRequires::By);
476 }
477 if let Some(finalize) = finalize {
478 graph.add_edge(index, finalize, SqlGraphRequires::By);
479 }
480}
481
482#[allow(clippy::type_complexity)]
483fn initialize_extension_sqls(
484 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
485 root: NodeIndex,
486 extension_sqls: Vec<ExtensionSqlEntity>,
487) -> eyre::Result<(HashMap<ExtensionSqlEntity, NodeIndex>, Option<NodeIndex>, Option<NodeIndex>)> {
488 let mut bootstrap = None;
489 let mut finalize = None;
490 let mut mapped_extension_sqls = HashMap::default();
491 for item in extension_sqls {
492 let entity: SqlGraphEntity = item.clone().into();
493 let index = graph.add_node(entity);
494 mapped_extension_sqls.insert(item.clone(), index);
495
496 if item.bootstrap {
497 if let Some(existing_index) = bootstrap {
498 let existing: &SqlGraphEntity = &graph[existing_index];
499 return Err(eyre!(
500 "Cannot have multiple `extension_sql!()` with `bootstrap` positioning, found `{}`, other was `{}`",
501 item.rust_identifier(),
502 existing.rust_identifier(),
503 ));
504 }
505 bootstrap = Some(index)
506 }
507 if item.finalize {
508 if let Some(existing_index) = finalize {
509 let existing: &SqlGraphEntity = &graph[existing_index];
510 return Err(eyre!(
511 "Cannot have multiple `extension_sql!()` with `finalize` positioning, found `{}`, other was `{}`",
512 item.rust_identifier(),
513 existing.rust_identifier(),
514 ));
515 }
516 finalize = Some(index)
517 }
518 }
519 for (item, index) in &mapped_extension_sqls {
520 graph.add_edge(root, *index, SqlGraphRequires::By);
521 if !item.bootstrap {
522 if let Some(bootstrap) = bootstrap {
523 graph.add_edge(bootstrap, *index, SqlGraphRequires::By);
524 }
525 }
526 if !item.finalize {
527 if let Some(finalize) = finalize {
528 graph.add_edge(*index, finalize, SqlGraphRequires::By);
529 }
530 }
531 }
532 Ok((mapped_extension_sqls, bootstrap, finalize))
533}
534
535pub fn find_positioning_ref_target<'a>(
537 positioning_ref: &'a PositioningRef,
538 types: &'a HashMap<PostgresTypeEntity, NodeIndex>,
539 enums: &'a HashMap<PostgresEnumEntity, NodeIndex>,
540 externs: &'a HashMap<PgExternEntity, NodeIndex>,
541 schemas: &'a HashMap<SchemaEntity, NodeIndex>,
542 extension_sqls: &'a HashMap<ExtensionSqlEntity, NodeIndex>,
543 triggers: &'a HashMap<PgTriggerEntity, NodeIndex>,
544) -> Option<&'a NodeIndex> {
545 match positioning_ref {
546 PositioningRef::FullPath(path) => {
547 let segments = path.split("::").collect::<Vec<_>>();
549 let last_segment = segments.last().expect("Expected at least one segment.");
550 let rest = &segments[..segments.len() - 1];
551 let module_path = rest.join("::");
552
553 for (other, other_index) in types {
554 if *last_segment == other.name && other.module_path.ends_with(&module_path) {
555 return Some(other_index);
556 }
557 }
558 for (other, other_index) in enums {
559 if last_segment == &other.name && other.module_path.ends_with(&module_path) {
560 return Some(other_index);
561 }
562 }
563 for (other, other_index) in externs {
564 if *last_segment == other.unaliased_name
565 && other.module_path.ends_with(&module_path)
566 {
567 return Some(other_index);
568 }
569 }
570 for (other, other_index) in schemas {
571 if other.module_path.ends_with(path) {
572 return Some(other_index);
573 }
574 }
575
576 for (other, other_index) in triggers {
577 if last_segment == &other.function_name && other.module_path.ends_with(&module_path)
578 {
579 return Some(other_index);
580 }
581 }
582 }
583 PositioningRef::Name(name) => {
584 for (other, other_index) in extension_sqls {
585 if other.name == name {
586 return Some(other_index);
587 }
588 }
589 }
590 };
591 None
592}
593
594fn connect_extension_sqls(
595 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
596 extension_sqls: &HashMap<ExtensionSqlEntity, NodeIndex>,
597 schemas: &HashMap<SchemaEntity, NodeIndex>,
598 types: &HashMap<PostgresTypeEntity, NodeIndex>,
599 enums: &HashMap<PostgresEnumEntity, NodeIndex>,
600 externs: &HashMap<PgExternEntity, NodeIndex>,
601 triggers: &HashMap<PgTriggerEntity, NodeIndex>,
602) -> eyre::Result<()> {
603 for (item, &index) in extension_sqls {
604 make_schema_connection(
605 graph,
606 "Extension SQL",
607 index,
608 &item.rust_identifier(),
609 item.module_path,
610 schemas,
611 );
612
613 for requires in &item.requires {
614 if let Some(target) = find_positioning_ref_target(
615 requires,
616 types,
617 enums,
618 externs,
619 schemas,
620 extension_sqls,
621 triggers,
622 ) {
623 graph.add_edge(*target, index, SqlGraphRequires::By);
624 } else {
625 return Err(eyre!(
626 "Could not find `requires` target of `{}`{}: {}",
627 item.rust_identifier(),
628 match (item.file(), item.line()) {
629 (Some(file), Some(line)) => format!(" ({file}:{line})"),
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, SqlGraphRequires>,
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, SqlGraphRequires::By);
656 }
657 if let Some(finalize) = finalize {
658 graph.add_edge(index, finalize, SqlGraphRequires::By);
659 }
660 }
661 Ok(mapped_schemas)
662}
663
664fn connect_schemas(
665 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
666 schemas: &HashMap<SchemaEntity, NodeIndex>,
667 root: NodeIndex,
668) {
669 for index in schemas.values().copied() {
670 graph.add_edge(root, index, SqlGraphRequires::By);
671 }
672}
673
674fn initialize_enums(
675 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
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, SqlGraphRequires>,
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, SqlGraphRequires>,
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, SqlGraphRequires>,
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, SqlGraphRequires>,
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 found = mapped_types.keys().any(|ty_item| ty_item.id_matches(&arg.used_ty.ty_id))
761 || mapped_enums.keys().any(|ty_item| ty_item.id_matches(&arg.used_ty.ty_id));
762
763 if !found {
764 mapped_builtin_types.entry(arg.used_ty.full_path.to_string()).or_insert_with(
765 || {
766 graph.add_node(SqlGraphEntity::BuiltinType(
767 arg.used_ty.full_path.to_string(),
768 ))
769 },
770 );
771 }
772 }
773
774 match &item.fn_return {
775 PgExternReturnEntity::None | PgExternReturnEntity::Trigger => (),
776 PgExternReturnEntity::Type { ty, .. } | PgExternReturnEntity::SetOf { ty, .. } => {
777 let found = mapped_types.keys().any(|ty_item| ty_item.id_matches(&ty.ty_id))
778 || mapped_enums.keys().any(|ty_item| ty_item.id_matches(&ty.ty_id));
779
780 if !found {
781 mapped_builtin_types.entry(ty.full_path.to_string()).or_insert_with(|| {
782 graph.add_node(SqlGraphEntity::BuiltinType(ty.full_path.to_string()))
783 });
784 }
785 }
786 PgExternReturnEntity::Iterated { tys: iterated_returns, .. } => {
787 for PgExternReturnEntityIteratedItem { ty, .. } in iterated_returns {
788 let found = mapped_types.keys().any(|ty_item| ty_item.id_matches(&ty.ty_id))
789 || mapped_enums.keys().any(|ty_item| ty_item.id_matches(&ty.ty_id));
790
791 if !found {
792 mapped_builtin_types.entry(ty.ty_source.to_string()).or_insert_with(|| {
793 graph.add_node(SqlGraphEntity::BuiltinType(ty.ty_source.to_string()))
794 });
795 }
796 }
797 }
798 }
799 }
800 Ok((mapped_externs, mapped_builtin_types))
801}
802
803fn connect_externs(
804 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
805 externs: &HashMap<PgExternEntity, NodeIndex>,
806 hashes: &HashMap<PostgresHashEntity, NodeIndex>,
807 schemas: &HashMap<SchemaEntity, NodeIndex>,
808 types: &HashMap<PostgresTypeEntity, NodeIndex>,
809 enums: &HashMap<PostgresEnumEntity, NodeIndex>,
810 builtin_types: &HashMap<String, NodeIndex>,
811 extension_sqls: &HashMap<ExtensionSqlEntity, NodeIndex>,
812 triggers: &HashMap<PgTriggerEntity, NodeIndex>,
813) -> eyre::Result<()> {
814 for (item, &index) in externs {
815 let mut found_schema_declaration = false;
816 let mut has_explicit_requires = false;
817 for extern_attr in &item.extern_attrs {
818 match extern_attr {
819 crate::ExternArgs::Requires(requirements) => {
820 for requires in requirements {
821 if let Some(target) = find_positioning_ref_target(
822 requires,
823 types,
824 enums,
825 externs,
826 schemas,
827 extension_sqls,
828 triggers,
829 ) {
830 graph.add_edge(*target, index, SqlGraphRequires::By);
831 has_explicit_requires = true;
832 } else {
833 return Err(eyre!("Could not find `requires` target: {:?}", requires));
834 }
835 }
836 }
837 crate::ExternArgs::Schema(declared_schema_name) => {
838 for (schema, schema_index) in schemas {
839 if schema.name == declared_schema_name {
840 graph.add_edge(*schema_index, index, SqlGraphRequires::By);
841 found_schema_declaration = true;
842 }
843 }
844 if !found_schema_declaration {
845 return Err(eyre!("Got manual `schema = \"{declared_schema_name}\"` setting, but that schema did not exist."));
846 }
847 }
848 _ => (),
849 }
850 }
851
852 if !found_schema_declaration {
853 make_schema_connection(
854 graph,
855 "Extern",
856 index,
857 &item.rust_identifier(),
858 item.module_path,
859 schemas,
860 );
861 }
862
863 for (hash_item, &hash_index) in hashes {
865 if item.module_path == hash_item.module_path
866 && item.name == hash_item.name.to_lowercase() + "_eq"
867 {
868 graph.add_edge(index, hash_index, SqlGraphRequires::By);
869 }
870 }
871
872 for arg in &item.fn_args {
873 let found = types
874 .iter()
875 .map(type_keyed)
876 .chain(enums.iter().map(type_keyed))
877 .find(|(item, _)| item.id_matches(&arg.used_ty.ty_id));
878 if let Some((_, ty_index)) = found {
879 graph.add_edge(*ty_index, index, SqlGraphRequires::ByArg);
880 } else {
881 let builtin_index = builtin_types.get(arg.used_ty.full_path).unwrap_or_else(|| {
882 panic!("Could not fetch Builtin Type {}.", arg.used_ty.full_path)
883 });
884 graph.add_edge(*builtin_index, index, SqlGraphRequires::ByArg);
885 }
886 for (ext_item, ext_index) in extension_sqls {
887 if ext_item
888 .has_sql_declared_entity(&SqlDeclared::Type(arg.used_ty.full_path.to_string()))
889 .is_some()
890 {
891 if !has_explicit_requires {
892 graph.add_edge(*ext_index, index, SqlGraphRequires::ByArg);
893 }
894 } else if ext_item
895 .has_sql_declared_entity(&SqlDeclared::Enum(arg.used_ty.full_path.to_string()))
896 .is_some()
897 {
898 graph.add_edge(*ext_index, index, SqlGraphRequires::ByArg);
899 }
900 }
901 }
902
903 match &item.fn_return {
904 PgExternReturnEntity::None | PgExternReturnEntity::Trigger => (),
905 PgExternReturnEntity::Type { ty, .. } | PgExternReturnEntity::SetOf { ty, .. } => {
906 let found_index =
907 types.iter().map(type_keyed).chain(enums.iter().map(type_keyed)).find_map(
908 |(ty_item, index)| ty_item.id_matches(&ty.ty_id).then_some(index),
909 );
910 if let Some(ty_index) = found_index {
911 graph.add_edge(*ty_index, index, SqlGraphRequires::ByReturn);
912 } else {
913 let builtin_index =
914 builtin_types.get(&ty.full_path.to_string()).unwrap_or_else(|| {
915 panic!("Could not fetch Builtin Type {}.", ty.full_path)
916 });
917 graph.add_edge(*builtin_index, index, SqlGraphRequires::ByReturn);
918 for (ext_item, ext_index) in extension_sqls {
919 if ext_item
920 .has_sql_declared_entity(&SqlDeclared::Type(ty.full_path.into()))
921 .is_some()
922 || ext_item
923 .has_sql_declared_entity(&SqlDeclared::Enum(ty.full_path.into()))
924 .is_some()
925 {
926 graph.add_edge(*ext_index, index, SqlGraphRequires::ByArg);
927 }
928 }
929 }
930 }
931 PgExternReturnEntity::Iterated { tys: iterated_returns, .. } => {
932 for PgExternReturnEntityIteratedItem { ty, .. } in iterated_returns {
933 let found_index =
934 types.iter().map(type_keyed).chain(enums.iter().map(type_keyed)).find_map(
935 |(ty_item, index)| ty_item.id_matches(&ty.ty_id).then_some(index),
936 );
937 if let Some(ty_index) = found_index {
938 graph.add_edge(*ty_index, index, SqlGraphRequires::ByReturn);
939 } else {
940 let builtin_index = builtin_types.get(ty.ty_source).unwrap_or_else(|| {
941 panic!("Could not fetch Builtin Type {}.", ty.ty_source)
942 });
943 graph.add_edge(*builtin_index, index, SqlGraphRequires::ByReturn);
944 for (ext_item, ext_index) in extension_sqls {
945 if ext_item
946 .has_sql_declared_entity(&SqlDeclared::Type(
947 ty.ty_source.to_string(),
948 ))
949 .is_some()
950 || ext_item
951 .has_sql_declared_entity(&SqlDeclared::Enum(
952 ty.ty_source.to_string(),
953 ))
954 .is_some()
955 {
956 graph.add_edge(*ext_index, index, SqlGraphRequires::ByArg);
957 }
958 }
959 }
960 }
961 }
962 }
963 }
964 Ok(())
965}
966
967fn initialize_ords(
968 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
969 root: NodeIndex,
970 bootstrap: Option<NodeIndex>,
971 finalize: Option<NodeIndex>,
972 ords: Vec<PostgresOrdEntity>,
973) -> eyre::Result<HashMap<PostgresOrdEntity, NodeIndex>> {
974 let mut mapped_ords = HashMap::default();
975 for item in ords {
976 let entity = item.clone().into();
977 let index = graph.add_node(entity);
978 mapped_ords.insert(item.clone(), index);
979 build_base_edges(graph, index, root, bootstrap, finalize);
980 }
981 Ok(mapped_ords)
982}
983
984fn connect_ords(
985 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
986 ords: &HashMap<PostgresOrdEntity, NodeIndex>,
987 schemas: &HashMap<SchemaEntity, NodeIndex>,
988 types: &HashMap<PostgresTypeEntity, NodeIndex>,
989 enums: &HashMap<PostgresEnumEntity, NodeIndex>,
990 externs: &HashMap<PgExternEntity, NodeIndex>,
991) {
992 for (item, &index) in ords {
993 make_schema_connection(
994 graph,
995 "Ord",
996 index,
997 &item.rust_identifier(),
998 item.module_path,
999 schemas,
1000 );
1001
1002 make_type_or_enum_connection(
1003 graph,
1004 "Ord",
1005 index,
1006 &item.rust_identifier(),
1007 &item.id,
1008 types,
1009 enums,
1010 );
1011
1012 for (extern_item, &extern_index) in externs {
1019 let fn_matches = |fn_name| {
1020 item.module_path == extern_item.module_path && extern_item.name == fn_name
1021 };
1022 let cmp_fn_matches = fn_matches(item.cmp_fn_name());
1023 let lt_fn_matches = fn_matches(item.lt_fn_name());
1024 let lte_fn_matches = fn_matches(item.le_fn_name());
1025 let eq_fn_matches = fn_matches(item.eq_fn_name());
1026 let gt_fn_matches = fn_matches(item.gt_fn_name());
1027 let gte_fn_matches = fn_matches(item.ge_fn_name());
1028 if cmp_fn_matches
1029 || lt_fn_matches
1030 || lte_fn_matches
1031 || eq_fn_matches
1032 || gt_fn_matches
1033 || gte_fn_matches
1034 {
1035 graph.add_edge(extern_index, index, SqlGraphRequires::By);
1036 }
1037 }
1038 }
1039}
1040
1041fn initialize_hashes(
1042 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1043 root: NodeIndex,
1044 bootstrap: Option<NodeIndex>,
1045 finalize: Option<NodeIndex>,
1046 hashes: Vec<PostgresHashEntity>,
1047) -> eyre::Result<HashMap<PostgresHashEntity, NodeIndex>> {
1048 let mut mapped_hashes = HashMap::default();
1049 for item in hashes {
1050 let entity: SqlGraphEntity = item.clone().into();
1051 let index = graph.add_node(entity);
1052 mapped_hashes.insert(item, index);
1053 build_base_edges(graph, index, root, bootstrap, finalize);
1054 }
1055 Ok(mapped_hashes)
1056}
1057
1058fn connect_hashes(
1059 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1060 hashes: &HashMap<PostgresHashEntity, NodeIndex>,
1061 schemas: &HashMap<SchemaEntity, NodeIndex>,
1062 types: &HashMap<PostgresTypeEntity, NodeIndex>,
1063 enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1064 externs: &HashMap<PgExternEntity, NodeIndex>,
1065) {
1066 for (item, &index) in hashes {
1067 make_schema_connection(
1068 graph,
1069 "Hash",
1070 index,
1071 &item.rust_identifier(),
1072 item.module_path,
1073 schemas,
1074 );
1075
1076 make_type_or_enum_connection(
1077 graph,
1078 "Hash",
1079 index,
1080 &item.rust_identifier(),
1081 &item.id,
1082 types,
1083 enums,
1084 );
1085
1086 if let Some((_, extern_index)) = externs.iter().find(|(extern_item, _)| {
1087 item.module_path == extern_item.module_path && extern_item.name == item.fn_name()
1088 }) {
1089 graph.add_edge(*extern_index, index, SqlGraphRequires::By);
1090 }
1091 }
1092}
1093
1094fn initialize_aggregates(
1095 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1096 root: NodeIndex,
1097 bootstrap: Option<NodeIndex>,
1098 finalize: Option<NodeIndex>,
1099 aggregates: Vec<PgAggregateEntity>,
1100 mapped_builtin_types: &mut HashMap<String, NodeIndex>,
1101 mapped_enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1102 mapped_types: &HashMap<PostgresTypeEntity, NodeIndex>,
1103) -> eyre::Result<HashMap<PgAggregateEntity, NodeIndex>> {
1104 let mut mapped_aggregates = HashMap::default();
1105 for item in aggregates {
1106 let entity: SqlGraphEntity = item.clone().into();
1107 let index = graph.add_node(entity);
1108
1109 for arg in &item.args {
1110 let found = mapped_types
1111 .iter()
1112 .map(type_keyed)
1113 .chain(mapped_enums.iter().map(type_keyed))
1114 .find(|(item, _)| item.id_matches(&arg.used_ty.ty_id));
1115
1116 if found.is_none() {
1117 mapped_builtin_types.entry(arg.used_ty.full_path.to_string()).or_insert_with(
1118 || {
1119 graph.add_node(SqlGraphEntity::BuiltinType(
1120 arg.used_ty.full_path.to_string(),
1121 ))
1122 },
1123 );
1124 }
1125 }
1126
1127 mapped_aggregates.insert(item, index);
1128 build_base_edges(graph, index, root, bootstrap, finalize);
1129 }
1130 Ok(mapped_aggregates)
1131}
1132
1133fn connect_aggregate(
1134 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1135 item: &PgAggregateEntity,
1136 index: NodeIndex,
1137 schemas: &HashMap<SchemaEntity, NodeIndex>,
1138 types: &HashMap<PostgresTypeEntity, NodeIndex>,
1139 enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1140 builtin_types: &HashMap<String, NodeIndex>,
1141 externs: &HashMap<PgExternEntity, NodeIndex>,
1142) -> eyre::Result<()> {
1143 make_schema_connection(
1144 graph,
1145 "Aggregate",
1146 index,
1147 &item.rust_identifier(),
1148 item.module_path,
1149 schemas,
1150 );
1151
1152 make_type_or_enum_connection(
1153 graph,
1154 "Aggregate",
1155 index,
1156 &item.rust_identifier(),
1157 &item.ty_id,
1158 types,
1159 enums,
1160 );
1161
1162 for arg in &item.args {
1163 let found = make_type_or_enum_connection(
1164 graph,
1165 "Aggregate",
1166 index,
1167 &item.rust_identifier(),
1168 &arg.used_ty.ty_id,
1169 types,
1170 enums,
1171 );
1172 if !found {
1173 let builtin_index = builtin_types.get(arg.used_ty.full_path).unwrap_or_else(|| {
1174 panic!("Could not fetch Builtin Type {}.", arg.used_ty.full_path)
1175 });
1176 graph.add_edge(*builtin_index, index, SqlGraphRequires::ByArg);
1177 }
1178 }
1179
1180 for arg in item.direct_args.as_ref().unwrap_or(&vec![]) {
1181 let found = make_type_or_enum_connection(
1182 graph,
1183 "Aggregate",
1184 index,
1185 &item.rust_identifier(),
1186 &arg.used_ty.ty_id,
1187 types,
1188 enums,
1189 );
1190 if !found {
1191 let builtin_index = builtin_types.get(arg.used_ty.full_path).unwrap_or_else(|| {
1192 panic!("Could not fetch Builtin Type {}.", arg.used_ty.full_path)
1193 });
1194 graph.add_edge(*builtin_index, index, SqlGraphRequires::ByArg);
1195 }
1196 }
1197
1198 if let Some(arg) = &item.mstype {
1199 let found = make_type_or_enum_connection(
1200 graph,
1201 "Aggregate",
1202 index,
1203 &item.rust_identifier(),
1204 &arg.ty_id,
1205 types,
1206 enums,
1207 );
1208 if !found {
1209 let builtin_index = builtin_types
1210 .get(arg.full_path)
1211 .unwrap_or_else(|| panic!("Could not fetch Builtin Type {}.", arg.full_path));
1212 graph.add_edge(*builtin_index, index, SqlGraphRequires::ByArg);
1213 }
1214 }
1215
1216 make_extern_connection(
1217 graph,
1218 "Aggregate",
1219 index,
1220 &item.rust_identifier(),
1221 &(item.module_path.to_string() + "::" + item.sfunc),
1222 externs,
1223 )?;
1224
1225 if let Some(value) = item.finalfunc {
1226 make_extern_connection(
1227 graph,
1228 "Aggregate",
1229 index,
1230 &item.rust_identifier(),
1231 &(item.module_path.to_string() + "::" + value),
1232 externs,
1233 )?;
1234 }
1235 if let Some(value) = item.combinefunc {
1236 make_extern_connection(
1237 graph,
1238 "Aggregate",
1239 index,
1240 &item.rust_identifier(),
1241 &(item.module_path.to_string() + "::" + value),
1242 externs,
1243 )?;
1244 }
1245 if let Some(value) = item.serialfunc {
1246 make_extern_connection(
1247 graph,
1248 "Aggregate",
1249 index,
1250 &item.rust_identifier(),
1251 &(item.module_path.to_string() + "::" + value),
1252 externs,
1253 )?;
1254 }
1255 if let Some(value) = item.deserialfunc {
1256 make_extern_connection(
1257 graph,
1258 "Aggregate",
1259 index,
1260 &item.rust_identifier(),
1261 &(item.module_path.to_string() + "::" + value),
1262 externs,
1263 )?;
1264 }
1265 if let Some(value) = item.msfunc {
1266 make_extern_connection(
1267 graph,
1268 "Aggregate",
1269 index,
1270 &item.rust_identifier(),
1271 &(item.module_path.to_string() + "::" + value),
1272 externs,
1273 )?;
1274 }
1275 if let Some(value) = item.minvfunc {
1276 make_extern_connection(
1277 graph,
1278 "Aggregate",
1279 index,
1280 &item.rust_identifier(),
1281 &(item.module_path.to_string() + "::" + value),
1282 externs,
1283 )?;
1284 }
1285 if let Some(value) = item.mfinalfunc {
1286 make_extern_connection(
1287 graph,
1288 "Aggregate",
1289 index,
1290 &item.rust_identifier(),
1291 &(item.module_path.to_string() + "::" + value),
1292 externs,
1293 )?;
1294 }
1295 if let Some(value) = item.sortop {
1296 make_extern_connection(
1297 graph,
1298 "Aggregate",
1299 index,
1300 &item.rust_identifier(),
1301 &(item.module_path.to_string() + "::" + value),
1302 externs,
1303 )?;
1304 }
1305 Ok(())
1306}
1307
1308fn connect_aggregates(
1309 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1310 aggregates: &HashMap<PgAggregateEntity, NodeIndex>,
1311 schemas: &HashMap<SchemaEntity, NodeIndex>,
1312 types: &HashMap<PostgresTypeEntity, NodeIndex>,
1313 enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1314 builtin_types: &HashMap<String, NodeIndex>,
1315 externs: &HashMap<PgExternEntity, NodeIndex>,
1316) -> eyre::Result<()> {
1317 for (item, &index) in aggregates {
1318 connect_aggregate(graph, item, index, schemas, types, enums, builtin_types, externs)?
1319 }
1320 Ok(())
1321}
1322
1323fn initialize_triggers(
1324 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1325 root: NodeIndex,
1326 bootstrap: Option<NodeIndex>,
1327 finalize: Option<NodeIndex>,
1328 triggers: Vec<PgTriggerEntity>,
1329) -> eyre::Result<HashMap<PgTriggerEntity, NodeIndex>> {
1330 let mut mapped_triggers = HashMap::default();
1331 for item in triggers {
1332 let entity: SqlGraphEntity = item.clone().into();
1333 let index = graph.add_node(entity);
1334
1335 mapped_triggers.insert(item, index);
1336 build_base_edges(graph, index, root, bootstrap, finalize);
1337 }
1338 Ok(mapped_triggers)
1339}
1340
1341fn connect_triggers(
1342 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1343 triggers: &HashMap<PgTriggerEntity, NodeIndex>,
1344 schemas: &HashMap<SchemaEntity, NodeIndex>,
1345) {
1346 for (item, &index) in triggers {
1347 make_schema_connection(
1348 graph,
1349 "Trigger",
1350 index,
1351 &item.rust_identifier(),
1352 item.module_path,
1353 schemas,
1354 );
1355 }
1356}
1357
1358fn make_schema_connection(
1359 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1360 _kind: &str,
1361 index: NodeIndex,
1362 _rust_identifier: &str,
1363 module_path: &str,
1364 schemas: &HashMap<SchemaEntity, NodeIndex>,
1365) -> bool {
1366 let mut found = false;
1367 for (schema_item, &schema_index) in schemas {
1368 if module_path == schema_item.module_path {
1369 graph.add_edge(schema_index, index, SqlGraphRequires::By);
1370 found = true;
1371 break;
1372 }
1373 }
1374 found
1375}
1376
1377fn make_extern_connection(
1378 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1379 _kind: &str,
1380 index: NodeIndex,
1381 _rust_identifier: &str,
1382 full_path: &str,
1383 externs: &HashMap<PgExternEntity, NodeIndex>,
1384) -> eyre::Result<()> {
1385 match externs.iter().find(|(extern_item, _)| full_path == extern_item.full_path) {
1386 Some((_, extern_index)) => {
1387 graph.add_edge(*extern_index, index, SqlGraphRequires::By);
1388 Ok(())
1389 }
1390 None => Err(eyre!("Did not find connection `{full_path}` in {:#?}", {
1391 let mut paths = externs.iter().map(|(v, _)| v.full_path).collect::<Vec<_>>();
1392 paths.sort();
1393 paths
1394 })),
1395 }
1396}
1397
1398fn make_type_or_enum_connection(
1399 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1400 _kind: &str,
1401 index: NodeIndex,
1402 _rust_identifier: &str,
1403 ty_id: &TypeId,
1404 types: &HashMap<PostgresTypeEntity, NodeIndex>,
1405 enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1406) -> bool {
1407 types
1408 .iter()
1409 .map(type_keyed)
1410 .chain(enums.iter().map(type_keyed))
1411 .find(|(ty, _)| ty.id_matches(ty_id))
1412 .map(|(_, ty_index)| graph.add_edge(*ty_index, index, SqlGraphRequires::By))
1413 .is_some()
1414}