From a1d0178a7ef488c93cf80aea94a52f73a1e01245 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Sat, 6 Jun 2026 11:29:16 +0800 Subject: [PATCH] fix(postgres): add OpenGauss-compatible index query fallback without WITH ORDINALITY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenGauss is based on PostgreSQL 9.2 and does not support WITH ORDINALITY (PG 9.4+). When the native pg_get_tabledef() path fails, the fallback DDL builder calls list_indexes() which previously only had two SQL variants, both using WITH ORDINALITY — causing "syntax error at or near 'WITH'" on OpenGauss. Add POSTGRES_INDEXES_OPENGAUSS_SQL as a third fallback that uses generate_series and array_length instead, both available since PG 8.0. --- crates/dbx-core/src/db/postgres.rs | 53 +++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/crates/dbx-core/src/db/postgres.rs b/crates/dbx-core/src/db/postgres.rs index 5023540d3e..b6dabeff93 100644 --- a/crates/dbx-core/src/db/postgres.rs +++ b/crates/dbx-core/src/db/postgres.rs @@ -1541,6 +1541,28 @@ const POSTGRES_INDEXES_COMPAT_SQL: &str = "SELECT i.relname AS index_name, \ GROUP BY i.relname, i.oid, ix.indisunique, ix.indisprimary, ix.indpred, ix.indrelid, am.amname, ix.indkey \ ORDER BY i.relname"; +const POSTGRES_INDEXES_OPENGAUSS_SQL: &str = "SELECT i.relname AS index_name, \ + array_agg(COALESCE(a.attname, pg_get_indexdef(ix.indexrelid, k.n::int, true)) ORDER BY k.n) AS columns, \ + ix.indisunique AS is_unique, \ + ix.indisprimary AS is_primary, \ + pg_get_expr(ix.indpred, ix.indrelid) AS filter_expr, \ + am.amname AS index_type, \ + NULL::smallint AS nkeyatts, \ + ix.indkey AS indkey, \ + obj_description(i.oid, 'pg_class') AS index_comment \ + FROM pg_index ix \ + JOIN pg_class t ON t.oid = ix.indrelid \ + JOIN pg_class i ON i.oid = ix.indexrelid \ + JOIN pg_namespace n ON n.oid = t.relnamespace \ + JOIN pg_am am ON am.oid = i.relam \ + JOIN LATERAL ( \ + SELECT unnest(ix.indkey) AS attnum, generate_series(1, array_length(ix.indkey, 1)) AS n \ + ) AS k ON true \ + LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum AND k.attnum > 0 \ + WHERE n.nspname = $1 AND t.relname = $2 \ + GROUP BY i.relname, i.oid, ix.indisunique, ix.indisprimary, ix.indpred, ix.indrelid, am.amname, ix.indkey \ + ORDER BY i.relname"; + async fn list_indexes_with_sql( client: &deadpool_postgres::Client, sql: &str, @@ -1579,14 +1601,21 @@ pub async fn list_indexes(pool: &Pool, schema: &str, table: &str) -> Result match list_indexes_with_sql(&client, POSTGRES_INDEXES_COMPAT_SQL, schema, table).await { Ok(indexes) => Ok(indexes), Err(fallback_error) => { - let primary_message = pg_error_to_string(primary_error); - let fallback_message = pg_error_to_string(fallback_error); - log::debug!( - "[postgres][list_indexes:compat-failed] primary_error={} fallback_error={}", - primary_message, - fallback_message - ); - Err(fallback_message) + match list_indexes_with_sql(&client, POSTGRES_INDEXES_OPENGAUSS_SQL, schema, table).await { + Ok(indexes) => Ok(indexes), + Err(opengauss_error) => { + let primary_message = pg_error_to_string(primary_error); + let fallback_message = pg_error_to_string(fallback_error); + let opengauss_message = pg_error_to_string(opengauss_error); + log::debug!( + "[postgres][list_indexes:opengauss-failed] primary_error={} fallback_error={} opengauss_error={}", + primary_message, + fallback_message, + opengauss_message + ); + Err(opengauss_message) + } + } } }, } @@ -2028,6 +2057,14 @@ mod tests { assert!(POSTGRES_INDEXES_COMPAT_SQL.contains("NULL::smallint AS nkeyatts")); } + #[test] + fn postgres_index_metadata_has_opengauss_compatible_fallback() { + assert!(!POSTGRES_INDEXES_OPENGAUSS_SQL.contains("WITH ORDINALITY")); + assert!(POSTGRES_INDEXES_OPENGAUSS_SQL.contains("generate_series")); + assert!(POSTGRES_INDEXES_OPENGAUSS_SQL.contains("array_length")); + assert!(POSTGRES_INDEXES_OPENGAUSS_SQL.contains("NULL::smallint AS nkeyatts")); + } + #[test] fn list_objects_sql_includes_routines() { let sql = list_objects_sql(true);