fix(gbase): load routines in object list
This commit is contained in:
1 parent
0894334764
commit
d64299b59e
2 files changed
+175
No files matched your search
@@ -4,9 +4,11 @@ import com.dbx.agent.ConfiguredJdbcAgent;
|
|||||||
import com.dbx.agent.ExecuteQueryOptions;
|
import com.dbx.agent.ExecuteQueryOptions;
|
||||||
import com.dbx.agent.JdbcAgentProfile;
|
import com.dbx.agent.JdbcAgentProfile;
|
||||||
import com.dbx.agent.JsonRpcServer;
|
import com.dbx.agent.JsonRpcServer;
|
||||||
|
import com.dbx.agent.ObjectInfo;
|
||||||
import com.dbx.agent.QueryPageOptions;
|
import com.dbx.agent.QueryPageOptions;
|
||||||
import com.dbx.agent.QueryPageResult;
|
import com.dbx.agent.QueryPageResult;
|
||||||
import com.dbx.agent.QueryResult;
|
import com.dbx.agent.QueryResult;
|
||||||
|
import com.dbx.agent.StandardJdbcMetadata;
|
||||||
import com.dbx.agent.TableInfo;
|
import com.dbx.agent.TableInfo;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
@@ -14,6 +16,7 @@ import java.sql.ResultSet;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
public final class Gbase8aAgent extends ConfiguredJdbcAgent {
|
public final class Gbase8aAgent extends ConfiguredJdbcAgent {
|
||||||
public static final JdbcAgentProfile GBASE8A_PROFILE = new JdbcAgentProfile(
|
public static final JdbcAgentProfile GBASE8A_PROFILE = new JdbcAgentProfile(
|
||||||
@@ -79,6 +82,49 @@ public final class Gbase8aAgent extends ConfiguredJdbcAgent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ObjectInfo> listObjects(String schema) {
|
||||||
|
return unchecked(() -> {
|
||||||
|
List<ObjectInfo> result = StandardJdbcMetadata.INSTANCE.listObjects(listTables(schema), schema);
|
||||||
|
// GBase 8a's table metadata does not surface routines, so sidebar objects must load them explicitly.
|
||||||
|
appendRoutines(result, schema);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendRoutines(List<ObjectInfo> result, String schema) throws Exception {
|
||||||
|
String sql;
|
||||||
|
boolean hasSchema = schema != null && !schema.trim().isEmpty();
|
||||||
|
if (hasSchema) {
|
||||||
|
sql = "SELECT ROUTINE_NAME, ROUTINE_TYPE, ROUTINE_COMMENT FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = ? ORDER BY ROUTINE_NAME";
|
||||||
|
} else {
|
||||||
|
sql = "SELECT ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_TYPE, ROUTINE_COMMENT FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA NOT IN ('information_schema', 'performance_schema', 'gclusterdb', 'gctmpdb') ORDER BY ROUTINE_SCHEMA, ROUTINE_NAME";
|
||||||
|
}
|
||||||
|
try (PreparedStatement stmt = requireConnection().prepareStatement(sql)) {
|
||||||
|
if (hasSchema) {
|
||||||
|
stmt.setString(1, schema);
|
||||||
|
}
|
||||||
|
try (ResultSet rs = stmt.executeQuery()) {
|
||||||
|
while (rs.next()) {
|
||||||
|
String routineSchema = hasSchema ? schema : rs.getString("ROUTINE_SCHEMA");
|
||||||
|
result.add(new ObjectInfo(
|
||||||
|
rs.getString("ROUTINE_NAME"),
|
||||||
|
normalizeRoutineType(rs.getString("ROUTINE_TYPE")),
|
||||||
|
routineSchema,
|
||||||
|
rs.getString("ROUTINE_COMMENT")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String normalizeRoutineType(String routineType) {
|
||||||
|
if (routineType == null || routineType.trim().isEmpty()) {
|
||||||
|
return "PROCEDURE";
|
||||||
|
}
|
||||||
|
return routineType.trim().toUpperCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
private static ExecuteQueryOptions withoutFetchSize(ExecuteQueryOptions options) {
|
private static ExecuteQueryOptions withoutFetchSize(ExecuteQueryOptions options) {
|
||||||
return new ExecuteQueryOptions(options.getMaxRows(), null, options.getTimeoutSecs());
|
return new ExecuteQueryOptions(options.getMaxRows(), null, options.getTimeoutSecs());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package com.dbx.agent.gbase8a;
|
||||||
|
|
||||||
|
import com.dbx.agent.ObjectInfo;
|
||||||
|
import com.dbx.agent.test.TestSupport;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class Gbase8aAgentTest {
|
||||||
|
@Test
|
||||||
|
void listObjectsIncludesRoutinesFromInformationSchema() {
|
||||||
|
List<String> sql = new ArrayList<>();
|
||||||
|
Gbase8aAgent agent = new Gbase8aAgent();
|
||||||
|
TestSupport.setPrivateConnection(agent, preparedConnection(sql,
|
||||||
|
resultSet(
|
||||||
|
new String[]{"TABLE_NAME", "TABLE_TYPE"},
|
||||||
|
new Object[][]{{"orders", "BASE TABLE"}}
|
||||||
|
),
|
||||||
|
resultSet(
|
||||||
|
new String[]{"ROUTINE_NAME", "ROUTINE_TYPE", "ROUTINE_COMMENT"},
|
||||||
|
new Object[][]{{"refresh_orders", "PROCEDURE", "proc comment"}, {"format_order", "FUNCTION", null}}
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
List<ObjectInfo> objects = agent.listObjects("app");
|
||||||
|
|
||||||
|
Assertions.assertEquals(3, objects.size());
|
||||||
|
Assertions.assertEquals("orders", objects.get(0).getName());
|
||||||
|
Assertions.assertEquals("TABLE", objects.get(0).getObject_type());
|
||||||
|
Assertions.assertEquals("refresh_orders", objects.get(1).getName());
|
||||||
|
Assertions.assertEquals("PROCEDURE", objects.get(1).getObject_type());
|
||||||
|
Assertions.assertEquals("app", objects.get(1).getSchema());
|
||||||
|
Assertions.assertEquals("proc comment", objects.get(1).getComment());
|
||||||
|
Assertions.assertEquals("format_order", objects.get(2).getName());
|
||||||
|
Assertions.assertEquals("FUNCTION", objects.get(2).getObject_type());
|
||||||
|
Assertions.assertEquals("app", objects.get(2).getSchema());
|
||||||
|
Assertions.assertTrue(sql.get(1).contains("FROM information_schema.ROUTINES"), sql.get(1));
|
||||||
|
Assertions.assertTrue(sql.get(1).contains("ROUTINE_SCHEMA = ?"), sql.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Connection preparedConnection(List<String> sql, ResultSet... resultSets) {
|
||||||
|
int[] resultSetIndex = {0};
|
||||||
|
PreparedStatement statement = proxy(PreparedStatement.class, (method, args) -> {
|
||||||
|
if ("executeQuery".equals(method.getName())) {
|
||||||
|
int current = Math.min(resultSetIndex[0], resultSets.length - 1);
|
||||||
|
resultSetIndex[0] += 1;
|
||||||
|
return resultSets[current];
|
||||||
|
}
|
||||||
|
if ("setString".equals(method.getName()) || "close".equals(method.getName())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return defaultValue(method.getReturnType());
|
||||||
|
});
|
||||||
|
return proxy(Connection.class, (method, args) -> {
|
||||||
|
if ("prepareStatement".equals(method.getName())) {
|
||||||
|
sql.add(String.valueOf(args[0]));
|
||||||
|
return statement;
|
||||||
|
}
|
||||||
|
if ("isClosed".equals(method.getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return defaultValue(method.getReturnType());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResultSet resultSet(String[] columns, Object[][] rows) {
|
||||||
|
int[] index = {-1};
|
||||||
|
return proxy(ResultSet.class, (method, args) -> {
|
||||||
|
switch (method.getName()) {
|
||||||
|
case "next":
|
||||||
|
index[0] += 1;
|
||||||
|
return index[0] < rows.length;
|
||||||
|
case "getString":
|
||||||
|
Object value = columnValue(columns, rows[index[0]], args[0]);
|
||||||
|
return value == null ? null : String.valueOf(value);
|
||||||
|
case "close":
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
return defaultValue(method.getReturnType());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object columnValue(String[] columns, Object[] row, Object key) {
|
||||||
|
if (key instanceof Number) {
|
||||||
|
return row[((Number) key).intValue() - 1];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < columns.length; i++) {
|
||||||
|
if (columns[i].equalsIgnoreCase(String.valueOf(key))) {
|
||||||
|
return row[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T proxy(Class<T> type, MethodHandler handler) {
|
||||||
|
InvocationHandler invocationHandler = new InvocationHandler() {
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
return handler.handle(method, args == null ? new Object[0] : args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return type.cast(Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[]{type}, invocationHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object defaultValue(Class<?> type) {
|
||||||
|
if (type == Boolean.TYPE) return false;
|
||||||
|
if (type == Byte.TYPE) return (byte) 0;
|
||||||
|
if (type == Short.TYPE) return (short) 0;
|
||||||
|
if (type == Integer.TYPE) return 0;
|
||||||
|
if (type == Long.TYPE) return 0L;
|
||||||
|
if (type == Float.TYPE) return 0f;
|
||||||
|
if (type == Double.TYPE) return 0d;
|
||||||
|
if (type == Character.TYPE) return (char) 0;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface MethodHandler {
|
||||||
|
Object handle(Method method, Object[] args) throws Throwable;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user