Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 48 additions & 9 deletions src/handlers/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -654,15 +654,21 @@ pub async fn get_routines(id: Value, params: &Value) -> Value {
.and_then(Value::as_str)
.unwrap_or("public");

// PG 11+ uses prokind; older versions use proisagg/proiswindow flags.
// CI runs PG 16, so we use the modern query.
let query = r#"
SELECT proname, prokind
FROM pg_proc
WHERE pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = $1)
AND prokind IN ('f', 'p')
ORDER BY proname
"#;
let server_version_num: i32 = match client::query_rows(
&conn_params,
"SELECT current_setting('server_version_num')::int4 AS v",
&[],
)
.await
{
Ok(rows) => rows
.first()
.and_then(|r| r.try_get::<_, i32>("v").ok())
.unwrap_or(0),
Err(e) => return error_response(id, -32603, &e),
};

let query = routine_query_for_version(server_version_num);

match client::query_rows(&conn_params, query, &[&schema]).await {
Ok(rows) => {
Expand All @@ -689,6 +695,35 @@ pub async fn get_routines(id: Value, params: &Value) -> Value {
}
}

/// Pick the `get_routines` query by server version. `pg_proc.prokind` was
/// introduced in PostgreSQL 11, replacing the boolean columns `proisagg` /
/// `proiswindow`. On 9.x/10 the column does not exist, so referencing it
/// fails at parse time (SQLSTATE 42703). Matches the builtin driver's
/// `get_routines` version branch exactly (`src-tauri/src/drivers/postgres/mod.rs`).
fn routine_query_for_version(server_version_num: i32) -> &'static str {
if server_version_num >= 110_000 {
r#"
SELECT proname, prokind
FROM pg_proc
WHERE pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = $1)
AND prokind IN ('f', 'p')
ORDER BY proname
"#
} else {
// Pre-11: procedures don't exist; exclude aggregates and window
// functions and report everything else as a plain function ('f').
// Cast to the internal "char" type so it maps to i8 like prokind.
r#"
SELECT proname, 'f'::"char" AS prokind
FROM pg_proc
WHERE pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = $1)
AND NOT proisagg
AND NOT proiswindow
ORDER BY proname
"#
}
}

pub async fn get_routine_parameters(id: Value, params: &Value) -> Value {
let conn_params = ConnectionParams::from_value(inner_params(params));
let routine_name = params
Expand Down Expand Up @@ -923,3 +958,7 @@ pub async fn get_all_columns_batch(id: Value, _params: &Value) -> Value {
pub async fn get_all_foreign_keys_batch(id: Value, _params: &Value) -> Value {
not_implemented(id, "get_all_foreign_keys_batch")
}

#[cfg(test)]
#[path = "metadata_tests.rs"]
mod metadata_tests;
56 changes: 56 additions & 0 deletions src/handlers/metadata_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//! Unit tests for `metadata.rs`'s pure query-selection helper
//! (`routine_query_for_version`). Sibling test file per repo convention
//! (`.rules/rust.md` #4/#5) — loaded via
//! `#[cfg(test)] #[path = "metadata_tests.rs"] mod metadata_tests;`.

use super::routine_query_for_version;

#[test]
fn pg11_and_newer_uses_the_prokind_query() {
for version in [110_000, 110_001, 120_000, 160_000] {
let query = routine_query_for_version(version);
assert!(
query.contains("prokind"),
"version {version} should use the prokind query, got: {query}"
);
assert!(
!query.contains("proisagg"),
"version {version} should not use the legacy proisagg/proiswindow query"
);
}
}

#[test]
fn pre_pg11_falls_back_to_proisagg_proiswindow_query() {
for version in [0, 90_600, 100_000, 100_015, 109_999] {
let query = routine_query_for_version(version);
assert!(
query.contains("proisagg") && query.contains("proiswindow"),
"version {version} should use the legacy proisagg/proiswindow query, got: {query}"
);
assert!(
!query.contains("prokind IN"),
"version {version} must not reference the prokind column, which doesn't exist \
before PostgreSQL 11 (SQLSTATE 42703)"
);
}
}

#[test]
fn boundary_is_exactly_110000_inclusive() {
// 110000 is PostgreSQL 11.0's server_version_num -- the exact version
// that introduced prokind, so it must take the modern branch.
assert!(routine_query_for_version(110_000).contains("prokind IN"));
// One below must take the legacy branch.
assert!(routine_query_for_version(109_999).contains("proisagg"));
}

#[test]
fn every_branch_selects_and_aliases_prokind_as_a_char_column() {
// Both queries must produce a `prokind` column the handler can
// try_get::<i8>() uniformly, regardless of which branch ran.
for version in [90_600, 160_000] {
let query = routine_query_for_version(version);
assert!(query.contains("prokind"), "{query}");
}
}
Loading