fix(kingbase): 修复连接与元数据查询异常
This commit is contained in:
13 files changed
+369
-127
No files matched your search
@@ -30,6 +30,21 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public final class KingbaseAgent extends PostgresLikeAgent {
|
public final class KingbaseAgent extends PostgresLikeAgent {
|
||||||
|
private static final int KINGBASE_VOID_TYPE_OID = 2278;
|
||||||
|
private static final String KINGBASE_REL_NAME = "CAST(c.relname AS varchar(256))";
|
||||||
|
private static final String KINGBASE_REL_OID = "CAST(c.oid AS varchar(64))";
|
||||||
|
private static final String KINGBASE_REL_NAMESPACE = "CAST(c.relnamespace AS varchar(64))";
|
||||||
|
private static final String KINGBASE_SCHEMA_NAME = "CAST(n.nspname AS varchar(256))";
|
||||||
|
private static final String KINGBASE_NAMESPACE_OID = "CAST(n.oid AS varchar(64))";
|
||||||
|
private static final String KINGBASE_DESCRIPTION = "CAST(d.description AS varchar(4000))";
|
||||||
|
private static final String KINGBASE_ROUTINE_NAME = "CAST(p.proname AS varchar(256))";
|
||||||
|
private static final String KINGBASE_ROUTINE_OID = "CAST(p.oid AS varchar(64))";
|
||||||
|
private static final String KINGBASE_ROUTINE_NAMESPACE = "CAST(p.pronamespace AS varchar(64))";
|
||||||
|
private static final String KINGBASE_VIEW_NAME = "CAST(v.viewname AS varchar(256))";
|
||||||
|
private static final String KINGBASE_VIEW_SCHEMA = "CAST(v.schemaname AS varchar(256))";
|
||||||
|
private static final String KINGBASE_MATVIEW_NAME = "CAST(mv.matviewname AS varchar(256))";
|
||||||
|
private static final String KINGBASE_MATVIEW_SCHEMA = "CAST(mv.schemaname AS varchar(256))";
|
||||||
|
|
||||||
public static final PostgresLikeAgentProfile KINGBASE_PROFILE = new PostgresLikeAgentProfile(
|
public static final PostgresLikeAgentProfile KINGBASE_PROFILE = new PostgresLikeAgentProfile(
|
||||||
"com.kingbase8.Driver",
|
"com.kingbase8.Driver",
|
||||||
"jdbc:kingbase8://{host}:{port}/{database}"
|
"jdbc:kingbase8://{host}:{port}/{database}"
|
||||||
@@ -110,36 +125,7 @@ public final class KingbaseAgent extends PostgresLikeAgent {
|
|||||||
if (isMysqlCompatMode()) {
|
if (isMysqlCompatMode()) {
|
||||||
return listTables(schema, "table_type IN ('BASE TABLE', 'VIEW')");
|
return listTables(schema, "table_type IN ('BASE TABLE', 'VIEW')");
|
||||||
}
|
}
|
||||||
return unchecked(() -> {
|
return queryRegularTables(schema, MetadataListConstraints.NONE);
|
||||||
List<TableInfo> result = new ArrayList<>();
|
|
||||||
String sql = "SELECT c.relname AS table_name, " +
|
|
||||||
"CASE c.relkind " +
|
|
||||||
"WHEN 'r' THEN 'TABLE' " +
|
|
||||||
"WHEN 'p' THEN 'TABLE' " +
|
|
||||||
"WHEN 'v' THEN 'VIEW' " +
|
|
||||||
"WHEN 'm' THEN 'MATERIALIZED_VIEW' " +
|
|
||||||
"WHEN 'f' THEN 'FOREIGN_TABLE' " +
|
|
||||||
"ELSE 'TABLE' END AS table_type, " +
|
|
||||||
"d.description AS table_comment " +
|
|
||||||
"FROM sys_catalog.sys_class c " +
|
|
||||||
"JOIN sys_catalog.sys_namespace n ON n.oid = c.relnamespace " +
|
|
||||||
"LEFT JOIN sys_catalog.sys_description d ON d.objoid = c.oid AND d.objsubid = 0 " +
|
|
||||||
"WHERE n.nspname = ? AND c.relkind IN ('r','p','v','m','f') " +
|
|
||||||
"ORDER BY c.relname";
|
|
||||||
try (PreparedStatement stmt = requireConnected().prepareStatement(sql)) {
|
|
||||||
stmt.setString(1, effectiveSchema(schema));
|
|
||||||
try (ResultSet rs = stmt.executeQuery()) {
|
|
||||||
while (rs.next()) {
|
|
||||||
result.add(new TableInfo(
|
|
||||||
rs.getString("table_name"),
|
|
||||||
normalizeTableType(rs.getString("table_type")),
|
|
||||||
rs.getString("table_comment")
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -164,23 +150,14 @@ public final class KingbaseAgent extends PostgresLikeAgent {
|
|||||||
return unchecked(() -> {
|
return unchecked(() -> {
|
||||||
List<TableInfo> result = new ArrayList<>();
|
List<TableInfo> result = new ArrayList<>();
|
||||||
List<Object> args = new ArrayList<>();
|
List<Object> args = new ArrayList<>();
|
||||||
StringBuilder sql = new StringBuilder("SELECT c.relname AS table_name, ")
|
List<String> branches = new ArrayList<>();
|
||||||
.append("CASE c.relkind ")
|
addRegularRelationBranches(branches, args, effectiveSchema(schema), constraints, "table_name", "table_type", "table_comment");
|
||||||
.append("WHEN 'r' THEN 'TABLE' ")
|
if (branches.isEmpty()) {
|
||||||
.append("WHEN 'p' THEN 'TABLE' ")
|
return List.of();
|
||||||
.append("WHEN 'v' THEN 'VIEW' ")
|
}
|
||||||
.append("WHEN 'm' THEN 'MATERIALIZED_VIEW' ")
|
StringBuilder sql = new StringBuilder("SELECT table_name, table_type, table_comment FROM (")
|
||||||
.append("WHEN 'f' THEN 'FOREIGN_TABLE' ")
|
.append(String.join(" UNION ALL ", branches))
|
||||||
.append("ELSE 'TABLE' END AS table_type, ")
|
.append(") metadata_tables ORDER BY table_name");
|
||||||
.append("d.description AS table_comment ")
|
|
||||||
.append("FROM sys_catalog.sys_class c ")
|
|
||||||
.append("JOIN sys_catalog.sys_namespace n ON n.oid = c.relnamespace ")
|
|
||||||
.append("LEFT JOIN sys_catalog.sys_description d ON d.objoid = c.oid AND d.objsubid = 0 ")
|
|
||||||
.append("WHERE n.nspname = ? AND c.relkind IN ('r','p','v','m','f')");
|
|
||||||
args.add(effectiveSchema(schema));
|
|
||||||
appendRelkindPredicate(sql, args, constraints);
|
|
||||||
MetadataSqlSupport.appendNameFilter(sql, args, "c.relname", constraints);
|
|
||||||
sql.append(" ORDER BY c.relname");
|
|
||||||
MetadataSqlSupport.appendLiteralLimitOffset(sql, constraints);
|
MetadataSqlSupport.appendLiteralLimitOffset(sql, constraints);
|
||||||
try (PreparedStatement stmt = requireConnected().prepareStatement(sql.toString())) {
|
try (PreparedStatement stmt = requireConnected().prepareStatement(sql.toString())) {
|
||||||
MetadataSqlSupport.bind(stmt, args);
|
MetadataSqlSupport.bind(stmt, args);
|
||||||
@@ -232,16 +209,15 @@ public final class KingbaseAgent extends PostgresLikeAgent {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
String sql = "SELECT p.proname AS routine_name, " +
|
String sql = "SELECT " + KINGBASE_ROUTINE_NAME + " AS routine_name, " +
|
||||||
"CASE p.prokind WHEN 'p' THEN 'PROCEDURE' ELSE 'FUNCTION' END AS routine_type, " +
|
"CASE WHEN p.prorettype = " + KINGBASE_VOID_TYPE_OID + " THEN 'PROCEDURE' ELSE 'FUNCTION' END AS routine_type, " +
|
||||||
"d.description AS routine_comment " +
|
KINGBASE_DESCRIPTION + " AS routine_comment " +
|
||||||
"FROM sys_catalog.sys_proc p " +
|
"FROM sys_catalog.sys_proc p " +
|
||||||
"JOIN sys_catalog.sys_namespace n ON n.oid = p.pronamespace " +
|
"JOIN sys_catalog.sys_namespace n ON " + KINGBASE_NAMESPACE_OID + " = " + KINGBASE_ROUTINE_NAMESPACE + " " +
|
||||||
"LEFT JOIN sys_catalog.sys_description d ON d.objoid = p.oid AND d.objsubid = 0 " +
|
"LEFT JOIN sys_catalog.sys_description d ON CAST(d.objoid AS varchar(64)) = " + KINGBASE_ROUTINE_OID + " AND d.objsubid = 0 " +
|
||||||
"WHERE n.nspname = ? AND p.prokind IN ('p','f') " +
|
"WHERE " + KINGBASE_SCHEMA_NAME + " = " + sqlString(effectiveSchema) + " " +
|
||||||
"ORDER BY p.proname";
|
"ORDER BY " + KINGBASE_ROUTINE_NAME;
|
||||||
try (PreparedStatement stmt = requireConnected().prepareStatement(sql)) {
|
try (PreparedStatement stmt = requireConnected().prepareStatement(sql)) {
|
||||||
stmt.setString(1, effectiveSchema);
|
|
||||||
try (ResultSet rs = stmt.executeQuery()) {
|
try (ResultSet rs = stmt.executeQuery()) {
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
result.add(new ObjectInfo(
|
result.add(new ObjectInfo(
|
||||||
@@ -282,29 +258,19 @@ public final class KingbaseAgent extends PostgresLikeAgent {
|
|||||||
List<String> branches = new ArrayList<>();
|
List<String> branches = new ArrayList<>();
|
||||||
List<Object> args = new ArrayList<>();
|
List<Object> args = new ArrayList<>();
|
||||||
if (constraints.includesTableLikeTypes()) {
|
if (constraints.includesTableLikeTypes()) {
|
||||||
StringBuilder tableSql = new StringBuilder("SELECT c.relname AS object_name, ")
|
addRegularRelationBranches(branches, args, effectiveSchema, constraints, "object_name", "object_type", "object_comment");
|
||||||
.append("CASE c.relkind WHEN 'r' THEN 'TABLE' WHEN 'p' THEN 'TABLE' WHEN 'v' THEN 'VIEW' WHEN 'm' THEN 'MATERIALIZED_VIEW' WHEN 'f' THEN 'FOREIGN_TABLE' ELSE 'TABLE' END AS object_type, ")
|
|
||||||
.append("d.description AS object_comment ")
|
|
||||||
.append("FROM sys_catalog.sys_class c ")
|
|
||||||
.append("JOIN sys_catalog.sys_namespace n ON n.oid = c.relnamespace ")
|
|
||||||
.append("LEFT JOIN sys_catalog.sys_description d ON d.objoid = c.oid AND d.objsubid = 0 ")
|
|
||||||
.append("WHERE n.nspname = ? AND c.relkind IN ('r','p','v','m','f')");
|
|
||||||
args.add(effectiveSchema);
|
|
||||||
appendRelkindPredicate(tableSql, args, constraints);
|
|
||||||
MetadataSqlSupport.appendNameFilter(tableSql, args, "c.relname", constraints);
|
|
||||||
branches.add(tableSql.toString());
|
|
||||||
}
|
}
|
||||||
if (constraints.objectTypeAllowed("PROCEDURE") || constraints.objectTypeAllowed("FUNCTION")) {
|
if (constraints.objectTypeAllowed("PROCEDURE") || constraints.objectTypeAllowed("FUNCTION")) {
|
||||||
StringBuilder routineSql = new StringBuilder("SELECT p.proname AS object_name, ")
|
StringBuilder routineSql = new StringBuilder("SELECT ")
|
||||||
.append("CASE p.prokind WHEN 'p' THEN 'PROCEDURE' ELSE 'FUNCTION' END AS object_type, ")
|
.append(KINGBASE_ROUTINE_NAME).append(" AS object_name, ")
|
||||||
.append("d.description AS object_comment ")
|
.append("CASE WHEN p.prorettype = ").append(KINGBASE_VOID_TYPE_OID).append(" THEN 'PROCEDURE' ELSE 'FUNCTION' END AS object_type, ")
|
||||||
|
.append(KINGBASE_DESCRIPTION).append(" AS object_comment ")
|
||||||
.append("FROM sys_catalog.sys_proc p ")
|
.append("FROM sys_catalog.sys_proc p ")
|
||||||
.append("JOIN sys_catalog.sys_namespace n ON n.oid = p.pronamespace ")
|
.append("JOIN sys_catalog.sys_namespace n ON ").append(KINGBASE_NAMESPACE_OID).append(" = ").append(KINGBASE_ROUTINE_NAMESPACE).append(' ')
|
||||||
.append("LEFT JOIN sys_catalog.sys_description d ON d.objoid = p.oid AND d.objsubid = 0 ")
|
.append("LEFT JOIN sys_catalog.sys_description d ON CAST(d.objoid AS varchar(64)) = ").append(KINGBASE_ROUTINE_OID).append(" AND d.objsubid = 0 ")
|
||||||
.append("WHERE n.nspname = ?");
|
.append("WHERE ").append(KINGBASE_SCHEMA_NAME).append(" = ").append(sqlString(effectiveSchema));
|
||||||
args.add(effectiveSchema);
|
|
||||||
appendRoutineKindPredicate(routineSql, args, constraints);
|
appendRoutineKindPredicate(routineSql, args, constraints);
|
||||||
MetadataSqlSupport.appendNameFilter(routineSql, args, "p.proname", constraints);
|
MetadataSqlSupport.appendNameFilter(routineSql, args, KINGBASE_ROUTINE_NAME, constraints);
|
||||||
branches.add(routineSql.toString());
|
branches.add(routineSql.toString());
|
||||||
}
|
}
|
||||||
if (branches.isEmpty()) {
|
if (branches.isEmpty()) {
|
||||||
@@ -345,6 +311,14 @@ public final class KingbaseAgent extends PostgresLikeAgent {
|
|||||||
"FROM information_schema.views " +
|
"FROM information_schema.views " +
|
||||||
"WHERE table_schema = " + sqlString(effectiveSchema(schema)) +
|
"WHERE table_schema = " + sqlString(effectiveSchema(schema)) +
|
||||||
" AND table_name = " + sqlString(name);
|
" AND table_name = " + sqlString(name);
|
||||||
|
if (!isMysqlCompatMode()) {
|
||||||
|
sql = "SELECT sys_get_viewdef(c.oid) AS view_definition " +
|
||||||
|
"FROM sys_catalog.sys_class c " +
|
||||||
|
"JOIN sys_catalog.sys_namespace n ON " + KINGBASE_NAMESPACE_OID + " = " + KINGBASE_REL_NAMESPACE + " " +
|
||||||
|
"WHERE " + KINGBASE_SCHEMA_NAME + " = " + sqlString(effectiveSchema(schema)) +
|
||||||
|
" AND " + KINGBASE_REL_NAME + " = " + sqlString(name) +
|
||||||
|
" LIMIT 1";
|
||||||
|
}
|
||||||
try (Statement stmt = requireConnected().createStatement()) {
|
try (Statement stmt = requireConnected().createStatement()) {
|
||||||
try (ResultSet rs = stmt.executeQuery(sql)) {
|
try (ResultSet rs = stmt.executeQuery(sql)) {
|
||||||
if (rs.next()) {
|
if (rs.next()) {
|
||||||
@@ -359,16 +333,15 @@ public final class KingbaseAgent extends PostgresLikeAgent {
|
|||||||
private ObjectSource routineSource(String schema, String name, String objectType) {
|
private ObjectSource routineSource(String schema, String name, String objectType) {
|
||||||
return unchecked(() -> {
|
return unchecked(() -> {
|
||||||
String source = "";
|
String source = "";
|
||||||
String prokind = "PROCEDURE".equalsIgnoreCase(objectType) ? "p" : "f";
|
|
||||||
String sql = "SELECT sys_get_functiondef(p.oid) AS source " +
|
String sql = "SELECT sys_get_functiondef(p.oid) AS source " +
|
||||||
"FROM sys_catalog.sys_proc p " +
|
"FROM sys_catalog.sys_proc p " +
|
||||||
"JOIN sys_catalog.sys_namespace n ON n.oid = p.pronamespace " +
|
"JOIN sys_catalog.sys_namespace n ON " + KINGBASE_NAMESPACE_OID + " = " + KINGBASE_ROUTINE_NAMESPACE + " " +
|
||||||
"WHERE n.nspname = ? AND p.proname = ? AND p.prokind = ? " +
|
"WHERE " + KINGBASE_SCHEMA_NAME + " = " + sqlString(effectiveSchema(schema)) +
|
||||||
"ORDER BY p.oid LIMIT 1";
|
" AND " + KINGBASE_ROUTINE_NAME + " = " + sqlString(name) + " " +
|
||||||
|
"ORDER BY CASE WHEN p.prorettype = " + KINGBASE_VOID_TYPE_OID + " THEN " +
|
||||||
|
("PROCEDURE".equalsIgnoreCase(objectType) ? "0 ELSE 1" : "1 ELSE 0") +
|
||||||
|
" END, p.oid LIMIT 1";
|
||||||
try (PreparedStatement stmt = requireConnected().prepareStatement(sql)) {
|
try (PreparedStatement stmt = requireConnected().prepareStatement(sql)) {
|
||||||
stmt.setString(1, effectiveSchema(schema));
|
|
||||||
stmt.setString(2, name);
|
|
||||||
stmt.setString(3, prokind);
|
|
||||||
try (ResultSet rs = stmt.executeQuery()) {
|
try (ResultSet rs = stmt.executeQuery()) {
|
||||||
if (rs.next()) {
|
if (rs.next()) {
|
||||||
source = coalesce(rs.getString("source"));
|
source = coalesce(rs.getString("source"));
|
||||||
@@ -647,27 +620,103 @@ public final class KingbaseAgent extends PostgresLikeAgent {
|
|||||||
|| constraints.objectTypeAllowed("FUNCTION");
|
|| constraints.objectTypeAllowed("FUNCTION");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void appendRelkindPredicate(StringBuilder sql, List<Object> args, MetadataListConstraints constraints) {
|
private static void addRegularRelationBranches(
|
||||||
if (!constraints.hasObjectTypes()) {
|
List<String> branches,
|
||||||
return;
|
List<Object> args,
|
||||||
}
|
String schema,
|
||||||
List<String> kinds = new ArrayList<>();
|
MetadataListConstraints constraints,
|
||||||
|
String nameAlias,
|
||||||
|
String typeAlias,
|
||||||
|
String commentAlias
|
||||||
|
) {
|
||||||
if (constraints.tableTypeAllowed("TABLE")) {
|
if (constraints.tableTypeAllowed("TABLE")) {
|
||||||
kinds.add("r");
|
branches.add(regularRelationBranch(schema, args, constraints, nameAlias, typeAlias, commentAlias, "TABLE"));
|
||||||
kinds.add("p");
|
|
||||||
}
|
}
|
||||||
if (constraints.tableTypeAllowed("VIEW")) {
|
if (constraints.tableTypeAllowed("VIEW")) {
|
||||||
kinds.add("v");
|
branches.add(regularViewBranch(schema, args, constraints, nameAlias, typeAlias, commentAlias));
|
||||||
}
|
}
|
||||||
if (constraints.tableTypeAllowed("MATERIALIZED_VIEW")) {
|
if (constraints.tableTypeAllowed("MATERIALIZED_VIEW")) {
|
||||||
kinds.add("m");
|
branches.add(regularMaterializedViewBranch(schema, args, constraints, nameAlias, typeAlias, commentAlias));
|
||||||
}
|
}
|
||||||
if (kinds.isEmpty()) {
|
}
|
||||||
sql.append(" AND 1 = 0");
|
|
||||||
return;
|
private static String regularRelationBranch(
|
||||||
}
|
String schema,
|
||||||
sql.append(" AND c.relkind IN (").append(MetadataSqlSupport.placeholders(kinds.size())).append(")");
|
List<Object> args,
|
||||||
args.addAll(kinds);
|
MetadataListConstraints constraints,
|
||||||
|
String nameAlias,
|
||||||
|
String typeAlias,
|
||||||
|
String commentAlias,
|
||||||
|
String objectType
|
||||||
|
) {
|
||||||
|
StringBuilder sql = new StringBuilder("SELECT ")
|
||||||
|
.append(KINGBASE_REL_NAME).append(" AS ").append(nameAlias).append(", '")
|
||||||
|
.append(objectType).append("' AS ").append(typeAlias).append(", ")
|
||||||
|
.append(KINGBASE_DESCRIPTION).append(" AS ").append(commentAlias).append(' ')
|
||||||
|
.append("FROM sys_catalog.sys_class c ")
|
||||||
|
.append("JOIN sys_catalog.sys_namespace n ON ").append(KINGBASE_NAMESPACE_OID).append(" = ").append(KINGBASE_REL_NAMESPACE).append(' ')
|
||||||
|
.append("LEFT JOIN sys_catalog.sys_description d ON CAST(d.objoid AS varchar(64)) = ").append(KINGBASE_REL_OID).append(" AND d.objsubid = 0 ")
|
||||||
|
.append("WHERE ").append(KINGBASE_SCHEMA_NAME).append(" = ").append(sqlString(schema));
|
||||||
|
appendRegularTablePredicate(sql);
|
||||||
|
MetadataSqlSupport.appendNameFilter(sql, args, KINGBASE_REL_NAME, constraints);
|
||||||
|
return sql.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String regularViewBranch(
|
||||||
|
String schema,
|
||||||
|
List<Object> args,
|
||||||
|
MetadataListConstraints constraints,
|
||||||
|
String nameAlias,
|
||||||
|
String typeAlias,
|
||||||
|
String commentAlias
|
||||||
|
) {
|
||||||
|
// sys_views/sys_matviews avoid relkind while preserving the sidebar's
|
||||||
|
// separate VIEW and MATERIALIZED_VIEW groups.
|
||||||
|
StringBuilder sql = new StringBuilder("SELECT ")
|
||||||
|
.append(KINGBASE_VIEW_NAME).append(" AS ").append(nameAlias).append(", 'VIEW' AS ").append(typeAlias).append(", ")
|
||||||
|
.append(KINGBASE_DESCRIPTION).append(" AS ").append(commentAlias).append(' ')
|
||||||
|
.append("FROM sys_catalog.sys_views v ")
|
||||||
|
.append("JOIN sys_catalog.sys_namespace n ON ").append(KINGBASE_SCHEMA_NAME).append(" = ").append(KINGBASE_VIEW_SCHEMA).append(' ')
|
||||||
|
.append("JOIN sys_catalog.sys_class c ON ").append(KINGBASE_REL_NAMESPACE).append(" = ").append(KINGBASE_NAMESPACE_OID)
|
||||||
|
.append(" AND ").append(KINGBASE_REL_NAME).append(" = ").append(KINGBASE_VIEW_NAME).append(' ')
|
||||||
|
.append("LEFT JOIN sys_catalog.sys_description d ON CAST(d.objoid AS varchar(64)) = ").append(KINGBASE_REL_OID).append(" AND d.objsubid = 0 ")
|
||||||
|
.append("WHERE ").append(KINGBASE_VIEW_SCHEMA).append(" = ").append(sqlString(schema));
|
||||||
|
MetadataSqlSupport.appendNameFilter(sql, args, KINGBASE_VIEW_NAME, constraints);
|
||||||
|
return sql.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String regularMaterializedViewBranch(
|
||||||
|
String schema,
|
||||||
|
List<Object> args,
|
||||||
|
MetadataListConstraints constraints,
|
||||||
|
String nameAlias,
|
||||||
|
String typeAlias,
|
||||||
|
String commentAlias
|
||||||
|
) {
|
||||||
|
StringBuilder sql = new StringBuilder("SELECT ")
|
||||||
|
.append(KINGBASE_MATVIEW_NAME).append(" AS ").append(nameAlias).append(", 'MATERIALIZED_VIEW' AS ").append(typeAlias).append(", ")
|
||||||
|
.append(KINGBASE_DESCRIPTION).append(" AS ").append(commentAlias).append(' ')
|
||||||
|
.append("FROM sys_catalog.sys_matviews mv ")
|
||||||
|
.append("JOIN sys_catalog.sys_namespace n ON ").append(KINGBASE_SCHEMA_NAME).append(" = ").append(KINGBASE_MATVIEW_SCHEMA).append(' ')
|
||||||
|
.append("JOIN sys_catalog.sys_class c ON ").append(KINGBASE_REL_NAMESPACE).append(" = ").append(KINGBASE_NAMESPACE_OID)
|
||||||
|
.append(" AND ").append(KINGBASE_REL_NAME).append(" = ").append(KINGBASE_MATVIEW_NAME).append(' ')
|
||||||
|
.append("LEFT JOIN sys_catalog.sys_description d ON CAST(d.objoid AS varchar(64)) = ").append(KINGBASE_REL_OID).append(" AND d.objsubid = 0 ")
|
||||||
|
.append("WHERE ").append(KINGBASE_MATVIEW_SCHEMA).append(" = ").append(sqlString(schema));
|
||||||
|
MetadataSqlSupport.appendNameFilter(sql, args, KINGBASE_MATVIEW_NAME, constraints);
|
||||||
|
return sql.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void appendRegularTablePredicate(StringBuilder sql) {
|
||||||
|
sql.append(" AND NOT EXISTS (SELECT 1 FROM sys_catalog.sys_rewrite r ")
|
||||||
|
.append("WHERE CAST(r.ev_class AS varchar(64)) = ").append(KINGBASE_REL_OID)
|
||||||
|
.append(" AND CAST(r.rulename AS varchar(256)) = '_RETURN')")
|
||||||
|
.append(" AND NOT EXISTS (SELECT 1 FROM sys_catalog.sys_index ix ")
|
||||||
|
.append("WHERE CAST(ix.indexrelid AS varchar(64)) = ").append(KINGBASE_REL_OID).append(')')
|
||||||
|
.append(" AND NOT EXISTS (SELECT 1 FROM sys_catalog.sys_attribute sa1 ")
|
||||||
|
.append("JOIN sys_catalog.sys_attribute sa2 ON CAST(sa2.attrelid AS varchar(64)) = CAST(sa1.attrelid AS varchar(64)) ")
|
||||||
|
.append("WHERE CAST(sa1.attrelid AS varchar(64)) = ").append(KINGBASE_REL_OID)
|
||||||
|
.append(" AND CAST(sa1.attname AS varchar(256)) = 'last_value'")
|
||||||
|
.append(" AND CAST(sa2.attname AS varchar(256)) = 'log_cnt')");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void appendMysqlCompatTableTypePredicate(StringBuilder sql, List<Object> args, MetadataListConstraints constraints) {
|
private static void appendMysqlCompatTableTypePredicate(StringBuilder sql, List<Object> args, MetadataListConstraints constraints) {
|
||||||
@@ -691,19 +740,21 @@ public final class KingbaseAgent extends PostgresLikeAgent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void appendRoutineKindPredicate(StringBuilder sql, List<Object> args, MetadataListConstraints constraints) {
|
private static void appendRoutineKindPredicate(StringBuilder sql, List<Object> args, MetadataListConstraints constraints) {
|
||||||
List<String> kinds = new ArrayList<>();
|
if (!constraints.hasObjectTypes()) {
|
||||||
if (!constraints.hasObjectTypes() || constraints.objectTypeAllowed("PROCEDURE")) {
|
return;
|
||||||
kinds.add("p");
|
|
||||||
}
|
}
|
||||||
if (!constraints.hasObjectTypes() || constraints.objectTypeAllowed("FUNCTION")) {
|
boolean includeProcedures = constraints.objectTypeAllowed("PROCEDURE");
|
||||||
kinds.add("f");
|
boolean includeFunctions = constraints.objectTypeAllowed("FUNCTION");
|
||||||
|
if (includeProcedures && includeFunctions) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (kinds.isEmpty()) {
|
if (!includeProcedures && !includeFunctions) {
|
||||||
sql.append(" AND 1 = 0");
|
sql.append(" AND 1 = 0");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sql.append(" AND p.prokind IN (").append(MetadataSqlSupport.placeholders(kinds.size())).append(")");
|
sql.append(includeProcedures
|
||||||
args.addAll(kinds);
|
? " AND p.prorettype = " + KINGBASE_VOID_TYPE_OID
|
||||||
|
: " AND p.prorettype <> " + KINGBASE_VOID_TYPE_OID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<ObjectInfo> toObjects(List<TableInfo> tables, String schema) {
|
private static List<ObjectInfo> toObjects(List<TableInfo> tables, String schema) {
|
||||||
|
|||||||
+34
-5
@@ -147,7 +147,11 @@ class KingbaseAgentTest extends JdbcFakeExecutionBehaviorTest {
|
|||||||
Assertions.assertEquals("app_view", tables.get(1).getName());
|
Assertions.assertEquals("app_view", tables.get(1).getName());
|
||||||
Assertions.assertEquals("VIEW", tables.get(1).getTable_type());
|
Assertions.assertEquals("VIEW", tables.get(1).getTable_type());
|
||||||
Assertions.assertTrue(sql.get(0).contains("FROM sys_catalog.sys_class"), sql.get(0));
|
Assertions.assertTrue(sql.get(0).contains("FROM sys_catalog.sys_class"), sql.get(0));
|
||||||
Assertions.assertTrue(sql.get(0).contains("c.relkind IN ('r','p','v','m','f')"), sql.get(0));
|
Assertions.assertTrue(sql.get(0).contains("FROM sys_catalog.sys_rewrite"), sql.get(0));
|
||||||
|
Assertions.assertTrue(sql.get(0).contains("FROM sys_catalog.sys_index"), sql.get(0));
|
||||||
|
Assertions.assertTrue(sql.get(0).contains("FROM sys_catalog.sys_views"), sql.get(0));
|
||||||
|
Assertions.assertTrue(sql.get(0).contains("FROM sys_catalog.sys_matviews"), sql.get(0));
|
||||||
|
Assertions.assertFalse(sql.get(0).contains("relkind"), sql.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -177,7 +181,8 @@ class KingbaseAgentTest extends JdbcFakeExecutionBehaviorTest {
|
|||||||
Assertions.assertEquals("format_name", objects.get(3).getName());
|
Assertions.assertEquals("format_name", objects.get(3).getName());
|
||||||
Assertions.assertEquals("FUNCTION", objects.get(3).getObject_type());
|
Assertions.assertEquals("FUNCTION", objects.get(3).getObject_type());
|
||||||
Assertions.assertTrue(sql.get(1).contains("FROM sys_catalog.sys_proc"), sql.get(1));
|
Assertions.assertTrue(sql.get(1).contains("FROM sys_catalog.sys_proc"), sql.get(1));
|
||||||
Assertions.assertTrue(sql.get(1).contains("p.prokind IN ('p','f')"), sql.get(1));
|
Assertions.assertTrue(sql.get(1).contains("p.prorettype = 2278"), sql.get(1));
|
||||||
|
Assertions.assertFalse(sql.get(1).contains("prokind"), sql.get(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -192,11 +197,33 @@ class KingbaseAgentTest extends JdbcFakeExecutionBehaviorTest {
|
|||||||
agent.listTables("public", new MetadataListConstraints("ord", 30, 60, List.of("TABLE", "VIEW")));
|
agent.listTables("public", new MetadataListConstraints("ord", 30, 60, List.of("TABLE", "VIEW")));
|
||||||
|
|
||||||
Assertions.assertTrue(sql.get(0).contains("FROM sys_catalog.sys_class"), sql.get(0));
|
Assertions.assertTrue(sql.get(0).contains("FROM sys_catalog.sys_class"), sql.get(0));
|
||||||
Assertions.assertTrue(sql.get(0).contains("c.relkind IN (?, ?, ?)"), sql.get(0));
|
Assertions.assertTrue(sql.get(0).contains("FROM sys_catalog.sys_views"), sql.get(0));
|
||||||
Assertions.assertTrue(sql.get(0).contains("UPPER(c.relname) LIKE ? ESCAPE '\\\\'"), sql.get(0));
|
Assertions.assertTrue(sql.get(0).contains("UNION ALL"), sql.get(0));
|
||||||
|
Assertions.assertTrue(sql.get(0).contains("UPPER(CAST(c.relname AS varchar(256))) LIKE ? ESCAPE '\\\\'"), sql.get(0));
|
||||||
|
Assertions.assertFalse(sql.get(0).contains("relkind"), sql.get(0));
|
||||||
Assertions.assertTrue(sql.get(0).endsWith("LIMIT 30 OFFSET 60"), sql.get(0));
|
Assertions.assertTrue(sql.get(0).endsWith("LIMIT 30 OFFSET 60"), sql.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void constrainedRegularMaterializedViewMetadataUsesMatviewCatalog() {
|
||||||
|
List<String> sql = new ArrayList<>();
|
||||||
|
KingbaseAgent agent = new KingbaseAgent();
|
||||||
|
TestSupport.setPrivateConnection(agent, preparedConnection(sql, resultSet(
|
||||||
|
new String[]{"table_name", "table_type", "table_comment"},
|
||||||
|
new Object[][]{{"mv_sales", "MATERIALIZED_VIEW", "cached sales"}}
|
||||||
|
)));
|
||||||
|
|
||||||
|
List<TableInfo> tables = agent.listTables("public", new MetadataListConstraints("sales", 10, null, List.of("MATERIALIZED_VIEW")));
|
||||||
|
|
||||||
|
Assertions.assertEquals(1, tables.size());
|
||||||
|
Assertions.assertEquals("mv_sales", tables.get(0).getName());
|
||||||
|
Assertions.assertEquals("MATERIALIZED_VIEW", tables.get(0).getTable_type());
|
||||||
|
Assertions.assertTrue(sql.get(0).contains("FROM sys_catalog.sys_matviews"), sql.get(0));
|
||||||
|
Assertions.assertFalse(sql.get(0).contains("FROM sys_catalog.sys_views"), sql.get(0));
|
||||||
|
Assertions.assertTrue(sql.get(0).contains("UPPER(CAST(mv.matviewname AS varchar(256))) LIKE ? ESCAPE '\\\\'"), sql.get(0));
|
||||||
|
Assertions.assertFalse(sql.get(0).contains("relkind"), sql.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void constrainedRegularObjectMetadataPushesRoutineTypesAndPaging() {
|
void constrainedRegularObjectMetadataPushesRoutineTypesAndPaging() {
|
||||||
List<String> sql = new ArrayList<>();
|
List<String> sql = new ArrayList<>();
|
||||||
@@ -209,7 +236,8 @@ class KingbaseAgentTest extends JdbcFakeExecutionBehaviorTest {
|
|||||||
agent.listObjects("public", new MetadataListConstraints("sync", 10, null, List.of("PROCEDURE", "FUNCTION")));
|
agent.listObjects("public", new MetadataListConstraints("sync", 10, null, List.of("PROCEDURE", "FUNCTION")));
|
||||||
|
|
||||||
Assertions.assertTrue(sql.get(0).contains("FROM sys_catalog.sys_proc"), sql.get(0));
|
Assertions.assertTrue(sql.get(0).contains("FROM sys_catalog.sys_proc"), sql.get(0));
|
||||||
Assertions.assertTrue(sql.get(0).contains("p.prokind IN (?, ?)"), sql.get(0));
|
Assertions.assertTrue(sql.get(0).contains("p.prorettype = 2278"), sql.get(0));
|
||||||
|
Assertions.assertFalse(sql.get(0).contains("prokind"), sql.get(0));
|
||||||
Assertions.assertTrue(sql.get(0).contains("ORDER BY CASE object_type"), sql.get(0));
|
Assertions.assertTrue(sql.get(0).contains("ORDER BY CASE object_type"), sql.get(0));
|
||||||
Assertions.assertTrue(sql.get(0).endsWith("LIMIT 10"), sql.get(0));
|
Assertions.assertTrue(sql.get(0).endsWith("LIMIT 10"), sql.get(0));
|
||||||
}
|
}
|
||||||
@@ -246,6 +274,7 @@ class KingbaseAgentTest extends JdbcFakeExecutionBehaviorTest {
|
|||||||
Assertions.assertTrue(source.getSource().startsWith("CREATE FUNCTION public.format_name()"), source.getSource());
|
Assertions.assertTrue(source.getSource().startsWith("CREATE FUNCTION public.format_name()"), source.getSource());
|
||||||
Assertions.assertTrue(sql.get(0).contains("SELECT sys_get_functiondef(p.oid) AS source"), sql.get(0));
|
Assertions.assertTrue(sql.get(0).contains("SELECT sys_get_functiondef(p.oid) AS source"), sql.get(0));
|
||||||
Assertions.assertTrue(sql.get(0).contains("FROM sys_catalog.sys_proc"), sql.get(0));
|
Assertions.assertTrue(sql.get(0).contains("FROM sys_catalog.sys_proc"), sql.get(0));
|
||||||
|
Assertions.assertFalse(sql.get(0).contains("prokind"), sql.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -1392,6 +1392,7 @@ const databaseLabel = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const databasePlaceholder = computed(() => {
|
const databasePlaceholder = computed(() => {
|
||||||
|
if (form.value.db_type === "kingbase") return t("connection.databasePlaceholderRequired");
|
||||||
const fallback = defaultDatabaseForProfile();
|
const fallback = defaultDatabaseForProfile();
|
||||||
if (!fallback) return t("connection.databasePlaceholder");
|
if (!fallback) return t("connection.databasePlaceholder");
|
||||||
return t("connection.databasePlaceholderWithDefault", { database: fallback });
|
return t("connection.databasePlaceholderWithDefault", { database: fallback });
|
||||||
@@ -1430,7 +1431,7 @@ function defaultDatabaseForProfile() {
|
|||||||
if (selectedType.value === "cockroachdb") return "defaultdb";
|
if (selectedType.value === "cockroachdb") return "defaultdb";
|
||||||
if (form.value.db_type === "highgo") return "highgo";
|
if (form.value.db_type === "highgo") return "highgo";
|
||||||
if (form.value.db_type === "yashandb") return "yasdb";
|
if (form.value.db_type === "yashandb") return "yasdb";
|
||||||
if (form.value.db_type === "postgres" || form.value.db_type === "kingbase" || form.value.db_type === "vastbase") return "postgres";
|
if (form.value.db_type === "postgres" || form.value.db_type === "vastbase") return "postgres";
|
||||||
if (form.value.db_type === "sqlserver") return "master";
|
if (form.value.db_type === "sqlserver") return "master";
|
||||||
if (form.value.db_type === "oracle") return "ORCL";
|
if (form.value.db_type === "oracle") return "ORCL";
|
||||||
if (form.value.db_type === "h2" && h2ConnectionMode.value === "tcp") return "test";
|
if (form.value.db_type === "h2" && h2ConnectionMode.value === "tcp") return "test";
|
||||||
@@ -1912,8 +1913,9 @@ async function testConnection() {
|
|||||||
const runId = ++testRunId;
|
const runId = ++testRunId;
|
||||||
isTesting.value = true;
|
isTesting.value = true;
|
||||||
testResult.value = null;
|
testResult.value = null;
|
||||||
const config = connectionConfigForSubmit(editingId.value || draftTestConnectionId.value);
|
let config: ConnectionConfig | null = null;
|
||||||
try {
|
try {
|
||||||
|
config = connectionConfigForSubmit(editingId.value || draftTestConnectionId.value);
|
||||||
await ensureRequiredAgentDriverInstalled(config);
|
await ensureRequiredAgentDriverInstalled(config);
|
||||||
const msg = await testConnectionWithTimeout(config, runId);
|
const msg = await testConnectionWithTimeout(config, runId);
|
||||||
if (runId !== testRunId) return;
|
if (runId !== testRunId) return;
|
||||||
@@ -1924,10 +1926,11 @@ async function testConnection() {
|
|||||||
clearEditedConnectionErrorAfterSuccessfulTest();
|
clearEditedConnectionErrorAfterSuccessfulTest();
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (runId !== testRunId) return;
|
if (runId !== testRunId) return;
|
||||||
const message = connectionErrorWithDriverUpdateHint(config, mongodbAuthFailureHint(String(e)));
|
const rawMessage = mongodbAuthFailureHint(errorMessage(e));
|
||||||
const fallbackMessage = await tryNacosDockerConsoleFallback(config, message, runId);
|
const message = config ? connectionErrorWithDriverUpdateHint(config, rawMessage) : rawMessage;
|
||||||
|
const fallbackMessage = config ? await tryNacosDockerConsoleFallback(config, message, runId) : null;
|
||||||
if (runId !== testRunId) return;
|
if (runId !== testRunId) return;
|
||||||
const shouldShowSqlServerLegacyMode = !fallbackMessage && config.db_type === "sqlserver" && !isSqlServerLegacyUnencryptedMode(config.url_params) && isSqlServerTlsHandshakeFailure(message);
|
const shouldShowSqlServerLegacyMode = !fallbackMessage && config?.db_type === "sqlserver" && !isSqlServerLegacyUnencryptedMode(config.url_params) && isSqlServerTlsHandshakeFailure(message);
|
||||||
if (shouldShowSqlServerLegacyMode) {
|
if (shouldShowSqlServerLegacyMode) {
|
||||||
configTab.value = "advanced";
|
configTab.value = "advanced";
|
||||||
}
|
}
|
||||||
@@ -2091,6 +2094,12 @@ function connectionConfigForSubmit(id: string): ConnectionConfig {
|
|||||||
if (!config.name?.trim()) {
|
if (!config.name?.trim()) {
|
||||||
config.name = generateConnectionName();
|
config.name = generateConnectionName();
|
||||||
}
|
}
|
||||||
|
if (config.db_type === "kingbase") {
|
||||||
|
config.database = config.database?.trim() || undefined;
|
||||||
|
if (!config.database) {
|
||||||
|
throw new Error(t("connection.kingbaseDatabaseRequired"));
|
||||||
|
}
|
||||||
|
}
|
||||||
config.transport_layers = (config.transport_layers || []).map(normalizeTransportLayer);
|
config.transport_layers = (config.transport_layers || []).map(normalizeTransportLayer);
|
||||||
config.transport_layers = config.transport_layers.map((layer) => {
|
config.transport_layers = config.transport_layers.map((layer) => {
|
||||||
if (layer.type !== "ssh") return layer;
|
if (layer.type !== "ssh") return layer;
|
||||||
@@ -2556,12 +2565,12 @@ async function preloadVisibleDatabaseNames() {
|
|||||||
if (visibleDatabaseNames.value.length > 0) return;
|
if (visibleDatabaseNames.value.length > 0) return;
|
||||||
isLoadingVisibleDatabases.value = true;
|
isLoadingVisibleDatabases.value = true;
|
||||||
const draftId = buildDraftVisibleDatabasesConnectionId(uuid());
|
const draftId = buildDraftVisibleDatabasesConnectionId(uuid());
|
||||||
const draftConfig = {
|
|
||||||
...connectionConfigForSubmit(draftId),
|
|
||||||
id: draftId,
|
|
||||||
one_time: true,
|
|
||||||
};
|
|
||||||
try {
|
try {
|
||||||
|
const draftConfig = {
|
||||||
|
...connectionConfigForSubmit(draftId),
|
||||||
|
id: draftId,
|
||||||
|
one_time: true,
|
||||||
|
};
|
||||||
await api.connectDb(draftConfig);
|
await api.connectDb(draftConfig);
|
||||||
visibleDatabaseNames.value = await loadVisibleDatabaseNames(draftId, draftConfig);
|
visibleDatabaseNames.value = await loadVisibleDatabaseNames(draftId, draftConfig);
|
||||||
} catch {
|
} catch {
|
||||||
@@ -2580,13 +2589,13 @@ async function openVisibleDatabasesPicker() {
|
|||||||
visibleDatabaseError.value = "";
|
visibleDatabaseError.value = "";
|
||||||
visibleDatabaseSearchText.value = "";
|
visibleDatabaseSearchText.value = "";
|
||||||
const draftId = buildDraftVisibleDatabasesConnectionId(uuid());
|
const draftId = buildDraftVisibleDatabasesConnectionId(uuid());
|
||||||
const draftConfig = {
|
|
||||||
...connectionConfigForSubmit(draftId),
|
|
||||||
id: draftId,
|
|
||||||
one_time: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const draftConfig = {
|
||||||
|
...connectionConfigForSubmit(draftId),
|
||||||
|
id: draftId,
|
||||||
|
one_time: true,
|
||||||
|
};
|
||||||
await api.connectDb(draftConfig);
|
await api.connectDb(draftConfig);
|
||||||
const names = await loadVisibleDatabaseNames(draftId, draftConfig);
|
const names = await loadVisibleDatabaseNames(draftId, draftConfig);
|
||||||
visibleDatabaseNames.value = names;
|
visibleDatabaseNames.value = names;
|
||||||
@@ -2598,8 +2607,9 @@ async function openVisibleDatabasesPicker() {
|
|||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
visibleDatabaseNames.value = [];
|
visibleDatabaseNames.value = [];
|
||||||
visibleDatabaseSelection.value = new Set();
|
visibleDatabaseSelection.value = new Set();
|
||||||
visibleDatabaseError.value = mongodbAuthFailureHint(String(e?.message || e));
|
visibleDatabaseError.value = mongodbAuthFailureHint(errorMessage(e));
|
||||||
testResult.value = { ok: false, message: visibleDatabaseError.value };
|
testResult.value = { ok: false, message: visibleDatabaseError.value };
|
||||||
|
showVisibleDatabasesDialog.value = true;
|
||||||
} finally {
|
} finally {
|
||||||
await api.disconnectDb(draftId).catch(() => undefined);
|
await api.disconnectDb(draftId).catch(() => undefined);
|
||||||
isLoadingVisibleDatabases.value = false;
|
isLoadingVisibleDatabases.value = false;
|
||||||
|
|||||||
@@ -180,7 +180,9 @@ export default {
|
|||||||
password: "Password",
|
password: "Password",
|
||||||
database: "Database",
|
database: "Database",
|
||||||
databasePlaceholder: "Optional",
|
databasePlaceholder: "Optional",
|
||||||
|
databasePlaceholderRequired: "Required, enter an existing database",
|
||||||
databasePlaceholderWithDefault: "Optional, defaults to {database}",
|
databasePlaceholderWithDefault: "Optional, defaults to {database}",
|
||||||
|
kingbaseDatabaseRequired: "Kingbase requires an existing database before connecting or loading the database list.",
|
||||||
defaultDatabase: "Default DB",
|
defaultDatabase: "Default DB",
|
||||||
authDatabase: "Auth DB",
|
authDatabase: "Auth DB",
|
||||||
authDatabasePlaceholder: "Optional, often admin",
|
authDatabasePlaceholder: "Optional, often admin",
|
||||||
|
|||||||
@@ -182,7 +182,9 @@ export default withEnglishFallback({
|
|||||||
password: "Contraseña",
|
password: "Contraseña",
|
||||||
database: "Base de datos",
|
database: "Base de datos",
|
||||||
databasePlaceholder: "Opcional",
|
databasePlaceholder: "Opcional",
|
||||||
|
databasePlaceholderRequired: "Obligatorio, ingresa una base de datos existente",
|
||||||
databasePlaceholderWithDefault: "Opcional, por defecto {database}",
|
databasePlaceholderWithDefault: "Opcional, por defecto {database}",
|
||||||
|
kingbaseDatabaseRequired: "Kingbase requiere una base de datos existente antes de conectar o cargar la lista de bases de datos.",
|
||||||
defaultDatabase: "BD predeterminada",
|
defaultDatabase: "BD predeterminada",
|
||||||
authDatabase: "BD de autenticación",
|
authDatabase: "BD de autenticación",
|
||||||
authDatabasePlaceholder: "Opcional, normalmente admin",
|
authDatabasePlaceholder: "Opcional, normalmente admin",
|
||||||
|
|||||||
@@ -181,7 +181,9 @@ export default withEnglishFallback({
|
|||||||
password: "Password",
|
password: "Password",
|
||||||
database: "Database",
|
database: "Database",
|
||||||
databasePlaceholder: "Opzionale",
|
databasePlaceholder: "Opzionale",
|
||||||
|
databasePlaceholderRequired: "Obbligatorio, inserisci un database esistente",
|
||||||
databasePlaceholderWithDefault: "Opzionale, predefinito {database}",
|
databasePlaceholderWithDefault: "Opzionale, predefinito {database}",
|
||||||
|
kingbaseDatabaseRequired: "Kingbase richiede un database esistente prima di connettersi o caricare l'elenco dei database.",
|
||||||
defaultDatabase: "DB Predefinito",
|
defaultDatabase: "DB Predefinito",
|
||||||
authDatabase: "DB di Autenticazione",
|
authDatabase: "DB di Autenticazione",
|
||||||
authDatabasePlaceholder: "Opzionale, spesso admin",
|
authDatabasePlaceholder: "Opzionale, spesso admin",
|
||||||
|
|||||||
@@ -181,7 +181,9 @@ export default withEnglishFallback({
|
|||||||
password: "パスワード",
|
password: "パスワード",
|
||||||
database: "データベース",
|
database: "データベース",
|
||||||
databasePlaceholder: "任意",
|
databasePlaceholder: "任意",
|
||||||
|
databasePlaceholderRequired: "必須、既存のデータベースを入力",
|
||||||
databasePlaceholderWithDefault: "任意、デフォルトは {database}",
|
databasePlaceholderWithDefault: "任意、デフォルトは {database}",
|
||||||
|
kingbaseDatabaseRequired: "Kingbase は接続またはデータベース一覧の読み込み前に、既存のデータベース名が必要です。",
|
||||||
defaultDatabase: "デフォルトDB",
|
defaultDatabase: "デフォルトDB",
|
||||||
authDatabase: "認証DB",
|
authDatabase: "認証DB",
|
||||||
authDatabasePlaceholder: "任意、通常は admin",
|
authDatabasePlaceholder: "任意、通常は admin",
|
||||||
|
|||||||
@@ -182,7 +182,9 @@ export default withEnglishFallback({
|
|||||||
password: "Senha",
|
password: "Senha",
|
||||||
database: "Banco de dados",
|
database: "Banco de dados",
|
||||||
databasePlaceholder: "Opcional",
|
databasePlaceholder: "Opcional",
|
||||||
|
databasePlaceholderRequired: "Obrigatório, informe um banco de dados existente",
|
||||||
databasePlaceholderWithDefault: "Opcional, padrão é {database}",
|
databasePlaceholderWithDefault: "Opcional, padrão é {database}",
|
||||||
|
kingbaseDatabaseRequired: "Kingbase exige um banco de dados existente antes de conectar ou carregar a lista de bancos de dados.",
|
||||||
defaultDatabase: "BD padrão",
|
defaultDatabase: "BD padrão",
|
||||||
authDatabase: "BD de autenticação",
|
authDatabase: "BD de autenticação",
|
||||||
authDatabasePlaceholder: "Opcional, geralmente admin",
|
authDatabasePlaceholder: "Opcional, geralmente admin",
|
||||||
|
|||||||
@@ -182,7 +182,9 @@ export default withEnglishFallback({
|
|||||||
password: "密码",
|
password: "密码",
|
||||||
database: "数据库",
|
database: "数据库",
|
||||||
databasePlaceholder: "可选",
|
databasePlaceholder: "可选",
|
||||||
|
databasePlaceholderRequired: "必填,请填写已存在的数据库",
|
||||||
databasePlaceholderWithDefault: "可选,默认 {database}",
|
databasePlaceholderWithDefault: "可选,默认 {database}",
|
||||||
|
kingbaseDatabaseRequired: "Kingbase 需要先填写一个已存在的数据库后才能连接或加载数据库列表。",
|
||||||
defaultDatabase: "默认库",
|
defaultDatabase: "默认库",
|
||||||
authDatabase: "认证库",
|
authDatabase: "认证库",
|
||||||
authDatabasePlaceholder: "可选,通常为 admin",
|
authDatabasePlaceholder: "可选,通常为 admin",
|
||||||
|
|||||||
@@ -182,7 +182,9 @@ export default withEnglishFallback({
|
|||||||
password: "密碼",
|
password: "密碼",
|
||||||
database: "資料庫",
|
database: "資料庫",
|
||||||
databasePlaceholder: "可選",
|
databasePlaceholder: "可選",
|
||||||
|
databasePlaceholderRequired: "必填,請填寫已存在的資料庫",
|
||||||
databasePlaceholderWithDefault: "可選,預設 {database}",
|
databasePlaceholderWithDefault: "可選,預設 {database}",
|
||||||
|
kingbaseDatabaseRequired: "Kingbase 需要先填寫一個已存在的資料庫後才能連線或載入資料庫清單。",
|
||||||
defaultDatabase: "預設 DB",
|
defaultDatabase: "預設 DB",
|
||||||
authDatabase: "驗證 DB",
|
authDatabase: "驗證 DB",
|
||||||
authDatabasePlaceholder: "可選,通常為 admin",
|
authDatabasePlaceholder: "可選,通常為 admin",
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { getDatabaseUserAdminProvider, kingbaseShowGrantsSql } from "@/lib/database/databaseUserAdmin";
|
||||||
|
|
||||||
|
describe("database user admin providers", () => {
|
||||||
|
it("uses sys_catalog for Kingbase role metadata", () => {
|
||||||
|
const provider = getDatabaseUserAdminProvider("kingbase");
|
||||||
|
|
||||||
|
expect(provider).not.toBeNull();
|
||||||
|
expect(provider?.dialect).toBe("postgres");
|
||||||
|
expect(provider?.listUsersSql()).toContain("FROM sys_catalog.sys_roles r");
|
||||||
|
expect(provider?.listUsersSql()).not.toContain("pg_catalog");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("builds Kingbase grant SQL without PostgreSQL catalog tables", () => {
|
||||||
|
const sql = kingbaseShowGrantsSql({ user: "role'o", host: "LOGIN" });
|
||||||
|
|
||||||
|
expect(sql).toContain("FROM sys_catalog.sys_roles r");
|
||||||
|
expect(sql).toContain("FROM sys_catalog.sys_auth_members m");
|
||||||
|
expect(sql).toContain("CROSS JOIN sys_catalog.sys_database d");
|
||||||
|
expect(sql).toContain("CROSS JOIN sys_catalog.sys_namespace n");
|
||||||
|
expect(sql).toContain("WHERE r.rolname = 'role''o'");
|
||||||
|
expect(sql).not.toContain("pg_catalog");
|
||||||
|
expect(sql).not.toContain("pg_roles");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -46,7 +46,8 @@ export interface DatabaseUserAdminProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const MYSQL_USER_ADMIN_TYPES = new Set<DatabaseType>(["mysql", "goldendb"]);
|
export const MYSQL_USER_ADMIN_TYPES = new Set<DatabaseType>(["mysql", "goldendb"]);
|
||||||
export const POSTGRES_USER_ADMIN_TYPES = new Set<DatabaseType>(["postgres", "gaussdb", "highgo", "kingbase", "kwdb", "opengauss", "questdb", "vastbase"]);
|
export const KINGBASE_USER_ADMIN_TYPES = new Set<DatabaseType>(["kingbase"]);
|
||||||
|
export const POSTGRES_USER_ADMIN_TYPES = new Set<DatabaseType>(["postgres", "gaussdb", "highgo", "kwdb", "opengauss", "questdb", "vastbase"]);
|
||||||
|
|
||||||
export const MYSQL_COMMON_PRIVILEGES = ["SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "ALTER", "INDEX", "REFERENCES", "EXECUTE", "SHOW VIEW", "TRIGGER", "EVENT", "CREATE TEMPORARY TABLES"] as const;
|
export const MYSQL_COMMON_PRIVILEGES = ["SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "ALTER", "INDEX", "REFERENCES", "EXECUTE", "SHOW VIEW", "TRIGGER", "EVENT", "CREATE TEMPORARY TABLES"] as const;
|
||||||
|
|
||||||
@@ -61,6 +62,7 @@ export function supportsDatabaseUserAdmin(dbType: DatabaseType | undefined): boo
|
|||||||
export function getDatabaseUserAdminProvider(dbType: DatabaseType | undefined): DatabaseUserAdminProvider | null {
|
export function getDatabaseUserAdminProvider(dbType: DatabaseType | undefined): DatabaseUserAdminProvider | null {
|
||||||
if (!dbType) return null;
|
if (!dbType) return null;
|
||||||
if (MYSQL_USER_ADMIN_TYPES.has(dbType)) return mysqlUserAdminProvider;
|
if (MYSQL_USER_ADMIN_TYPES.has(dbType)) return mysqlUserAdminProvider;
|
||||||
|
if (KINGBASE_USER_ADMIN_TYPES.has(dbType)) return kingbaseUserAdminProvider;
|
||||||
if (POSTGRES_USER_ADMIN_TYPES.has(dbType)) return postgresUserAdminProvider;
|
if (POSTGRES_USER_ADMIN_TYPES.has(dbType)) return postgresUserAdminProvider;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -257,6 +259,91 @@ FROM (
|
|||||||
ORDER BY sort, line;`.trim();
|
ORDER BY sort, line;`.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function kingbaseListRolesSql(): string {
|
||||||
|
return `
|
||||||
|
SELECT
|
||||||
|
r.rolname AS user,
|
||||||
|
CASE WHEN r.rolcanlogin THEN 'LOGIN' ELSE 'ROLE' END AS host,
|
||||||
|
concat_ws(', ',
|
||||||
|
CASE WHEN r.rolsuper THEN 'SUPERUSER' END,
|
||||||
|
CASE WHEN r.rolcreatedb THEN 'CREATEDB' END,
|
||||||
|
CASE WHEN r.rolcreaterole THEN 'CREATEROLE' END,
|
||||||
|
CASE WHEN r.rolreplication THEN 'REPLICATION' END,
|
||||||
|
CASE WHEN r.rolbypassrls THEN 'BYPASSRLS' END
|
||||||
|
) AS plugin
|
||||||
|
FROM sys_catalog.sys_roles r
|
||||||
|
ORDER BY r.rolname;`.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function kingbaseShowGrantsSql(user: DatabaseUserIdentity): string {
|
||||||
|
const role = quoteSqlString(user.user);
|
||||||
|
return `
|
||||||
|
WITH target AS (
|
||||||
|
SELECT oid, rolname, rolsuper, rolcreatedb, rolcreaterole, rolcanlogin, rolreplication, rolbypassrls
|
||||||
|
FROM sys_catalog.sys_roles r
|
||||||
|
WHERE r.rolname = ${role}
|
||||||
|
)
|
||||||
|
SELECT line
|
||||||
|
FROM (
|
||||||
|
SELECT 1 AS sort, 'Role: ' || quote_ident(rolname) AS line
|
||||||
|
FROM target
|
||||||
|
UNION ALL
|
||||||
|
SELECT 2, 'Attributes: ' || COALESCE(NULLIF(concat_ws(', ',
|
||||||
|
CASE WHEN rolsuper THEN 'SUPERUSER' END,
|
||||||
|
CASE WHEN rolcreatedb THEN 'CREATEDB' END,
|
||||||
|
CASE WHEN rolcreaterole THEN 'CREATEROLE' END,
|
||||||
|
CASE WHEN rolcanlogin THEN 'LOGIN' ELSE 'NOLOGIN' END,
|
||||||
|
CASE WHEN rolreplication THEN 'REPLICATION' END,
|
||||||
|
CASE WHEN rolbypassrls THEN 'BYPASSRLS' END
|
||||||
|
), ''), 'none')
|
||||||
|
FROM target
|
||||||
|
UNION ALL
|
||||||
|
SELECT 10, 'Member of: ' || quote_ident(parent.rolname) || CASE WHEN m.admin_option THEN ' WITH ADMIN OPTION' ELSE '' END
|
||||||
|
FROM sys_catalog.sys_auth_members m
|
||||||
|
JOIN target t ON t.oid = m.member
|
||||||
|
JOIN sys_catalog.sys_roles parent ON parent.oid = m.roleid
|
||||||
|
UNION ALL
|
||||||
|
SELECT 20, 'Has member: ' || quote_ident(member.rolname) || CASE WHEN m.admin_option THEN ' WITH ADMIN OPTION' ELSE '' END
|
||||||
|
FROM sys_catalog.sys_auth_members m
|
||||||
|
JOIN target t ON t.oid = m.roleid
|
||||||
|
JOIN sys_catalog.sys_roles member ON member.oid = m.member
|
||||||
|
UNION ALL
|
||||||
|
SELECT 30, 'Database: ' || quote_ident(d.datname) || ' = ' ||
|
||||||
|
concat_ws(', ',
|
||||||
|
CASE WHEN has_database_privilege(t.rolname, d.oid, 'CONNECT') THEN 'CONNECT' END,
|
||||||
|
CASE WHEN has_database_privilege(t.rolname, d.oid, 'CREATE') THEN 'CREATE' END,
|
||||||
|
CASE WHEN has_database_privilege(t.rolname, d.oid, 'TEMPORARY') THEN 'TEMPORARY' END
|
||||||
|
)
|
||||||
|
FROM target t
|
||||||
|
CROSS JOIN sys_catalog.sys_database d
|
||||||
|
WHERE has_database_privilege(t.rolname, d.oid, 'CONNECT')
|
||||||
|
OR has_database_privilege(t.rolname, d.oid, 'CREATE')
|
||||||
|
OR has_database_privilege(t.rolname, d.oid, 'TEMPORARY')
|
||||||
|
UNION ALL
|
||||||
|
SELECT 40, 'Schema: ' || quote_ident(n.nspname) || ' = ' ||
|
||||||
|
concat_ws(', ',
|
||||||
|
CASE WHEN has_schema_privilege(t.rolname, n.oid, 'USAGE') THEN 'USAGE' END,
|
||||||
|
CASE WHEN has_schema_privilege(t.rolname, n.oid, 'CREATE') THEN 'CREATE' END
|
||||||
|
)
|
||||||
|
FROM target t
|
||||||
|
CROSS JOIN sys_catalog.sys_namespace n
|
||||||
|
WHERE UPPER(n.nspname) <> 'INFORMATION_SCHEMA'
|
||||||
|
AND UPPER(n.nspname) NOT LIKE 'SYS%'
|
||||||
|
AND UPPER(n.nspname) NOT LIKE 'XLOG%'
|
||||||
|
AND (has_schema_privilege(t.rolname, n.oid, 'USAGE') OR has_schema_privilege(t.rolname, n.oid, 'CREATE'))
|
||||||
|
UNION ALL
|
||||||
|
SELECT 50, 'Table: ' || quote_ident(table_schema) || '.' || quote_ident(table_name) || ' = ' ||
|
||||||
|
string_agg(privilege_type || CASE WHEN is_grantable = 'YES' THEN ' WITH GRANT OPTION' ELSE '' END, ', ' ORDER BY privilege_type)
|
||||||
|
FROM information_schema.role_table_grants
|
||||||
|
WHERE grantee = ${role}
|
||||||
|
AND UPPER(table_schema) <> 'INFORMATION_SCHEMA'
|
||||||
|
AND UPPER(table_schema) NOT LIKE 'SYS%'
|
||||||
|
AND UPPER(table_schema) NOT LIKE 'XLOG%'
|
||||||
|
GROUP BY table_schema, table_name
|
||||||
|
) grants
|
||||||
|
ORDER BY sort, line;`.trim();
|
||||||
|
}
|
||||||
|
|
||||||
export function postgresCreateRoleSql(input: CreatePrincipalInput): string {
|
export function postgresCreateRoleSql(input: CreatePrincipalInput): string {
|
||||||
const login = input.canLogin === false ? "NOLOGIN" : "LOGIN";
|
const login = input.canLogin === false ? "NOLOGIN" : "LOGIN";
|
||||||
return `CREATE ROLE ${quotePostgresIdentifier(input.user)} ${login} PASSWORD ${quoteSqlString(input.password)};`;
|
return `CREATE ROLE ${quotePostgresIdentifier(input.user)} ${login} PASSWORD ${quoteSqlString(input.password)};`;
|
||||||
@@ -373,3 +460,9 @@ export const postgresUserAdminProvider: DatabaseUserAdminProvider = {
|
|||||||
privilegesForScope: postgresPrivilegesForScope,
|
privilegesForScope: postgresPrivilegesForScope,
|
||||||
defaultPrivilegesForScope: (scope) => (scope === "role" ? [] : [postgresDefaultPrivilege(scope)]),
|
defaultPrivilegesForScope: (scope) => (scope === "role" ? [] : [postgresDefaultPrivilege(scope)]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const kingbaseUserAdminProvider: DatabaseUserAdminProvider = {
|
||||||
|
...postgresUserAdminProvider,
|
||||||
|
listUsersSql: kingbaseListRolesSql,
|
||||||
|
showGrantsSql: kingbaseShowGrantsSql,
|
||||||
|
};
|
||||||
@@ -676,7 +676,7 @@ impl ConnectionConfig {
|
|||||||
DatabaseType::Rqlite | DatabaseType::Turso => Some("main"),
|
DatabaseType::Rqlite | DatabaseType::Turso => Some("main"),
|
||||||
DatabaseType::Gaussdb | DatabaseType::OpenGauss => Some("postgres"),
|
DatabaseType::Gaussdb | DatabaseType::OpenGauss => Some("postgres"),
|
||||||
DatabaseType::Kwdb => Some("defaultdb"),
|
DatabaseType::Kwdb => Some("defaultdb"),
|
||||||
DatabaseType::Kingbase | DatabaseType::Vastbase => Some("postgres"),
|
DatabaseType::Vastbase => Some("postgres"),
|
||||||
DatabaseType::Highgo => Some("highgo"),
|
DatabaseType::Highgo => Some("highgo"),
|
||||||
DatabaseType::Yashandb => Some("yasdb"),
|
DatabaseType::Yashandb => Some("yasdb"),
|
||||||
DatabaseType::Oscar => Some("osrdb"),
|
DatabaseType::Oscar => Some("osrdb"),
|
||||||
@@ -2153,6 +2153,26 @@ mod tests {
|
|||||||
assert_eq!(config.effective_database(), Some("default"));
|
assert_eq!(config.effective_database(), Some("default"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn kingbase_empty_database_has_no_default() {
|
||||||
|
let mut config = mysql_config("SYSTEM", "secret", None);
|
||||||
|
config.db_type = DatabaseType::Kingbase;
|
||||||
|
config.port = 54321;
|
||||||
|
|
||||||
|
assert_eq!(config.effective_database(), None);
|
||||||
|
assert_eq!(config.connection_url(), "kingbase://SYSTEM:secret@10.1.2.3:54321");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vastbase_empty_database_uses_postgres_for_connection() {
|
||||||
|
let mut config = mysql_config("vastbase", "secret", None);
|
||||||
|
config.db_type = DatabaseType::Vastbase;
|
||||||
|
config.port = 5432;
|
||||||
|
|
||||||
|
assert_eq!(config.effective_database(), Some("postgres"));
|
||||||
|
assert_eq!(config.connection_url(), "vastbase://vastbase:secret@10.1.2.3:5432/postgres");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn clickhouse_tls_uses_https_from_ssl_or_secure_param() {
|
fn clickhouse_tls_uses_https_from_ssl_or_secure_param() {
|
||||||
let mut config = mysql_config("default", "", None);
|
let mut config = mysql_config("default", "", None);
|
||||||
|
|||||||
Reference in new issue
Block a user