diff --git a/arex-instrumentation-api/pom.xml b/arex-instrumentation-api/pom.xml index ce6ae7772..c0cbf5049 100644 --- a/arex-instrumentation-api/pom.xml +++ b/arex-instrumentation-api/pom.xml @@ -32,7 +32,6 @@ com.fasterxml.jackson.core jackson-databind ${jackson.version} - test com.google.code.gson diff --git a/arex-instrumentation-api/src/main/java/io/arex/inst/runtime/util/MockUtils.java b/arex-instrumentation-api/src/main/java/io/arex/inst/runtime/util/MockUtils.java index fdc0f4c94..eada95d41 100644 --- a/arex-instrumentation-api/src/main/java/io/arex/inst/runtime/util/MockUtils.java +++ b/arex-instrumentation-api/src/main/java/io/arex/inst/runtime/util/MockUtils.java @@ -1,5 +1,6 @@ package io.arex.inst.runtime.util; +import com.fasterxml.jackson.databind.JsonNode; import io.arex.agent.bootstrap.model.ArexMocker; import io.arex.agent.bootstrap.model.MockCategoryType; import io.arex.agent.bootstrap.model.MockStrategyEnum; @@ -7,6 +8,8 @@ import io.arex.agent.bootstrap.model.Mocker.Target; import io.arex.agent.bootstrap.util.MapUtils; import io.arex.agent.bootstrap.util.StringUtil; +import io.arex.agent.thirdparty.util.parse.sqlparse.SqlParseManager; +import io.arex.agent.thirdparty.util.parse.sqlparse.constants.DbParseConstants; import io.arex.inst.runtime.log.LogManager; import io.arex.inst.runtime.config.Config; import io.arex.inst.runtime.context.ArexContext; @@ -17,6 +20,8 @@ import io.arex.inst.runtime.service.DataService; import io.arex.inst.runtime.util.sizeof.AgentSizeOf; +import java.util.Map; + public final class MockUtils { private static final String EMPTY_JSON = "{}"; @@ -48,6 +53,25 @@ public static ArexMocker createDatabase(String method) { return create(MockCategoryType.DATABASE, method); } + public static ArexMocker createDatabase(String method, String sql, String dbName) { + StringBuilder operationName = new StringBuilder(); + try { + String[] splitSql = sql.split(";"); + for (String s : splitSql) { + Map tableAndAction = SqlParseManager.getInstance().parseTableAndAction(s); + if (tableAndAction != null && !tableAndAction.isEmpty()) { + String action = tableAndAction.getOrDefault(DbParseConstants.ACTION, StringUtil.EMPTY); + String tableName = tableAndAction.getOrDefault(DbParseConstants.TABLE, StringUtil.EMPTY); + operationName.append(dbName).append("-").append(tableName).append("-").append(action).append(";"); + } + } + } catch (Exception e) { + LogManager.warn("createDatabase", "parse sql error", e); + operationName.append(method); + } + return createDatabase(operationName.toString()); + } + public static ArexMocker createRedis(String method) { return create(MockCategoryType.REDIS, method); } diff --git a/arex-instrumentation-api/src/test/java/io/arex/inst/runtime/util/MockUtilsTest.java b/arex-instrumentation-api/src/test/java/io/arex/inst/runtime/util/MockUtilsTest.java index 5284b0977..28e9a7d24 100644 --- a/arex-instrumentation-api/src/test/java/io/arex/inst/runtime/util/MockUtilsTest.java +++ b/arex-instrumentation-api/src/test/java/io/arex/inst/runtime/util/MockUtilsTest.java @@ -178,6 +178,9 @@ void createMocker() { actualResult = MockUtils.createNettyProvider("query"); assertEquals(MockCategoryType.NETTY_PROVIDER, actualResult.getCategoryType()); + + actualResult = MockUtils.createDatabase("query", "select * from test", "testDB"); + assertEquals(MockCategoryType.DATABASE, actualResult.getCategoryType()); } @Test diff --git a/arex-instrumentation/database/arex-database-common/src/main/java/io/arex/inst/database/common/DatabaseExtractor.java b/arex-instrumentation/database/arex-database-common/src/main/java/io/arex/inst/database/common/DatabaseExtractor.java index 3e8976513..9403a49f1 100644 --- a/arex-instrumentation/database/arex-database-common/src/main/java/io/arex/inst/database/common/DatabaseExtractor.java +++ b/arex-instrumentation/database/arex-database-common/src/main/java/io/arex/inst/database/common/DatabaseExtractor.java @@ -116,7 +116,7 @@ public MockResult replay(String serializer) { } private Mocker makeMocker(Object response, String serializer) { - Mocker mocker = MockUtils.createDatabase(this.methodName); + Mocker mocker = MockUtils.createDatabase(this.methodName, this.sql, this.dbName); mocker.getTargetRequest().setBody(this.sql); mocker.getTargetRequest().setAttribute("dbName", this.dbName); mocker.getTargetRequest().setAttribute("parameters", this.parameters); diff --git a/arex-third-party/pom.xml b/arex-third-party/pom.xml index 0f89cec99..5e78fd883 100644 --- a/arex-third-party/pom.xml +++ b/arex-third-party/pom.xml @@ -7,7 +7,59 @@ io.arex ${revision} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + com.github.jsqlparser + jsqlparser + ${jsqlparser.version} + + 4.0.0 arex-third-party + jar + + + + + org.apache.maven.plugins + maven-shade-plugin + ${maven-shade-plugin.version} + + + package + + shade + + + + + com.github.jsqlparser + shaded.com.github.jsqlparser + + + com.fasterxml.jackson.core + shaded.com.fasterxml.jackson.core + + + + + + + + + com.github.jsqlparser:jsqlparser + com.fasterxml.jackson.core:jackson-databind + io.arex:arex-third-party + + + + + + \ No newline at end of file diff --git a/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/JacksonHelperUtil.java b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/JacksonHelperUtil.java new file mode 100644 index 000000000..229b35439 --- /dev/null +++ b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/JacksonHelperUtil.java @@ -0,0 +1,23 @@ +package io.arex.agent.thirdparty.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * @author niyan + * @date 2024/4/3 + * @since 1.0.0 + */ +public class JacksonHelperUtil { + + public static ObjectMapper objectMapper = new ObjectMapper(); + + public static ObjectNode getObjectNode() { + return objectMapper.createObjectNode(); + } + + public static ArrayNode getArrayNode() { + return objectMapper.createArrayNode(); + } +} diff --git a/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/SqlParseManager.java b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/SqlParseManager.java new file mode 100644 index 000000000..212b0a852 --- /dev/null +++ b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/SqlParseManager.java @@ -0,0 +1,88 @@ +package io.arex.agent.thirdparty.util.parse.sqlparse; + +import com.fasterxml.jackson.databind.JsonNode; +import io.arex.agent.thirdparty.util.parse.sqlparse.action.AbstractParse; +import io.arex.agent.thirdparty.util.parse.sqlparse.action.ActionFactory; +import io.arex.agent.thirdparty.util.parse.sqlparse.constants.DbParseConstants; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.execute.Execute; +import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.replace.Replace; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.update.Update; +import net.sf.jsqlparser.util.TablesNamesFinder; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * sql parse + * + * @author niyan + * @date 2024/4/3 + * @since 1.0.0 + */ +public class SqlParseManager { + + private static SqlParseManager INSTANCE; + + private SqlParseManager() { + } + + public static SqlParseManager getInstance() { + if (INSTANCE == null) { + INSTANCE = new SqlParseManager(); + } + return INSTANCE; + } + + public JsonNode parse(String sql) throws JSQLParserException { + if (sql == null || sql.isEmpty()) { + return null; + } + + Statement statement = CCJSqlParserUtil.parse(sql); + AbstractParse parse = ActionFactory.selectParse(statement); + return parse.parse(statement); + } + + public Map parseTableAndAction(String sql) throws JSQLParserException { + if (sql == null || sql.isEmpty()) { + return null; + } + + Map result = new HashMap<>(2); + + Statement statement = CCJSqlParserUtil.parse(sql); + result.put(DbParseConstants.ACTION, getAction(statement)); + + TablesNamesFinder tablesNamesFinder = new TablesNamesFinder(); + List tableNames = tablesNamesFinder.getTableList(statement); + if (tableNames != null && !tableNames.isEmpty()) { + result.put(DbParseConstants.TABLE, String.join(",", tableNames)); + } + return result; + } + + private String getAction(Statement statement) { + if (statement instanceof Select) { + return DbParseConstants.SELECT; + } else if (statement instanceof Execute) { + return DbParseConstants.EXECUTE; + } else if (statement instanceof Delete) { + return DbParseConstants.DELETE; + } else if (statement instanceof Insert) { + return DbParseConstants.INSERT; + } else if (statement instanceof Replace) { + return DbParseConstants.REPLACE; + } else if (statement instanceof Update) { + return DbParseConstants.UPDATE; + } else { + return ""; + } + } +} diff --git a/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/AbstractParse.java b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/AbstractParse.java new file mode 100644 index 000000000..8fb495f42 --- /dev/null +++ b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/AbstractParse.java @@ -0,0 +1,26 @@ +package io.arex.agent.thirdparty.util.parse.sqlparse.action; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.arex.agent.thirdparty.util.JacksonHelperUtil; +import io.arex.agent.thirdparty.util.parse.sqlparse.constants.DbParseConstants; + +/** + * @author niyan + * @date 2024/4/8 + * @since 1.0.0 + */ +public abstract class AbstractParse { + + protected final ObjectNode sqlObjectNode; + + public AbstractParse() { + sqlObjectNode = JacksonHelperUtil.getObjectNode(); + } + + public AbstractParse(String action) { + this(); + sqlObjectNode.put(DbParseConstants.ACTION, action); + } + + public abstract ObjectNode parse(T parseObj); +} diff --git a/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/ActionFactory.java b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/ActionFactory.java new file mode 100644 index 000000000..08c6d61a4 --- /dev/null +++ b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/ActionFactory.java @@ -0,0 +1,36 @@ +package io.arex.agent.thirdparty.util.parse.sqlparse.action; + +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.execute.Execute; +import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.replace.Replace; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.update.Update; + +import net.sf.jsqlparser.statement.Statement; + + +/** + * @author niyan + * @date 2024/4/3 + * @since 1.0.0 + */ +public class ActionFactory { + public static AbstractParse selectParse(Statement statement) { + if (statement instanceof Select) { + return new SelectParse(); + } else if (statement instanceof Execute) { + return new ExecuteParse(); + } else if (statement instanceof Delete) { + return new DeleteParse(); + } else if (statement instanceof Insert) { + return new InsertParse(); + } else if (statement instanceof Replace) { + return new ReplaceParse(); + } else if (statement instanceof Update) { + return new UpdateParse(); + } else { + throw new UnsupportedOperationException("not support"); + } + } +} diff --git a/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/DeleteParse.java b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/DeleteParse.java new file mode 100644 index 000000000..4c3a371ae --- /dev/null +++ b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/DeleteParse.java @@ -0,0 +1,42 @@ +package io.arex.agent.thirdparty.util.parse.sqlparse.action; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.arex.agent.thirdparty.util.parse.sqlparse.parse.CommonParse; +import io.arex.agent.thirdparty.util.parse.sqlparse.parse.ExpressionParse; +import io.arex.agent.thirdparty.util.parse.sqlparse.parse.JoinParse; +import io.arex.agent.thirdparty.util.parse.sqlparse.parse.OrderByParse; +import io.arex.agent.thirdparty.util.parse.sqlparse.parse.TableParse; +import io.arex.agent.thirdparty.util.parse.sqlparse.constants.DbParseConstants; +import net.sf.jsqlparser.statement.delete.Delete; + +/** + * @author niyan + * @date 2024/4/3 + * @since 1.0.0 + */ +public class DeleteParse extends AbstractParse { + + public DeleteParse() { + super(DbParseConstants.DELETE); + } + @Override + public ObjectNode parse(Delete parseObj) { + + // tables parse + TableParse.parseDelTable(parseObj.getTables(), parseObj.getTable(), sqlObjectNode); + + // join parse + JoinParse.parse(parseObj.getJoins(), sqlObjectNode); + + // where parse + ExpressionParse.parseWhere(parseObj.getWhere(), sqlObjectNode); + + // orderBy parse + OrderByParse.parse(parseObj.getOrderByElements(), sqlObjectNode); + + // limit parse + CommonParse.parseLimit(parseObj.getLimit(), sqlObjectNode); + + return sqlObjectNode; + } +} diff --git a/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/ExecuteParse.java b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/ExecuteParse.java new file mode 100644 index 000000000..f023c2c08 --- /dev/null +++ b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/ExecuteParse.java @@ -0,0 +1,31 @@ +package io.arex.agent.thirdparty.util.parse.sqlparse.action; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.arex.agent.thirdparty.util.parse.sqlparse.parse.CommonParse; +import io.arex.agent.thirdparty.util.parse.sqlparse.parse.ExpressionParse; +import io.arex.agent.thirdparty.util.parse.sqlparse.constants.DbParseConstants; +import net.sf.jsqlparser.statement.execute.Execute; + +/** + * @author niyan + * @date 2024/4/3 + * @since 1.0.0 + */ +public class ExecuteParse extends AbstractParse { + + public ExecuteParse() { + super(DbParseConstants.EXECUTE); + } + + @Override + public ObjectNode parse(Execute parseObj) { + + // execute name parse + CommonParse.parseName(parseObj.getName(), sqlObjectNode); + + // expressions parse + ExpressionParse.parse(parseObj.getExprList(), sqlObjectNode); + + return sqlObjectNode; + } +} diff --git a/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/InsertParse.java b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/InsertParse.java new file mode 100644 index 000000000..1265effb0 --- /dev/null +++ b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/InsertParse.java @@ -0,0 +1,32 @@ +package io.arex.agent.thirdparty.util.parse.sqlparse.action; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.arex.agent.thirdparty.util.parse.sqlparse.parse.ColumnParse; +import io.arex.agent.thirdparty.util.parse.sqlparse.parse.TableParse; +import io.arex.agent.thirdparty.util.parse.sqlparse.constants.DbParseConstants; +import net.sf.jsqlparser.statement.insert.Insert; + +/** + * @author niyan + * @date 2024/4/3 + * @since 1.0.0 + */ +public class InsertParse extends AbstractParse { + + public InsertParse() { + super(DbParseConstants.INSERT); + } + @Override + public ObjectNode parse(Insert parseObj) { + // table parse + TableParse.parse(parseObj.getTable(), sqlObjectNode); + + // columns parse + ColumnParse.parse(parseObj.getColumns(), parseObj.getItemsList(), sqlObjectNode); + + // setColumns parse + ColumnParse.parseSetColumns(parseObj.getSetColumns(), parseObj.getSetExpressionList(), sqlObjectNode); + + return sqlObjectNode; + } +} diff --git a/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/ReplaceParse.java b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/ReplaceParse.java new file mode 100644 index 000000000..e1d3ff617 --- /dev/null +++ b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/ReplaceParse.java @@ -0,0 +1,33 @@ +package io.arex.agent.thirdparty.util.parse.sqlparse.action; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.arex.agent.thirdparty.util.parse.sqlparse.parse.ColumnParse; +import io.arex.agent.thirdparty.util.parse.sqlparse.parse.ExpressionParse; +import io.arex.agent.thirdparty.util.parse.sqlparse.parse.TableParse; +import io.arex.agent.thirdparty.util.parse.sqlparse.constants.DbParseConstants; +import net.sf.jsqlparser.statement.replace.Replace; + +/** + * @author niyan + * @date 2024/4/3 + * @since 1.0.0 + */ +public class ReplaceParse extends AbstractParse { + + public ReplaceParse() { + super(DbParseConstants.REPLACE); + } + @Override + public ObjectNode parse(Replace parseObj) { + // table parse + TableParse.parse(parseObj.getTable(), sqlObjectNode); + + // columns parse + ColumnParse.parse(parseObj.getColumns(), parseObj.getItemsList(), sqlObjectNode); + + // expressions parse + ExpressionParse.parse(parseObj.getExpressions(), parseObj.getColumns(), sqlObjectNode); + + return sqlObjectNode; + } +} diff --git a/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/SelectParse.java b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/SelectParse.java new file mode 100644 index 000000000..a3fb48cf8 --- /dev/null +++ b/arex-third-party/src/main/java/io/arex/agent/thirdparty/util/parse/sqlparse/action/SelectParse.java @@ -0,0 +1,29 @@ +package io.arex.agent.thirdparty.util.parse.sqlparse.action; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.arex.agent.thirdparty.util.parse.sqlparse.adapter.ArexSelectVisitorAdapter; +import io.arex.agent.thirdparty.util.parse.sqlparse.constants.DbParseConstants; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.select.SelectBody; + +/** + * @author niyan + * @date 2024/4/3 + * @since 1.0.0 + */ +public class SelectParse extends AbstractParse