Skip to content

feat: support sql parser #436

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion arex-instrumentation-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
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;
import io.arex.agent.bootstrap.model.Mocker;
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;
Expand All @@ -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 = "{}";
Expand Down Expand Up @@ -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<String, String> 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
52 changes: 52 additions & 0 deletions arex-third-party/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,59 @@
<groupId>io.arex</groupId>
<version>${revision}</version>
</parent>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>${jsqlparser.version}</version>
</dependency>
</dependencies>
<modelVersion>4.0.0</modelVersion>

<artifactId>arex-third-party</artifactId>
<packaging>jar</packaging>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>${maven-shade-plugin.version}</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<pattern>com.github.jsqlparser</pattern>
<shadedPattern>shaded.com.github.jsqlparser</shadedPattern>
</relocation>
<relocation>
<pattern>com.fasterxml.jackson.core</pattern>
<shadedPattern>shaded.com.fasterxml.jackson.core</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
<configuration>
<artifactSet>
<includes>
<include>com.github.jsqlparser:jsqlparser</include>
<include>com.fasterxml.jackson.core:jackson-databind</include>
<include>io.arex:arex-third-party</include>
</includes>
</artifactSet>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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<String, String> parseTableAndAction(String sql) throws JSQLParserException {
if (sql == null || sql.isEmpty()) {
return null;
}

Map<String, String> result = new HashMap<>(2);

Statement statement = CCJSqlParserUtil.parse(sql);
result.put(DbParseConstants.ACTION, getAction(statement));

TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
List<String> 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 "";
}
}
}
Original file line number Diff line number Diff line change
@@ -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<T> {

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);
}
Original file line number Diff line number Diff line change
@@ -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<? extends Statement> 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");
}
}
}
Original file line number Diff line number Diff line change
@@ -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<Delete> {

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;
}
}
Original file line number Diff line number Diff line change
@@ -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<Execute> {

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;
}
}
Loading