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!("{extname}-{extver}")
460 } else {
461 String::from("MODULE_PATHNAME")
462 }
463 }
464
465 pub fn find_matching_fn(&self, name: &str) -> Option<&PgExternEntity> {
466 self.externs.keys().find(|key| key.full_path.ends_with(name))
467 }
468}
469
470fn build_base_edges(
471 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
472 index: NodeIndex,
473 root: NodeIndex,
474 bootstrap: Option<NodeIndex>,
475 finalize: Option<NodeIndex>,
476) {
477 graph.add_edge(root, index, SqlGraphRequires::By);
478 if let Some(bootstrap) = bootstrap {
479 graph.add_edge(bootstrap, index, SqlGraphRequires::By);
480 }
481 if let Some(finalize) = finalize {
482 graph.add_edge(index, finalize, SqlGraphRequires::By);
483 }
484}
485
486#[allow(clippy::type_complexity)]
487fn initialize_extension_sqls(
488 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
489 root: NodeIndex,
490 extension_sqls: Vec<ExtensionSqlEntity>,
491) -> eyre::Result<(HashMap<ExtensionSqlEntity, NodeIndex>, Option<NodeIndex>, Option<NodeIndex>)> {
492 let mut bootstrap = None;
493 let mut finalize = None;
494 let mut mapped_extension_sqls = HashMap::default();
495 for item in extension_sqls {
496 let entity: SqlGraphEntity = item.clone().into();
497 let index = graph.add_node(entity);
498 mapped_extension_sqls.insert(item.clone(), index);
499
500 if item.bootstrap {
501 if let Some(existing_index) = bootstrap {
502 let existing: &SqlGraphEntity = &graph[existing_index];
503 return Err(eyre!(
504 "Cannot have multiple `extension_sql!()` with `bootstrap` positioning, found `{}`, other was `{}`",
505 item.rust_identifier(),
506 existing.rust_identifier(),
507 ));
508 }
509 bootstrap = Some(index)
510 }
511 if item.finalize {
512 if let Some(existing_index) = finalize {
513 let existing: &SqlGraphEntity = &graph[existing_index];
514 return Err(eyre!(
515 "Cannot have multiple `extension_sql!()` with `finalize` positioning, found `{}`, other was `{}`",
516 item.rust_identifier(),
517 existing.rust_identifier(),
518 ));
519 }
520 finalize = Some(index)
521 }
522 }
523 for (item, index) in &mapped_extension_sqls {
524 graph.add_edge(root, *index, SqlGraphRequires::By);
525 if !item.bootstrap {
526 if let Some(bootstrap) = bootstrap {
527 graph.add_edge(bootstrap, *index, SqlGraphRequires::By);
528 }
529 }
530 if !item.finalize {
531 if let Some(finalize) = finalize {
532 graph.add_edge(*index, finalize, SqlGraphRequires::By);
533 }
534 }
535 }
536 Ok((mapped_extension_sqls, bootstrap, finalize))
537}
538
539pub fn find_positioning_ref_target<'a>(
541 positioning_ref: &'a PositioningRef,
542 types: &'a HashMap<PostgresTypeEntity, NodeIndex>,
543 enums: &'a HashMap<PostgresEnumEntity, NodeIndex>,
544 externs: &'a HashMap<PgExternEntity, NodeIndex>,
545 schemas: &'a HashMap<SchemaEntity, NodeIndex>,
546 extension_sqls: &'a HashMap<ExtensionSqlEntity, NodeIndex>,
547 triggers: &'a HashMap<PgTriggerEntity, NodeIndex>,
548) -> Option<&'a NodeIndex> {
549 match positioning_ref {
550 PositioningRef::FullPath(path) => {
551 let segments = path.split("::").collect::<Vec<_>>();
553 let last_segment = segments.last().expect("Expected at least one segment.");
554 let rest = &segments[..segments.len() - 1];
555 let module_path = rest.join("::");
556
557 for (other, other_index) in types {
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 enums {
563 if last_segment == &other.name && other.module_path.ends_with(&module_path) {
564 return Some(other_index);
565 }
566 }
567 for (other, other_index) in externs {
568 if *last_segment == other.unaliased_name
569 && other.module_path.ends_with(&module_path)
570 {
571 return Some(other_index);
572 }
573 }
574 for (other, other_index) in schemas {
575 if other.module_path.ends_with(path) {
576 return Some(other_index);
577 }
578 }
579
580 for (other, other_index) in triggers {
581 if last_segment == &other.function_name && other.module_path.ends_with(&module_path)
582 {
583 return Some(other_index);
584 }
585 }
586 }
587 PositioningRef::Name(name) => {
588 for (other, other_index) in extension_sqls {
589 if other.name == name {
590 return Some(other_index);
591 }
592 }
593 }
594 };
595 None
596}
597
598fn connect_extension_sqls(
599 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
600 extension_sqls: &HashMap<ExtensionSqlEntity, NodeIndex>,
601 schemas: &HashMap<SchemaEntity, NodeIndex>,
602 types: &HashMap<PostgresTypeEntity, NodeIndex>,
603 enums: &HashMap<PostgresEnumEntity, NodeIndex>,
604 externs: &HashMap<PgExternEntity, NodeIndex>,
605 triggers: &HashMap<PgTriggerEntity, NodeIndex>,
606) -> eyre::Result<()> {
607 for (item, &index) in extension_sqls {
608 make_schema_connection(
609 graph,
610 "Extension SQL",
611 index,
612 &item.rust_identifier(),
613 item.module_path,
614 schemas,
615 );
616
617 for requires in &item.requires {
618 if let Some(target) = find_positioning_ref_target(
619 requires,
620 types,
621 enums,
622 externs,
623 schemas,
624 extension_sqls,
625 triggers,
626 ) {
627 graph.add_edge(*target, index, SqlGraphRequires::By);
628 } else {
629 return Err(eyre!(
630 "Could not find `requires` target of `{}`{}: {}",
631 item.rust_identifier(),
632 match (item.file(), item.line()) {
633 (Some(file), Some(line)) => format!(" ({file}:{line})"),
634 _ => "".to_string(),
635 },
636 match requires {
637 PositioningRef::FullPath(path) => path.to_string(),
638 PositioningRef::Name(name) => format!(r#""{name}""#),
639 },
640 ));
641 }
642 }
643 }
644 Ok(())
645}
646
647fn initialize_schemas(
648 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
649 bootstrap: Option<NodeIndex>,
650 finalize: Option<NodeIndex>,
651 schemas: Vec<SchemaEntity>,
652) -> eyre::Result<HashMap<SchemaEntity, NodeIndex>> {
653 let mut mapped_schemas = HashMap::default();
654 for item in schemas {
655 let entity = item.clone().into();
656 let index = graph.add_node(entity);
657 mapped_schemas.insert(item, index);
658 if let Some(bootstrap) = bootstrap {
659 graph.add_edge(bootstrap, index, SqlGraphRequires::By);
660 }
661 if let Some(finalize) = finalize {
662 graph.add_edge(index, finalize, SqlGraphRequires::By);
663 }
664 }
665 Ok(mapped_schemas)
666}
667
668fn connect_schemas(
669 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
670 schemas: &HashMap<SchemaEntity, NodeIndex>,
671 root: NodeIndex,
672) {
673 for index in schemas.values().copied() {
674 graph.add_edge(root, index, SqlGraphRequires::By);
675 }
676}
677
678fn initialize_enums(
679 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
680 root: NodeIndex,
681 bootstrap: Option<NodeIndex>,
682 finalize: Option<NodeIndex>,
683 enums: Vec<PostgresEnumEntity>,
684) -> eyre::Result<HashMap<PostgresEnumEntity, NodeIndex>> {
685 let mut mapped_enums = HashMap::default();
686 for item in enums {
687 let entity: SqlGraphEntity = item.clone().into();
688 let index = graph.add_node(entity);
689 mapped_enums.insert(item, index);
690 build_base_edges(graph, index, root, bootstrap, finalize);
691 }
692 Ok(mapped_enums)
693}
694
695fn connect_enums(
696 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
697 enums: &HashMap<PostgresEnumEntity, NodeIndex>,
698 schemas: &HashMap<SchemaEntity, NodeIndex>,
699) {
700 for (item, &index) in enums {
701 make_schema_connection(
702 graph,
703 "Enum",
704 index,
705 &item.rust_identifier(),
706 item.module_path,
707 schemas,
708 );
709 }
710}
711
712fn initialize_types(
713 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
714 root: NodeIndex,
715 bootstrap: Option<NodeIndex>,
716 finalize: Option<NodeIndex>,
717 types: Vec<PostgresTypeEntity>,
718) -> eyre::Result<HashMap<PostgresTypeEntity, NodeIndex>> {
719 let mut mapped_types = HashMap::default();
720 for item in types {
721 let entity = item.clone().into();
722 let index = graph.add_node(entity);
723 mapped_types.insert(item, index);
724 build_base_edges(graph, index, root, bootstrap, finalize);
725 }
726 Ok(mapped_types)
727}
728
729fn connect_types(
730 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
731 types: &HashMap<PostgresTypeEntity, NodeIndex>,
732 schemas: &HashMap<SchemaEntity, NodeIndex>,
733) {
734 for (item, &index) in types {
735 make_schema_connection(
736 graph,
737 "Type",
738 index,
739 &item.rust_identifier(),
740 item.module_path,
741 schemas,
742 );
743 }
744}
745
746fn initialize_externs(
747 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
748 root: NodeIndex,
749 bootstrap: Option<NodeIndex>,
750 finalize: Option<NodeIndex>,
751 externs: Vec<PgExternEntity>,
752 mapped_types: &HashMap<PostgresTypeEntity, NodeIndex>,
753 mapped_enums: &HashMap<PostgresEnumEntity, NodeIndex>,
754) -> eyre::Result<(HashMap<PgExternEntity, NodeIndex>, HashMap<String, NodeIndex>)> {
755 let mut mapped_externs = HashMap::default();
756 let mut mapped_builtin_types = HashMap::default();
757 for item in externs {
758 let entity: SqlGraphEntity = item.clone().into();
759 let index = graph.add_node(entity.clone());
760 mapped_externs.insert(item.clone(), index);
761 build_base_edges(graph, index, root, bootstrap, finalize);
762
763 for arg in &item.fn_args {
764 let found = mapped_types.keys().any(|ty_item| ty_item.id_matches(&arg.used_ty.ty_id))
765 || mapped_enums.keys().any(|ty_item| ty_item.id_matches(&arg.used_ty.ty_id));
766
767 if !found {
768 mapped_builtin_types.entry(arg.used_ty.full_path.to_string()).or_insert_with(
769 || {
770 graph.add_node(SqlGraphEntity::BuiltinType(
771 arg.used_ty.full_path.to_string(),
772 ))
773 },
774 );
775 }
776 }
777
778 match &item.fn_return {
779 PgExternReturnEntity::None | PgExternReturnEntity::Trigger => (),
780 PgExternReturnEntity::Type { ty, .. } | PgExternReturnEntity::SetOf { ty, .. } => {
781 let found = mapped_types.keys().any(|ty_item| ty_item.id_matches(&ty.ty_id))
782 || mapped_enums.keys().any(|ty_item| ty_item.id_matches(&ty.ty_id));
783
784 if !found {
785 mapped_builtin_types.entry(ty.full_path.to_string()).or_insert_with(|| {
786 graph.add_node(SqlGraphEntity::BuiltinType(ty.full_path.to_string()))
787 });
788 }
789 }
790 PgExternReturnEntity::Iterated { tys: iterated_returns, .. } => {
791 for PgExternReturnEntityIteratedItem { ty, .. } in iterated_returns {
792 let found = mapped_types.keys().any(|ty_item| ty_item.id_matches(&ty.ty_id))
793 || mapped_enums.keys().any(|ty_item| ty_item.id_matches(&ty.ty_id));
794
795 if !found {
796 mapped_builtin_types.entry(ty.ty_source.to_string()).or_insert_with(|| {
797 graph.add_node(SqlGraphEntity::BuiltinType(ty.ty_source.to_string()))
798 });
799 }
800 }
801 }
802 }
803 }
804 Ok((mapped_externs, mapped_builtin_types))
805}
806
807fn connect_externs(
808 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
809 externs: &HashMap<PgExternEntity, NodeIndex>,
810 hashes: &HashMap<PostgresHashEntity, NodeIndex>,
811 schemas: &HashMap<SchemaEntity, NodeIndex>,
812 types: &HashMap<PostgresTypeEntity, NodeIndex>,
813 enums: &HashMap<PostgresEnumEntity, NodeIndex>,
814 builtin_types: &HashMap<String, NodeIndex>,
815 extension_sqls: &HashMap<ExtensionSqlEntity, NodeIndex>,
816 triggers: &HashMap<PgTriggerEntity, NodeIndex>,
817) -> eyre::Result<()> {
818 for (item, &index) in externs {
819 let mut found_schema_declaration = false;
820 let mut has_explicit_requires = false;
821 for extern_attr in &item.extern_attrs {
822 match extern_attr {
823 crate::ExternArgs::Requires(requirements) => {
824 for requires in requirements {
825 if let Some(target) = find_positioning_ref_target(
826 requires,
827 types,
828 enums,
829 externs,
830 schemas,
831 extension_sqls,
832 triggers,
833 ) {
834 graph.add_edge(*target, index, SqlGraphRequires::By);
835 has_explicit_requires = true;
836 } else {
837 return Err(eyre!("Could not find `requires` target: {:?}", requires));
838 }
839 }
840 }
841 crate::ExternArgs::Support(support_fn) => {
842 if let Some(target) = find_positioning_ref_target(
843 support_fn,
844 types,
845 enums,
846 externs,
847 schemas,
848 extension_sqls,
849 triggers,
850 ) {
851 graph.add_edge(*target, index, SqlGraphRequires::By);
852 has_explicit_requires = true
853 }
854 }
855 crate::ExternArgs::Schema(declared_schema_name) => {
856 for (schema, schema_index) in schemas {
857 if schema.name == declared_schema_name {
858 graph.add_edge(*schema_index, index, SqlGraphRequires::By);
859 found_schema_declaration = true;
860 }
861 }
862 if !found_schema_declaration {
863 return Err(eyre!("Got manual `schema = \"{declared_schema_name}\"` setting, but that schema did not exist."));
864 }
865 }
866 _ => (),
867 }
868 }
869
870 if !found_schema_declaration {
871 make_schema_connection(
872 graph,
873 "Extern",
874 index,
875 &item.rust_identifier(),
876 item.module_path,
877 schemas,
878 );
879 }
880
881 for (hash_item, &hash_index) in hashes {
883 if item.module_path == hash_item.module_path
884 && item.name == hash_item.name.to_lowercase() + "_eq"
885 {
886 graph.add_edge(index, hash_index, SqlGraphRequires::By);
887 }
888 }
889
890 for arg in &item.fn_args {
891 let found = types
892 .iter()
893 .map(type_keyed)
894 .chain(enums.iter().map(type_keyed))
895 .find(|(item, _)| item.id_matches(&arg.used_ty.ty_id));
896 if let Some((_, ty_index)) = found {
897 graph.add_edge(*ty_index, index, SqlGraphRequires::ByArg);
898 } else {
899 let builtin_index = builtin_types.get(arg.used_ty.full_path).unwrap_or_else(|| {
900 panic!("Could not fetch Builtin Type {}.", arg.used_ty.full_path)
901 });
902 graph.add_edge(*builtin_index, index, SqlGraphRequires::ByArg);
903 }
904 for (ext_item, ext_index) in extension_sqls {
905 if ext_item
906 .has_sql_declared_entity(&SqlDeclared::Type(arg.used_ty.full_path.to_string()))
907 .is_some()
908 {
909 if !has_explicit_requires {
910 graph.add_edge(*ext_index, index, SqlGraphRequires::ByArg);
911 }
912 } else if ext_item
913 .has_sql_declared_entity(&SqlDeclared::Enum(arg.used_ty.full_path.to_string()))
914 .is_some()
915 {
916 graph.add_edge(*ext_index, index, SqlGraphRequires::ByArg);
917 }
918 }
919 }
920
921 match &item.fn_return {
922 PgExternReturnEntity::None | PgExternReturnEntity::Trigger => (),
923 PgExternReturnEntity::Type { ty, .. } | PgExternReturnEntity::SetOf { ty, .. } => {
924 let found_index =
925 types.iter().map(type_keyed).chain(enums.iter().map(type_keyed)).find_map(
926 |(ty_item, index)| ty_item.id_matches(&ty.ty_id).then_some(index),
927 );
928 if let Some(ty_index) = found_index {
929 graph.add_edge(*ty_index, index, SqlGraphRequires::ByReturn);
930 } else {
931 let builtin_index =
932 builtin_types.get(&ty.full_path.to_string()).unwrap_or_else(|| {
933 panic!("Could not fetch Builtin Type {}.", ty.full_path)
934 });
935 graph.add_edge(*builtin_index, index, SqlGraphRequires::ByReturn);
936 for (ext_item, ext_index) in extension_sqls {
937 if ext_item
938 .has_sql_declared_entity(&SqlDeclared::Type(ty.full_path.into()))
939 .is_some()
940 || ext_item
941 .has_sql_declared_entity(&SqlDeclared::Enum(ty.full_path.into()))
942 .is_some()
943 {
944 graph.add_edge(*ext_index, index, SqlGraphRequires::ByArg);
945 }
946 }
947 }
948 }
949 PgExternReturnEntity::Iterated { tys: iterated_returns, .. } => {
950 for PgExternReturnEntityIteratedItem { ty, .. } in iterated_returns {
951 let found_index =
952 types.iter().map(type_keyed).chain(enums.iter().map(type_keyed)).find_map(
953 |(ty_item, index)| ty_item.id_matches(&ty.ty_id).then_some(index),
954 );
955 if let Some(ty_index) = found_index {
956 graph.add_edge(*ty_index, index, SqlGraphRequires::ByReturn);
957 } else {
958 let builtin_index = builtin_types.get(ty.ty_source).unwrap_or_else(|| {
959 panic!("Could not fetch Builtin Type {}.", ty.ty_source)
960 });
961 graph.add_edge(*builtin_index, index, SqlGraphRequires::ByReturn);
962 for (ext_item, ext_index) in extension_sqls {
963 if ext_item
964 .has_sql_declared_entity(&SqlDeclared::Type(
965 ty.ty_source.to_string(),
966 ))
967 .is_some()
968 || ext_item
969 .has_sql_declared_entity(&SqlDeclared::Enum(
970 ty.ty_source.to_string(),
971 ))
972 .is_some()
973 {
974 graph.add_edge(*ext_index, index, SqlGraphRequires::ByArg);
975 }
976 }
977 }
978 }
979 }
980 }
981 }
982 Ok(())
983}
984
985fn initialize_ords(
986 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
987 root: NodeIndex,
988 bootstrap: Option<NodeIndex>,
989 finalize: Option<NodeIndex>,
990 ords: Vec<PostgresOrdEntity>,
991) -> eyre::Result<HashMap<PostgresOrdEntity, NodeIndex>> {
992 let mut mapped_ords = HashMap::default();
993 for item in ords {
994 let entity = item.clone().into();
995 let index = graph.add_node(entity);
996 mapped_ords.insert(item.clone(), index);
997 build_base_edges(graph, index, root, bootstrap, finalize);
998 }
999 Ok(mapped_ords)
1000}
1001
1002fn connect_ords(
1003 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1004 ords: &HashMap<PostgresOrdEntity, NodeIndex>,
1005 schemas: &HashMap<SchemaEntity, NodeIndex>,
1006 types: &HashMap<PostgresTypeEntity, NodeIndex>,
1007 enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1008 externs: &HashMap<PgExternEntity, NodeIndex>,
1009) {
1010 for (item, &index) in ords {
1011 make_schema_connection(
1012 graph,
1013 "Ord",
1014 index,
1015 &item.rust_identifier(),
1016 item.module_path,
1017 schemas,
1018 );
1019
1020 make_type_or_enum_connection(
1021 graph,
1022 "Ord",
1023 index,
1024 &item.rust_identifier(),
1025 &item.id,
1026 types,
1027 enums,
1028 );
1029
1030 for (extern_item, &extern_index) in externs {
1037 let fn_matches = |fn_name| {
1038 item.module_path == extern_item.module_path && extern_item.name == fn_name
1039 };
1040 let cmp_fn_matches = fn_matches(item.cmp_fn_name());
1041 let lt_fn_matches = fn_matches(item.lt_fn_name());
1042 let lte_fn_matches = fn_matches(item.le_fn_name());
1043 let eq_fn_matches = fn_matches(item.eq_fn_name());
1044 let gt_fn_matches = fn_matches(item.gt_fn_name());
1045 let gte_fn_matches = fn_matches(item.ge_fn_name());
1046 if cmp_fn_matches
1047 || lt_fn_matches
1048 || lte_fn_matches
1049 || eq_fn_matches
1050 || gt_fn_matches
1051 || gte_fn_matches
1052 {
1053 graph.add_edge(extern_index, index, SqlGraphRequires::By);
1054 }
1055 }
1056 }
1057}
1058
1059fn initialize_hashes(
1060 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1061 root: NodeIndex,
1062 bootstrap: Option<NodeIndex>,
1063 finalize: Option<NodeIndex>,
1064 hashes: Vec<PostgresHashEntity>,
1065) -> eyre::Result<HashMap<PostgresHashEntity, NodeIndex>> {
1066 let mut mapped_hashes = HashMap::default();
1067 for item in hashes {
1068 let entity: SqlGraphEntity = item.clone().into();
1069 let index = graph.add_node(entity);
1070 mapped_hashes.insert(item, index);
1071 build_base_edges(graph, index, root, bootstrap, finalize);
1072 }
1073 Ok(mapped_hashes)
1074}
1075
1076fn connect_hashes(
1077 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1078 hashes: &HashMap<PostgresHashEntity, NodeIndex>,
1079 schemas: &HashMap<SchemaEntity, NodeIndex>,
1080 types: &HashMap<PostgresTypeEntity, NodeIndex>,
1081 enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1082 externs: &HashMap<PgExternEntity, NodeIndex>,
1083) {
1084 for (item, &index) in hashes {
1085 make_schema_connection(
1086 graph,
1087 "Hash",
1088 index,
1089 &item.rust_identifier(),
1090 item.module_path,
1091 schemas,
1092 );
1093
1094 make_type_or_enum_connection(
1095 graph,
1096 "Hash",
1097 index,
1098 &item.rust_identifier(),
1099 &item.id,
1100 types,
1101 enums,
1102 );
1103
1104 if let Some((_, extern_index)) = externs.iter().find(|(extern_item, _)| {
1105 item.module_path == extern_item.module_path && extern_item.name == item.fn_name()
1106 }) {
1107 graph.add_edge(*extern_index, index, SqlGraphRequires::By);
1108 }
1109 }
1110}
1111
1112fn initialize_aggregates(
1113 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1114 root: NodeIndex,
1115 bootstrap: Option<NodeIndex>,
1116 finalize: Option<NodeIndex>,
1117 aggregates: Vec<PgAggregateEntity>,
1118 mapped_builtin_types: &mut HashMap<String, NodeIndex>,
1119 mapped_enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1120 mapped_types: &HashMap<PostgresTypeEntity, NodeIndex>,
1121) -> eyre::Result<HashMap<PgAggregateEntity, NodeIndex>> {
1122 let mut mapped_aggregates = HashMap::default();
1123 for item in aggregates {
1124 let entity: SqlGraphEntity = item.clone().into();
1125 let index = graph.add_node(entity);
1126
1127 for arg in &item.args {
1128 let found = mapped_types
1129 .iter()
1130 .map(type_keyed)
1131 .chain(mapped_enums.iter().map(type_keyed))
1132 .find(|(item, _)| item.id_matches(&arg.used_ty.ty_id));
1133
1134 if found.is_none() {
1135 mapped_builtin_types.entry(arg.used_ty.full_path.to_string()).or_insert_with(
1136 || {
1137 graph.add_node(SqlGraphEntity::BuiltinType(
1138 arg.used_ty.full_path.to_string(),
1139 ))
1140 },
1141 );
1142 }
1143 }
1144
1145 mapped_aggregates.insert(item, index);
1146 build_base_edges(graph, index, root, bootstrap, finalize);
1147 }
1148 Ok(mapped_aggregates)
1149}
1150
1151fn connect_aggregate(
1152 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1153 item: &PgAggregateEntity,
1154 index: NodeIndex,
1155 schemas: &HashMap<SchemaEntity, NodeIndex>,
1156 types: &HashMap<PostgresTypeEntity, NodeIndex>,
1157 enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1158 builtin_types: &HashMap<String, NodeIndex>,
1159 externs: &HashMap<PgExternEntity, NodeIndex>,
1160) -> eyre::Result<()> {
1161 make_schema_connection(
1162 graph,
1163 "Aggregate",
1164 index,
1165 &item.rust_identifier(),
1166 item.module_path,
1167 schemas,
1168 );
1169
1170 make_type_or_enum_connection(
1171 graph,
1172 "Aggregate",
1173 index,
1174 &item.rust_identifier(),
1175 &item.ty_id,
1176 types,
1177 enums,
1178 );
1179
1180 for arg in &item.args {
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 for arg in item.direct_args.as_ref().unwrap_or(&vec![]) {
1199 let found = make_type_or_enum_connection(
1200 graph,
1201 "Aggregate",
1202 index,
1203 &item.rust_identifier(),
1204 &arg.used_ty.ty_id,
1205 types,
1206 enums,
1207 );
1208 if !found {
1209 let builtin_index = builtin_types.get(arg.used_ty.full_path).unwrap_or_else(|| {
1210 panic!("Could not fetch Builtin Type {}.", arg.used_ty.full_path)
1211 });
1212 graph.add_edge(*builtin_index, index, SqlGraphRequires::ByArg);
1213 }
1214 }
1215
1216 if let Some(arg) = &item.mstype {
1217 let found = make_type_or_enum_connection(
1218 graph,
1219 "Aggregate",
1220 index,
1221 &item.rust_identifier(),
1222 &arg.ty_id,
1223 types,
1224 enums,
1225 );
1226 if !found {
1227 let builtin_index = builtin_types
1228 .get(arg.full_path)
1229 .unwrap_or_else(|| panic!("Could not fetch Builtin Type {}.", arg.full_path));
1230 graph.add_edge(*builtin_index, index, SqlGraphRequires::ByArg);
1231 }
1232 }
1233
1234 make_extern_connection(
1235 graph,
1236 "Aggregate",
1237 index,
1238 &item.rust_identifier(),
1239 &(item.module_path.to_string() + "::" + item.sfunc),
1240 externs,
1241 )?;
1242
1243 if let Some(value) = item.finalfunc {
1244 make_extern_connection(
1245 graph,
1246 "Aggregate",
1247 index,
1248 &item.rust_identifier(),
1249 &(item.module_path.to_string() + "::" + value),
1250 externs,
1251 )?;
1252 }
1253 if let Some(value) = item.combinefunc {
1254 make_extern_connection(
1255 graph,
1256 "Aggregate",
1257 index,
1258 &item.rust_identifier(),
1259 &(item.module_path.to_string() + "::" + value),
1260 externs,
1261 )?;
1262 }
1263 if let Some(value) = item.serialfunc {
1264 make_extern_connection(
1265 graph,
1266 "Aggregate",
1267 index,
1268 &item.rust_identifier(),
1269 &(item.module_path.to_string() + "::" + value),
1270 externs,
1271 )?;
1272 }
1273 if let Some(value) = item.deserialfunc {
1274 make_extern_connection(
1275 graph,
1276 "Aggregate",
1277 index,
1278 &item.rust_identifier(),
1279 &(item.module_path.to_string() + "::" + value),
1280 externs,
1281 )?;
1282 }
1283 if let Some(value) = item.msfunc {
1284 make_extern_connection(
1285 graph,
1286 "Aggregate",
1287 index,
1288 &item.rust_identifier(),
1289 &(item.module_path.to_string() + "::" + value),
1290 externs,
1291 )?;
1292 }
1293 if let Some(value) = item.minvfunc {
1294 make_extern_connection(
1295 graph,
1296 "Aggregate",
1297 index,
1298 &item.rust_identifier(),
1299 &(item.module_path.to_string() + "::" + value),
1300 externs,
1301 )?;
1302 }
1303 if let Some(value) = item.mfinalfunc {
1304 make_extern_connection(
1305 graph,
1306 "Aggregate",
1307 index,
1308 &item.rust_identifier(),
1309 &(item.module_path.to_string() + "::" + value),
1310 externs,
1311 )?;
1312 }
1313 if let Some(value) = item.sortop {
1314 make_extern_connection(
1315 graph,
1316 "Aggregate",
1317 index,
1318 &item.rust_identifier(),
1319 &(item.module_path.to_string() + "::" + value),
1320 externs,
1321 )?;
1322 }
1323 Ok(())
1324}
1325
1326fn connect_aggregates(
1327 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1328 aggregates: &HashMap<PgAggregateEntity, NodeIndex>,
1329 schemas: &HashMap<SchemaEntity, NodeIndex>,
1330 types: &HashMap<PostgresTypeEntity, NodeIndex>,
1331 enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1332 builtin_types: &HashMap<String, NodeIndex>,
1333 externs: &HashMap<PgExternEntity, NodeIndex>,
1334) -> eyre::Result<()> {
1335 for (item, &index) in aggregates {
1336 connect_aggregate(graph, item, index, schemas, types, enums, builtin_types, externs)?
1337 }
1338 Ok(())
1339}
1340
1341fn initialize_triggers(
1342 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1343 root: NodeIndex,
1344 bootstrap: Option<NodeIndex>,
1345 finalize: Option<NodeIndex>,
1346 triggers: Vec<PgTriggerEntity>,
1347) -> eyre::Result<HashMap<PgTriggerEntity, NodeIndex>> {
1348 let mut mapped_triggers = HashMap::default();
1349 for item in triggers {
1350 let entity: SqlGraphEntity = item.clone().into();
1351 let index = graph.add_node(entity);
1352
1353 mapped_triggers.insert(item, index);
1354 build_base_edges(graph, index, root, bootstrap, finalize);
1355 }
1356 Ok(mapped_triggers)
1357}
1358
1359fn connect_triggers(
1360 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1361 triggers: &HashMap<PgTriggerEntity, NodeIndex>,
1362 schemas: &HashMap<SchemaEntity, NodeIndex>,
1363) {
1364 for (item, &index) in triggers {
1365 make_schema_connection(
1366 graph,
1367 "Trigger",
1368 index,
1369 &item.rust_identifier(),
1370 item.module_path,
1371 schemas,
1372 );
1373 }
1374}
1375
1376fn make_schema_connection(
1377 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1378 _kind: &str,
1379 index: NodeIndex,
1380 _rust_identifier: &str,
1381 module_path: &str,
1382 schemas: &HashMap<SchemaEntity, NodeIndex>,
1383) -> bool {
1384 let mut found = false;
1385 for (schema_item, &schema_index) in schemas {
1386 if module_path == schema_item.module_path {
1387 graph.add_edge(schema_index, index, SqlGraphRequires::By);
1388 found = true;
1389 break;
1390 }
1391 }
1392 found
1393}
1394
1395fn make_extern_connection(
1396 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1397 _kind: &str,
1398 index: NodeIndex,
1399 _rust_identifier: &str,
1400 full_path: &str,
1401 externs: &HashMap<PgExternEntity, NodeIndex>,
1402) -> eyre::Result<()> {
1403 match externs.iter().find(|(extern_item, _)| full_path == extern_item.full_path) {
1404 Some((_, extern_index)) => {
1405 graph.add_edge(*extern_index, index, SqlGraphRequires::By);
1406 Ok(())
1407 }
1408 None => Err(eyre!("Did not find connection `{full_path}` in {:#?}", {
1409 let mut paths = externs.keys().map(|v| v.full_path).collect::<Vec<_>>();
1410 paths.sort();
1411 paths
1412 })),
1413 }
1414}
1415
1416fn make_type_or_enum_connection(
1417 graph: &mut StableGraph<SqlGraphEntity, SqlGraphRequires>,
1418 _kind: &str,
1419 index: NodeIndex,
1420 _rust_identifier: &str,
1421 ty_id: &TypeId,
1422 types: &HashMap<PostgresTypeEntity, NodeIndex>,
1423 enums: &HashMap<PostgresEnumEntity, NodeIndex>,
1424) -> bool {
1425 types
1426 .iter()
1427 .map(type_keyed)
1428 .chain(enums.iter().map(type_keyed))
1429 .find(|(ty, _)| ty.id_matches(ty_id))
1430 .map(|(_, ty_index)| graph.add_edge(*ty_index, index, SqlGraphRequires::By))
1431 .is_some()
1432}