mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-20 00:17:44 +00:00
feat(saphana): open stored procedure source
This commit is contained in:
@@ -3,8 +3,12 @@ package com.dbx.agent.saphana;
|
||||
import com.dbx.agent.ConfiguredJdbcAgent;
|
||||
import com.dbx.agent.JdbcAgentProfile;
|
||||
import com.dbx.agent.MultiSessionJsonRpcServer;
|
||||
import com.dbx.agent.ObjectSource;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class SapHanaAgent extends ConfiguredJdbcAgent {
|
||||
public static final JdbcAgentProfile SAPHANA_PROFILE = new JdbcAgentProfile(
|
||||
@@ -20,6 +24,30 @@ public final class SapHanaAgent extends ConfiguredJdbcAgent {
|
||||
super(SAPHANA_PROFILE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectSource getObjectSource(String schema, String name, String objectType) {
|
||||
String normalizedType = objectType == null ? "" : objectType.trim().toUpperCase(Locale.ROOT);
|
||||
if (!"PROCEDURE".equals(normalizedType)) {
|
||||
return super.getObjectSource(schema, name, objectType);
|
||||
}
|
||||
|
||||
return unchecked(() -> {
|
||||
String sql = "SELECT DEFINITION FROM SYS.PROCEDURES WHERE SCHEMA_NAME = ? AND PROCEDURE_NAME = ?";
|
||||
String source = "";
|
||||
try (PreparedStatement stmt = requireConnection().prepareStatement(sql)) {
|
||||
stmt.setString(1, schema);
|
||||
stmt.setString(2, name);
|
||||
try (ResultSet rs = stmt.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
String definition = rs.getString(1);
|
||||
source = definition == null ? "" : definition;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ObjectSource(name, normalizedType, schema, source);
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new MultiSessionJsonRpcServer(SapHanaAgent::new).run();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.dbx.agent.saphana;
|
||||
|
||||
import com.dbx.agent.ObjectSource;
|
||||
import com.dbx.agent.test.TestSupport;
|
||||
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;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class SapHanaAgentTest {
|
||||
@Test
|
||||
void getObjectSourceReadsProcedureDefinitionFromSystemCatalog() {
|
||||
List<String> calls = new ArrayList<>();
|
||||
String definition = "CREATE PROCEDURE \"APP\".\"REFRESH_ORDERS\" () AS BEGIN END";
|
||||
SapHanaAgent agent = agentWithDefinition(calls, definition, true);
|
||||
|
||||
ObjectSource source = agent.getObjectSource("APP", "REFRESH_ORDERS", " procedure ");
|
||||
|
||||
Assertions.assertEquals("REFRESH_ORDERS", source.getName());
|
||||
Assertions.assertEquals("PROCEDURE", source.getObject_type());
|
||||
Assertions.assertEquals("APP", source.getSchema());
|
||||
Assertions.assertEquals(definition, source.getSource());
|
||||
Assertions.assertEquals(
|
||||
List.of(
|
||||
"sql:SELECT DEFINITION FROM SYS.PROCEDURES WHERE SCHEMA_NAME = ? AND PROCEDURE_NAME = ?",
|
||||
"param:1=APP",
|
||||
"param:2=REFRESH_ORDERS"
|
||||
),
|
||||
calls
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getObjectSourceReturnsEmptyTextWhenHanaHidesProcedureDefinition() {
|
||||
SapHanaAgent agent = agentWithDefinition(new ArrayList<>(), null, true);
|
||||
|
||||
ObjectSource source = agent.getObjectSource("SYS", "BACKUP_DATA", "PROCEDURE");
|
||||
|
||||
Assertions.assertEquals("", source.getSource());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getObjectSourceKeepsOtherObjectTypesUnsupported() {
|
||||
SapHanaAgent agent = new SapHanaAgent();
|
||||
|
||||
UnsupportedOperationException error = Assertions.assertThrows(
|
||||
UnsupportedOperationException.class,
|
||||
() -> agent.getObjectSource("APP", "ACTIVE_ORDERS", "VIEW")
|
||||
);
|
||||
|
||||
Assertions.assertEquals("Object source is not supported", error.getMessage());
|
||||
}
|
||||
|
||||
private static SapHanaAgent agentWithDefinition(List<String> calls, String definition, boolean hasRow) {
|
||||
ResultSet resultSet = proxy(ResultSet.class, (method, args) -> {
|
||||
if ("next".equals(method.getName())) {
|
||||
return hasRow;
|
||||
}
|
||||
if ("getString".equals(method.getName())) {
|
||||
return definition;
|
||||
}
|
||||
return defaultValue(method.getReturnType());
|
||||
});
|
||||
PreparedStatement statement = proxy(PreparedStatement.class, (method, args) -> {
|
||||
if ("setString".equals(method.getName())) {
|
||||
calls.add("param:" + args[0] + "=" + args[1]);
|
||||
return null;
|
||||
}
|
||||
if ("executeQuery".equals(method.getName())) {
|
||||
return resultSet;
|
||||
}
|
||||
return defaultValue(method.getReturnType());
|
||||
});
|
||||
Connection connection = proxy(Connection.class, (method, args) -> {
|
||||
if ("prepareStatement".equals(method.getName())) {
|
||||
calls.add("sql:" + args[0]);
|
||||
return statement;
|
||||
}
|
||||
if ("isClosed".equals(method.getName())) {
|
||||
return false;
|
||||
}
|
||||
return defaultValue(method.getReturnType());
|
||||
});
|
||||
|
||||
SapHanaAgent agent = new SapHanaAgent();
|
||||
TestSupport.setPrivateConnection(agent, connection);
|
||||
return agent;
|
||||
}
|
||||
|
||||
private static <T> T proxy(Class<T> type, Handler handler) {
|
||||
return type.cast(Proxy.newProxyInstance(
|
||||
type.getClassLoader(),
|
||||
new Class<?>[]{type},
|
||||
(proxy, method, args) -> handler.handle(method, args)
|
||||
));
|
||||
}
|
||||
|
||||
private static Object defaultValue(Class<?> type) {
|
||||
if (!type.isPrimitive()) return null;
|
||||
if (type == boolean.class) return false;
|
||||
if (type == byte.class) return (byte) 0;
|
||||
if (type == short.class) return (short) 0;
|
||||
if (type == int.class) return 0;
|
||||
if (type == long.class) return 0L;
|
||||
if (type == float.class) return 0f;
|
||||
if (type == double.class) return 0d;
|
||||
if (type == char.class) return '\0';
|
||||
return null;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface Handler {
|
||||
Object handle(Method method, Object[] args) throws Throwable;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user