feat(uxdb): add UXDB database support

This commit is contained in:
t8y2 committed 2026-07-24 21:15:06 +08:00
1 parent 3035800658
commit 7ee335e38a
65 files changed
+697 -96

No files matched your search

@@ -136,7 +136,9 @@ public abstract class PostgresLikeAgent extends AbstractJdbcAgent {
public List<DatabaseInfo> listDatabases() {
return unchecked(() -> {
List<DatabaseInfo> result = new ArrayList<>();
try (java.sql.PreparedStatement stmt = requireConnection().prepareStatement("SELECT datname FROM pg_catalog.pg_database WHERE datistemplate = false ORDER BY datname");
String sql = "SELECT datname FROM " + profile.catalogRelation("database") +
" WHERE datistemplate = false ORDER BY datname";
try (java.sql.PreparedStatement stmt = requireConnection().prepareStatement(sql);
ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
result.add(new DatabaseInfo(rs.getString("datname")));
@@ -152,10 +154,10 @@ public abstract class PostgresLikeAgent extends AbstractJdbcAgent {
List<String> result = new ArrayList<>();
try (java.sql.PreparedStatement stmt = requireConnection().prepareStatement(
"SELECT n.nspname AS schema_name " +
"FROM pg_catalog.pg_namespace n " +
"WHERE n.nspname NOT IN ('pg_catalog','information_schema','pg_toast') " +
"AND n.nspname NOT LIKE 'pg_toast_temp_%' " +
"AND n.nspname NOT LIKE 'pg_temp_%' " +
"FROM " + profile.catalogRelation("namespace") + " n " +
"WHERE n.nspname NOT IN ('" + profile.getCatalogSchema() + "','information_schema','" + profile.getToastSchema() + "') " +
"AND n.nspname NOT LIKE '" + profile.getToastTemporarySchemaPrefix() + "%' " +
"AND n.nspname NOT LIKE '" + profile.getTemporarySchemaPrefix() + "%' " +
"ORDER BY n.nspname"
); ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
@@ -179,9 +181,9 @@ public abstract class PostgresLikeAgent extends AbstractJdbcAgent {
"WHEN 'm' THEN 'MATERIALIZED VIEW' " +
"WHEN 'f' THEN 'FOREIGN TABLE' " +
"ELSE 'TABLE' END AS table_type, " +
"obj_description(c.oid) AS table_comment " +
"FROM pg_catalog.pg_class c " +
"JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace " +
profile.catalogBuiltinFunction("obj_description") + "(c.oid) AS table_comment " +
"FROM " + profile.catalogRelation("class") + " c " +
"JOIN " + profile.catalogRelation("namespace") + " n ON n.oid = c.relnamespace " +
"WHERE n.nspname = ? AND c.relkind IN ('r','p','v','m','f') " +
"ORDER BY c.relname"
)) {
@@ -207,8 +209,8 @@ public abstract class PostgresLikeAgent extends AbstractJdbcAgent {
try (java.sql.PreparedStatement stmt = requireConnection().prepareStatement(
"SELECT p.proname AS routine_name, " +
"CASE p.prokind WHEN 'p' THEN 'PROCEDURE' ELSE 'FUNCTION' END AS routine_type " +
"FROM pg_catalog.pg_proc p " +
"JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace " +
"FROM " + profile.catalogRelation("proc") + " p " +
"JOIN " + profile.catalogRelation("namespace") + " n ON n.oid = p.pronamespace " +
"WHERE n.nspname = ? AND p.prokind IN ('p','f') " +
"ORDER BY p.proname"
)) {
@@ -221,8 +223,8 @@ public abstract class PostgresLikeAgent extends AbstractJdbcAgent {
}
try (java.sql.PreparedStatement stmt = requireConnection().prepareStatement(
"SELECT c.relname AS sequence_name, 'SEQUENCE' AS object_type " +
"FROM pg_catalog.pg_class c " +
"JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace " +
"FROM " + profile.catalogRelation("class") + " c " +
"JOIN " + profile.catalogRelation("namespace") + " n ON n.oid = c.relnamespace " +
"WHERE n.nspname = ? AND c.relkind = 'S' " +
"ORDER BY c.relname"
)) {
@@ -286,9 +288,9 @@ public abstract class PostgresLikeAgent extends AbstractJdbcAgent {
public String getTableComment(String schema, String table) {
return unchecked(() -> {
try (java.sql.PreparedStatement stmt = requireConnection().prepareStatement(
"SELECT obj_description(c.oid) AS table_comment " +
"FROM pg_catalog.pg_class c " +
"JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace " +
"SELECT " + profile.catalogBuiltinFunction("obj_description") + "(c.oid) AS table_comment " +
"FROM " + profile.catalogRelation("class") + " c " +
"JOIN " + profile.catalogRelation("namespace") + " n ON n.oid = c.relnamespace " +
"WHERE n.nspname = ? AND c.relname = ? AND c.relkind IN ('r','m','f','p') " +
"LIMIT 1"
)) {
@@ -308,10 +310,11 @@ public abstract class PostgresLikeAgent extends AbstractJdbcAgent {
protected List<CheckConstraintInfo> listCheckConstraints(String schema, String table) {
return unchecked(() -> {
List<CheckConstraintInfo> result = new ArrayList<>();
String sql = "SELECT co.conname AS constraint_name, pg_catalog.pg_get_constraintdef(co.oid, true) AS constraint_definition " +
"FROM pg_catalog.pg_constraint co " +
"JOIN pg_catalog.pg_class c ON c.oid = co.conrelid " +
"JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace " +
String sql = "SELECT co.conname AS constraint_name, " +
profile.catalogPrefixedFunction("get_constraintdef") + "(co.oid, true) AS constraint_definition " +
"FROM " + profile.catalogRelation("constraint") + " co " +
"JOIN " + profile.catalogRelation("class") + " c ON c.oid = co.conrelid " +
"JOIN " + profile.catalogRelation("namespace") + " n ON n.oid = c.relnamespace " +
"WHERE co.contype = 'c' AND n.nspname = ? AND c.relname = ? " +
"ORDER BY co.conname";
try (java.sql.PreparedStatement stmt = requireConnection().prepareStatement(sql)) {
@@ -336,15 +339,18 @@ public abstract class PostgresLikeAgent extends AbstractJdbcAgent {
String upperType = objectType.toUpperCase();
String sql;
if ("VIEW".equals(upperType) || "MATERIALIZED VIEW".equals(upperType)) {
sql = "SELECT pg_catalog.pg_get_viewdef(pg_catalog.to_regclass(?), true)";
sql = "SELECT " + profile.catalogPrefixedFunction("get_viewdef") + "(" +
profile.catalogBuiltinFunction("to_regclass") + "(?), true)";
} else if ("FUNCTION".equals(upperType)) {
sql = "SELECT pg_catalog.pg_get_functiondef(p.oid)\n" +
"FROM pg_catalog.pg_proc p JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n" +
sql = "SELECT " + profile.catalogPrefixedFunction("get_functiondef") + "(p.oid)\n" +
"FROM " + profile.catalogRelation("proc") + " p JOIN " +
profile.catalogRelation("namespace") + " n ON n.oid = p.pronamespace\n" +
"WHERE n.nspname = ? AND p.proname = ? AND p.prokind = 'f'\n" +
"ORDER BY p.oid LIMIT 1";
} else if ("PROCEDURE".equals(upperType)) {
sql = "SELECT pg_catalog.pg_get_functiondef(p.oid)\n" +
"FROM pg_catalog.pg_proc p JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n" +
sql = "SELECT " + profile.catalogPrefixedFunction("get_functiondef") + "(p.oid)\n" +
"FROM " + profile.catalogRelation("proc") + " p JOIN " +
profile.catalogRelation("namespace") + " n ON n.oid = p.pronamespace\n" +
"WHERE n.nspname = ? AND p.proname = ? AND p.prokind = 'p'\n" +
"ORDER BY p.oid LIMIT 1";
} else {
@@ -378,21 +384,21 @@ public abstract class PostgresLikeAgent extends AbstractJdbcAgent {
Set<String> primaryKeys = primaryKeys(schema, table);
List<ColumnInfo> result = new ArrayList<>();
String sql = "SELECT a.attname AS column_name, " +
"format_type(a.atttypid, a.atttypmod) AS data_type, " +
profile.catalogBuiltinFunction("format_type") + "(a.atttypid, a.atttypmod) AS data_type, " +
"NOT a.attnotnull AS is_nullable, " +
"pg_get_expr(ad.adbin, ad.adrelid) AS column_default, " +
"col_description(a.attrelid, a.attnum) AS column_comment, " +
profile.catalogPrefixedFunction("get_expr") + "(ad.adbin, ad.adrelid) AS column_default, " +
profile.catalogBuiltinFunction("col_description") + "(a.attrelid, a.attnum) AS column_comment, " +
"CASE WHEN t.typname = 'numeric' AND a.atttypmod > 0 " +
"THEN ((a.atttypmod - 4) >> 16) & 65535 ELSE NULL END AS numeric_precision, " +
"CASE WHEN t.typname = 'numeric' AND a.atttypmod > 0 " +
"THEN (a.atttypmod - 4) & 65535 ELSE NULL END AS numeric_scale, " +
"CASE WHEN t.typname IN ('varchar', 'bpchar') AND a.atttypmod > 0 " +
"THEN a.atttypmod - 4 ELSE NULL END AS character_maximum_length " +
"FROM pg_catalog.pg_attribute a " +
"JOIN pg_catalog.pg_type t ON t.oid = a.atttypid " +
"JOIN pg_catalog.pg_class c ON c.oid = a.attrelid " +
"JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace " +
"LEFT JOIN pg_catalog.pg_attrdef ad ON ad.adrelid = a.attrelid AND ad.adnum = a.attnum " +
"FROM " + profile.catalogRelation("attribute") + " a " +
"JOIN " + profile.catalogRelation("type") + " t ON t.oid = a.atttypid " +
"JOIN " + profile.catalogRelation("class") + " c ON c.oid = a.attrelid " +
"JOIN " + profile.catalogRelation("namespace") + " n ON n.oid = c.relnamespace " +
"LEFT JOIN " + profile.catalogRelation("attrdef") + " ad ON ad.adrelid = a.attrelid AND ad.adnum = a.attnum " +
"WHERE n.nspname = ? AND c.relname = ? " +
"AND a.attnum > 0 AND NOT a.attisdropped " +
"ORDER BY a.attnum";
@@ -425,11 +431,11 @@ public abstract class PostgresLikeAgent extends AbstractJdbcAgent {
return unchecked(() -> {
Set<String> primaryKeys = new LinkedHashSet<>();
String sql = "SELECT a.attname AS column_name " +
"FROM pg_catalog.pg_constraint co " +
"JOIN pg_catalog.pg_class c ON c.oid = co.conrelid " +
"JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace " +
"FROM " + profile.catalogRelation("constraint") + " co " +
"JOIN " + profile.catalogRelation("class") + " c ON c.oid = co.conrelid " +
"JOIN " + profile.catalogRelation("namespace") + " n ON n.oid = c.relnamespace " +
"JOIN LATERAL (SELECT unnest(co.conkey) AS attnum, generate_series(1, array_length(co.conkey, 1)) AS ord) AS pk_cols ON true " +
"JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid AND a.attnum = pk_cols.attnum " +
"JOIN " + profile.catalogRelation("attribute") + " a ON a.attrelid = c.oid AND a.attnum = pk_cols.attnum " +
"WHERE co.contype = 'p' " +
"AND n.nspname = ? " +
"AND c.relname = ? " +
@@ -454,13 +460,13 @@ public abstract class PostgresLikeAgent extends AbstractJdbcAgent {
String sql = "SELECT i.relname AS index_name, am.amname AS index_type, " +
"ix.indisunique AS is_unique, ix.indisprimary AS is_primary, " +
"array_agg(a.attname ORDER BY k.n) AS columns " +
"FROM pg_catalog.pg_index ix " +
"JOIN pg_catalog.pg_class t ON t.oid = ix.indrelid " +
"JOIN pg_catalog.pg_class i ON i.oid = ix.indexrelid " +
"JOIN pg_catalog.pg_namespace n ON n.oid = t.relnamespace " +
"JOIN pg_catalog.pg_am am ON am.oid = i.relam " +
"FROM " + profile.catalogRelation("index") + " ix " +
"JOIN " + profile.catalogRelation("class") + " t ON t.oid = ix.indrelid " +
"JOIN " + profile.catalogRelation("class") + " i ON i.oid = ix.indexrelid " +
"JOIN " + profile.catalogRelation("namespace") + " n ON n.oid = t.relnamespace " +
"JOIN " + profile.catalogRelation("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 " +
"JOIN pg_catalog.pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum " +
"JOIN " + profile.catalogRelation("attribute") + " a ON a.attrelid = t.oid AND a.attnum = k.attnum " +
"WHERE n.nspname = ? AND t.relname = ? " +
"GROUP BY i.relname, am.amname, ix.indisunique, ix.indisprimary " +
"ORDER BY i.relname";
@@ -499,14 +505,14 @@ public abstract class PostgresLikeAgent extends AbstractJdbcAgent {
"a.attname AS column_name, " +
"rc.relname AS ref_table, " +
"ra.attname AS ref_column " +
"FROM pg_catalog.pg_constraint co " +
"JOIN pg_catalog.pg_class c ON c.oid = co.conrelid " +
"JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace " +
"JOIN pg_catalog.pg_class rc ON rc.oid = co.confrelid " +
"FROM " + profile.catalogRelation("constraint") + " co " +
"JOIN " + profile.catalogRelation("class") + " c ON c.oid = co.conrelid " +
"JOIN " + profile.catalogRelation("namespace") + " n ON n.oid = c.relnamespace " +
"JOIN " + profile.catalogRelation("class") + " rc ON rc.oid = co.confrelid " +
"JOIN LATERAL (SELECT unnest(co.conkey) AS attnum, generate_series(1, array_length(co.conkey, 1)) AS ord) AS fk ON true " +
"JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid AND a.attnum = fk.attnum " +
"JOIN " + profile.catalogRelation("attribute") + " a ON a.attrelid = c.oid AND a.attnum = fk.attnum " +
"JOIN LATERAL (SELECT unnest(co.confkey) AS attnum, generate_series(1, array_length(co.confkey, 1)) AS ord) AS pk ON pk.ord = fk.ord " +
"JOIN pg_catalog.pg_attribute ra ON ra.attrelid = rc.oid AND ra.attnum = pk.attnum " +
"JOIN " + profile.catalogRelation("attribute") + " ra ON ra.attrelid = rc.oid AND ra.attnum = pk.attnum " +
"WHERE co.contype = 'f' " +
"AND n.nspname = ? " +
"AND c.relname = ? " +
@@ -541,9 +547,9 @@ public abstract class PostgresLikeAgent extends AbstractJdbcAgent {
"CASE WHEN (tg.tgtype & 32) <> 0 THEN 'TRUNCATE,' ELSE '' END" +
")) AS event_manipulation, " +
"CASE WHEN (tg.tgtype & 2) <> 0 THEN 'BEFORE' ELSE 'AFTER' END AS action_timing " +
"FROM pg_catalog.pg_trigger tg " +
"JOIN pg_catalog.pg_class c ON c.oid = tg.tgrelid " +
"JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace " +
"FROM " + profile.catalogRelation("trigger") + " tg " +
"JOIN " + profile.catalogRelation("class") + " c ON c.oid = tg.tgrelid " +
"JOIN " + profile.catalogRelation("namespace") + " n ON n.oid = c.relnamespace " +
"WHERE n.nspname = ? AND c.relname = ? AND NOT tg.tgisinternal " +
"ORDER BY tg.tgname";
try (java.sql.PreparedStatement stmt = requireConnection().prepareStatement(sql)) {
@@ -3,10 +3,38 @@ package com.dbx.agent;
public final class PostgresLikeAgentProfile {
private final String driverClass;
private final String urlTemplate;
private final int defaultPort;
private final String catalogSchema;
private final String catalogPrefix;
public PostgresLikeAgentProfile(String driverClass, String urlTemplate) {
this(driverClass, urlTemplate, 0, "pg_catalog", "pg_");
}
public PostgresLikeAgentProfile(
String driverClass,
String urlTemplate,
String catalogSchema,
String catalogPrefix
) {
this(driverClass, urlTemplate, 0, catalogSchema, catalogPrefix);
}
public PostgresLikeAgentProfile(
String driverClass,
String urlTemplate,
int defaultPort,
String catalogSchema,
String catalogPrefix
) {
this.driverClass = driverClass;
this.urlTemplate = urlTemplate;
this.defaultPort = defaultPort;
// PostgreSQL derivatives may preserve catalog semantics while renaming every
// relation and helper function. Centralize that mapping instead of copying
// the full metadata implementation for each compatible database.
this.catalogSchema = catalogSchema;
this.catalogPrefix = catalogPrefix;
}
public String getDriverClass() {
@@ -17,11 +45,39 @@ public final class PostgresLikeAgentProfile {
return urlTemplate;
}
public String getCatalogSchema() {
return catalogSchema;
}
public String getToastSchema() {
return catalogPrefix + "toast";
}
public String getTemporarySchemaPrefix() {
return catalogPrefix + "temp_";
}
public String getToastTemporarySchemaPrefix() {
return catalogPrefix + "toast_temp_";
}
public String catalogRelation(String name) {
return catalogSchema + "." + catalogPrefix + name;
}
public String catalogPrefixedFunction(String name) {
return catalogSchema + "." + catalogPrefix + name;
}
public String catalogBuiltinFunction(String name) {
return catalogSchema + "." + name;
}
public String buildUrl(ConnectParams params) {
return new JdbcAgentProfile(
driverClass,
urlTemplate,
0,
defaultPort,
false,
java.util.Collections.emptySet(),
java.util.Arrays.asList("TABLE", "VIEW", "MATERIALIZED VIEW", "SYSTEM TABLE", "SYSTEM VIEW")