Skip to main content

uqa_sql/catalog/resolution/
candidates.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Legacy relation candidates with lazy session input reads and exact search-path ordering.
8#[cfg(test)]
9mod tests;
10use std::ops::Deref;
11use uqa_core::RelationIdentity;
12pub type SearchPathRead<'a> = Box<dyn Deref<Target = Vec<String>> + 'a>;
13pub trait RelationCandidateState {
14    fn temporary_schema_name(&self) -> String;
15    fn search_path(&self) -> SearchPathRead<'_>;
16}
17pub fn relation_lookup_candidates(
18    state: &dyn RelationCandidateState,
19    name: &str,
20) -> Result<Vec<RelationIdentity>, String> {
21    let (schema, relation) = RelationIdentity::parse_reference(name)?;
22    if let Some(schema) = schema {
23        if schema == "pg_temp" {
24            return Ok(vec![RelationIdentity::new(
25                state.temporary_schema_name(),
26                relation,
27            )]);
28        }
29        return Ok(vec![RelationIdentity::new(schema, relation)]);
30    }
31    let temporary = state.temporary_schema_name();
32    let path = state.search_path();
33    Ok(unqualified_candidates(&temporary, &path, &relation))
34}
35
36pub(super) fn unqualified_candidates(
37    temporary: &str,
38    path: &[String],
39    relation: &str,
40) -> Vec<RelationIdentity> {
41    let mut candidates = Vec::new();
42    if !path
43        .iter()
44        .any(|schema| schema == "pg_temp" || schema == temporary)
45    {
46        candidates.push(RelationIdentity::new(temporary, relation));
47    }
48    if !path.iter().any(|schema| schema == "pg_catalog") {
49        candidates.push(RelationIdentity::new("pg_catalog", relation));
50    }
51    candidates.extend(path.iter().map(|schema| {
52        RelationIdentity::new(
53            if schema == "pg_temp" {
54                temporary
55            } else {
56                schema
57            },
58            relation,
59        )
60    }));
61    candidates
62}