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