Skip to main content

dbrest_core/schema_cache/queries/
computed_fields.rs

1//! Computed fields introspection query
2//!
3//! Query for detecting functions that can be used as computed fields.
4//! These are functions that take a table row type as the first parameter
5//! and return a scalar value (not a composite type).
6
7/// SQL query to find computed field functions
8///
9/// Finds functions that:
10/// - Take exactly 1 parameter (a table row type)
11/// - Return a scalar value (not a composite type)
12/// - Are in exposed schemas or db-extra-search-path
13///
14/// Returns:
15/// - table_schema, table_name: The table the function operates on
16/// - function_schema, function_name: The function that computes the field
17/// - return_type: The PostgreSQL type returned by the function
18/// - returns_set: Whether the function returns SETOF
19pub const COMPUTED_FIELDS_QUERY: &str = r#"
20SELECT
21    n.nspname AS table_schema,
22    t.relname AS table_name,
23    pn.nspname AS function_schema,
24    p.proname AS function_name,
25    format_type(p.prorettype, NULL) AS return_type,
26    p.proretset AS returns_set
27FROM pg_proc p
28JOIN pg_namespace pn ON p.pronamespace = pn.oid
29JOIN pg_type pt ON p.proargtypes[0] = pt.oid
30JOIN pg_class t ON pt.typrelid = t.oid
31JOIN pg_namespace n ON t.relnamespace = n.oid
32JOIN pg_type rt ON p.prorettype = rt.oid
33WHERE array_length(p.proargtypes, 1) = 1
34    AND t.relkind IN ('r', 'v', 'm', 'f', 'p')
35    AND rt.typtype != 'c'  -- Not a composite type (scalar return)
36    AND pn.nspname = ANY($1::text[])  -- Exposed schemas + extra-search-path
37ORDER BY n.nspname, t.relname, p.proname
38"#;
39
40// Note: ComputedFieldRow is defined in schema_cache::db module
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45
46    #[test]
47    fn test_computed_fields_query_not_empty() {
48        assert!(!COMPUTED_FIELDS_QUERY.is_empty());
49        assert!(COMPUTED_FIELDS_QUERY.contains("SELECT"));
50        assert!(COMPUTED_FIELDS_QUERY.contains("pg_proc"));
51    }
52
53    #[test]
54    fn test_computed_fields_query_has_required_columns() {
55        assert!(COMPUTED_FIELDS_QUERY.contains("table_schema"));
56        assert!(COMPUTED_FIELDS_QUERY.contains("table_name"));
57        assert!(COMPUTED_FIELDS_QUERY.contains("function_schema"));
58        assert!(COMPUTED_FIELDS_QUERY.contains("function_name"));
59        assert!(COMPUTED_FIELDS_QUERY.contains("return_type"));
60        assert!(COMPUTED_FIELDS_QUERY.contains("returns_set"));
61    }
62
63    #[test]
64    fn test_computed_fields_query_filters_scalar_returns() {
65        assert!(COMPUTED_FIELDS_QUERY.contains("rt.typtype != 'c'"));
66    }
67
68    #[test]
69    fn test_computed_fields_query_filters_single_param() {
70        assert!(COMPUTED_FIELDS_QUERY.contains("array_length(p.proargtypes, 1) = 1"));
71    }
72
73    #[test]
74    fn test_computed_fields_query_filters_schemas() {
75        assert!(COMPUTED_FIELDS_QUERY.contains("pn.nspname = ANY($1::text[])"));
76    }
77}