feat(greenplum): expose distribution key + storage type to chat agent

When connected to a Greenplum cluster, schema introspection now surfaces
the two attributes that dominate query performance there: the data
distribution policy (DISTRIBUTED BY / RANDOMLY) and the storage kind
(heap / AO row / AO column). Without these, the agent writes
PG-optimal SQL that triggers Redistribute Motion in GP and runs orders
of magnitude slower than necessary.

- track Greenplum major version (6 vs 7) at connect time, since GP6
  uses pg_class.relstorage and GP7 dropped it in favor of pg_am
- new fetch_gp_table_extras helper queries gp_distribution_policy and
  the version-appropriate storage catalog, returns a per-table map
- format_table_block prints `  -- GP: DISTRIBUTED BY (...) | STORAGE: ...`
  under the TABLE header when extras are available
- build_overview_postgres appends a GREENPLUM NOTES block with the
  distribution-aware-join rules and a skew-detection one-liner; the
  agent sees this in every system prompt on a GP connection
- build_schema_context (legacy generate_sql / explain_sql / fix_sql_error
  path) and the chat get_columns tool both feed extras into format_table_block

P0 of the GP support arc — partitioning, EXPLAIN motion-aware parsing,
external tables, resource queues, and a skew-check tool are deliberately
deferred to follow-up commits.
This commit is contained in:
2026-05-06 23:34:44 +03:00
parent 223a09c636
commit 5c5d256cee
4 changed files with 205 additions and 2 deletions

View File

@@ -6,7 +6,8 @@
use crate::commands::ai::{
fetch_column_comments, fetch_columns, fetch_enum_types, fetch_foreign_keys_raw,
fetch_table_comments, fetch_unique_constraints, format_table_block, ColumnInfo,
fetch_gp_table_extras, fetch_table_comments, fetch_unique_constraints, format_table_block,
ColumnInfo,
};
use crate::commands::connections::{load_connection_config, switch_database_core};
use crate::commands::saved_queries::{list_saved_queries_core, save_query_core};
@@ -219,6 +220,8 @@ async fn get_columns_postgres(
requested: &[(String, String, String)],
) -> TuskResult<String> {
let pool = state.get_pool(connection_id).await?;
let is_greenplum = matches!(state.get_flavor(connection_id).await, DbFlavor::Greenplum);
let gp_major = state.get_gp_major(connection_id).await.unwrap_or(7);
let (col_res, fk_res, enum_res, tbl_comm_res, col_comm_res, unique_res) = tokio::join!(
fetch_columns(&pool),
@@ -234,6 +237,11 @@ async fn get_columns_postgres(
let tbl_comments = tbl_comm_res.unwrap_or_default();
let col_comments = col_comm_res.unwrap_or_default();
let uniques = unique_res.unwrap_or_default();
let gp_extras = if is_greenplum {
Some(fetch_gp_table_extras(&pool, gp_major).await)
} else {
None
};
// Build (schema, table) → Vec<ColumnInfo>
let mut by_table: BTreeMap<(String, String), Vec<ColumnInfo>> = BTreeMap::new();
@@ -282,6 +290,7 @@ async fn get_columns_postgres(
&unique_map,
&varchar_values,
&jsonb_keys,
gp_extras.as_ref(),
&mut output,
);
}