fix(gbase): load object source definitions

This commit is contained in:
t8y2 2026-07-10 17:08:42 +08:00
parent d3ac9805a4
commit 2bfa4c3234
2 changed files with 126 additions and 1 deletions

View File

@ -2,10 +2,12 @@ package com.dbx.agent.gbase8a;
import com.dbx.agent.ConfiguredJdbcAgent;
import com.dbx.agent.ExecuteQueryOptions;
import com.dbx.agent.JdbcIdentifiers;
import com.dbx.agent.JdbcAgentProfile;
import com.dbx.agent.JsonRpcServer;
import com.dbx.agent.MetadataListConstraints;
import com.dbx.agent.ObjectInfo;
import com.dbx.agent.ObjectSource;
import com.dbx.agent.QueryPageOptions;
import com.dbx.agent.QueryPageResult;
import com.dbx.agent.QueryResult;
@ -14,6 +16,8 @@ import com.dbx.agent.TableInfo;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@ -31,7 +35,7 @@ public final class Gbase8aAgent extends ConfiguredJdbcAgent {
"USE",
true,
false,
false,
true,
false
);
@ -166,6 +170,40 @@ public final class Gbase8aAgent extends ConfiguredJdbcAgent {
}
}
@Override
public ObjectSource getObjectSource(String schema, String name, String objectType) {
return unchecked(() -> {
String normalizedType = objectType.toUpperCase(Locale.ROOT);
String sql = switch (normalizedType) {
case "VIEW" -> "SHOW CREATE VIEW ";
case "PROCEDURE" -> "SHOW CREATE PROCEDURE ";
case "FUNCTION" -> "SHOW CREATE FUNCTION ";
default -> throw new IllegalArgumentException("Unsupported object type: " + objectType);
};
String qualifiedName = hasSchema(schema)
? JdbcIdentifiers.INSTANCE.backtick(schema) + "." + JdbcIdentifiers.INSTANCE.backtick(name)
: JdbcIdentifiers.INSTANCE.backtick(name);
String source = "";
try (Statement stmt = requireConnection().createStatement();
ResultSet rs = stmt.executeQuery(sql + qualifiedName)) {
if (rs.next()) {
// SHOW CREATE result layouts vary by GBase driver version, so locate the definition column by label.
ResultSetMetaData metadata = rs.getMetaData();
for (int index = 1; index <= metadata.getColumnCount(); index++) {
String label = metadata.getColumnLabel(index);
if (label != null && label.toUpperCase(Locale.ROOT).startsWith("CREATE ")) {
String value = rs.getString(index);
source = value == null ? "" : value;
break;
}
}
}
}
return new ObjectSource(name, normalizedType, schema, source);
});
}
private static void appendGbase8aTableTypePredicate(
StringBuilder sql,
List<Object> args,

View File

@ -2,6 +2,7 @@ package com.dbx.agent.gbase8a;
import com.dbx.agent.MetadataListConstraints;
import com.dbx.agent.ObjectInfo;
import com.dbx.agent.ObjectSource;
import com.dbx.agent.TableInfo;
import com.dbx.agent.test.TestSupport;
import org.junit.jupiter.api.Assertions;
@ -13,6 +14,8 @@ import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
@ -95,6 +98,57 @@ class Gbase8aAgentTest {
Assertions.assertTrue(sql.get(0).contains("LIMIT ?"), sql.get(0));
}
@Test
void getObjectSourceUsesShowCreateForRoutines() {
List<String> sql = new ArrayList<>();
Gbase8aAgent agent = new Gbase8aAgent();
TestSupport.setPrivateConnection(agent, statementConnection(sql, resultSet(
new String[]{"Procedure", "sql_mode", "Create Procedure"},
new Object[][]{{"refresh_orders", "", "CREATE PROCEDURE `refresh_orders`() SELECT 1"}}
)));
ObjectSource source = agent.getObjectSource("app", "refresh_orders", "procedure");
Assertions.assertEquals("PROCEDURE", source.getObject_type());
Assertions.assertEquals("app", source.getSchema());
Assertions.assertEquals("CREATE PROCEDURE `refresh_orders`() SELECT 1", source.getSource());
Assertions.assertEquals("SHOW CREATE PROCEDURE `app`.`refresh_orders`", sql.get(0));
}
@Test
void getObjectSourceFindsDefinitionAcrossDriverResultLayouts() {
List<String> sql = new ArrayList<>();
Gbase8aAgent agent = new Gbase8aAgent();
TestSupport.setPrivateConnection(agent, statementConnection(sql, resultSet(
new String[]{"Function", "Create Function", "character_set_client", "Database Collation"},
new Object[][]{{"format_order", "CREATE FUNCTION `format_order`() RETURNS INT RETURN 1", "utf8", "utf8_bin"}}
)));
ObjectSource source = agent.getObjectSource("app`prod", "format`order", "FUNCTION");
Assertions.assertEquals("CREATE FUNCTION `format_order`() RETURNS INT RETURN 1", source.getSource());
Assertions.assertEquals("SHOW CREATE FUNCTION `app``prod`.`format``order`", sql.get(0));
}
@Test
void getObjectSourceSupportsViewsAndRejectsOtherObjectTypes() {
List<String> sql = new ArrayList<>();
Gbase8aAgent agent = new Gbase8aAgent();
TestSupport.setPrivateConnection(agent, statementConnection(sql, resultSet(
new String[]{"View", "Create View"},
new Object[][]{{"active_orders", "CREATE VIEW `active_orders` AS SELECT 1"}}
)));
ObjectSource source = agent.getObjectSource("app", "active_orders", "VIEW");
Assertions.assertEquals("CREATE VIEW `active_orders` AS SELECT 1", source.getSource());
Assertions.assertEquals("SHOW CREATE VIEW `app`.`active_orders`", sql.get(0));
Assertions.assertThrows(
IllegalArgumentException.class,
() -> agent.getObjectSource("app", "orders", "TABLE")
);
}
private static Connection preparedConnection(List<String> sql, ResultSet... resultSets) {
int[] resultSetIndex = {0};
PreparedStatement statement = proxy(PreparedStatement.class, (method, args) -> {
@ -120,8 +174,39 @@ class Gbase8aAgentTest {
});
}
private static Connection statementConnection(List<String> sql, ResultSet resultSet) {
Statement statement = proxy(Statement.class, (method, args) -> {
if ("executeQuery".equals(method.getName())) {
sql.add(String.valueOf(args[0]));
return resultSet;
}
if ("close".equals(method.getName())) {
return null;
}
return defaultValue(method.getReturnType());
});
return proxy(Connection.class, (method, args) -> {
if ("createStatement".equals(method.getName())) {
return statement;
}
if ("isClosed".equals(method.getName())) {
return false;
}
return defaultValue(method.getReturnType());
});
}
private static ResultSet resultSet(String[] columns, Object[][] rows) {
int[] index = {-1};
ResultSetMetaData metadata = proxy(ResultSetMetaData.class, (method, args) -> {
if ("getColumnCount".equals(method.getName())) {
return columns.length;
}
if ("getColumnLabel".equals(method.getName())) {
return columns[((Number) args[0]).intValue() - 1];
}
return defaultValue(method.getReturnType());
});
return proxy(ResultSet.class, (method, args) -> {
switch (method.getName()) {
case "next":
@ -130,6 +215,8 @@ class Gbase8aAgentTest {
case "getString":
Object value = columnValue(columns, rows[index[0]], args[0]);
return value == null ? null : String.valueOf(value);
case "getMetaData":
return metadata;
case "close":
return null;
default: