Skip to content
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

support custom where sql construction logic #2010

Closed
wants to merge 25 commits into from
Closed

support custom where sql construction logic #2010

wants to merge 25 commits into from

Conversation

redkale
Copy link
Contributor

@redkale redkale commented May 23, 2024

I hope that XxxDeParser can overload this part of the logic when building the where condition sql statement. My requirement is to dynamically build the filter conditions in where according to the parameters.

Copy link
Contributor

@manticore-projects manticore-projects left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution. Please consider the recommended changes and then I would merge it.

@redkale
Copy link
Contributor Author

redkale commented May 24, 2024

@manticore-projects thanks, updated
My requirement scenario is :
sql template "SELECT * FROM user WHERE name = :param_name AND status = :param_status"
where param_name and param_status both is null, need generated sql: "SELECT * FROM user"
where param_name is not null, param_status is null, need generated sql: "SELECT * FROM user WHERE name = ? "
so deParse where expression maybe is empty

deparseDistinctClause、deparseSelectItemsClause、deparseOrderByElementsClause requires two parameters ,
Parameter plainSelect mainly used to determine the current selection. like:

  @Override
  protected void deparseDistinctClause(PlainSelect plainSelect, Distinct distinct) {
    if (this.countSelect != plainSelect) {
        super.deparseDistinctClause(plainSelect, distinct);
    } else {
        super.deparseDistinctClause(plainSelect, this.countDistinct);
    }
}

protected void deparseSelectItemsClause(PlainSelect plainSelect, List<SelectItem<?>> selectItems) {
    if (this.countSelect != plainSelect) {
        super.deparseSelectItemsClause(plainSelect, selectItems);
    } else {
        super.deparseSelectItemsClause(plainSelect, this.countRootSelectItems);
    }
}

protected void deparseOrderByElementsClause(PlainSelect plainSelect, List<OrderByElement> orderByElements) {
    if (this.countSelect != plainSelect) {
        super.deparseOrderByElementsClause(plainSelect, orderByElements);
    } else {
        super.deparseOrderByElementsClause(plainSelect, null);
    }
}

@manticore-projects
Copy link
Contributor

sql template "SELECT * FROM user WHERE name = :param_name AND status = :param_status"

Greetings.

In my opinion, your approach is flawed. Instead of tampering with the WHERE clause, you will need to filter for the JDBCNamedParameter, then check it's parent AST Node and when it's an EqualTo rewrite it into an IsNull.
Alternatively you can rewrite any EqualTo Expression into an IsNull Expression when the Left or Right Expression is a Null Value.

Those are specific solutions though that won't fit well into the generic de-parser.

Copy link
Contributor

@manticore-projects manticore-projects left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I won't agree with the StringBuffer based WHERE clause exception. Rest is ok though and I would merge it.

@@ -92,7 +92,11 @@ public void deParse(Delete delete) {

if (delete.getWhere() != null) {
buffer.append(" WHERE ");
delete.getWhere().accept(expressionVisitor);
int len = buffer.length();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove those specific parts from the PR as it does not fit into the generic Deparser.

@@ -251,7 +225,11 @@ public void visit(PlainSelect plainSelect) {

if (plainSelect.getWhere() != null) {
buffer.append(" WHERE ");
int len = buffer.length();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove those specific parts from the PR as it does not fit into the generic Deparser.

@@ -90,7 +90,11 @@ public void deParse(Update update) {

if (update.getWhere() != null) {
buffer.append(" WHERE ");
update.getWhere().accept(expressionVisitor);
int len = buffer.length();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove those specific parts from the PR as it does not fit into the generic Deparser.

@manticore-projects
Copy link
Contributor

Please fix QA exceptions and then I would merge.

@redkale
Copy link
Contributor Author

redkale commented May 25, 2024

@manticore-projects thanks, I add deparseWhereClause like before。
Statement will be caching, it cannot modify. generate a new sql template based on parameters, like this:

class SqlTemple {

    private final Statement sqlStatement;

    public SqlTemple() throws ParseException {
        String sql = "SELECT * FROM user WHERE name = :param_name AND status = :param_status";
        CCJSqlParser sqlParser = new CCJSqlParser(sql).withAllowComplexParsing(true);
        this.sqlStatement = sqlParser.SingleStatement();
    }
    
    public String createJdbcSql(Map<String, Object> map) {
        StringBuilder buffer = new StringBuilder();
        MyExprDeParser myParser = new MyExprDeParser (buffer, map);
        SelectDeParser parser = new SelectDeParser();
        StatementDeParser deParser = new StatementDeParser(myParser, parser, buffer);
        sqlStatement.accept(deParser);
        //map is empty  return "SELECT * FROM user"
        //map is {"param_name":"aa"}  reutrn "SELECT * FROM user WHERE name = :param_name"
        //map is {"param_status":2}  reutrn "SELECT * FROM user WHERE status = :param_status"
        //map is {"param_name":"aa","param_status":2}  reutrn "SELECT * FROM user WHERE name = :param_name AND status = :param_status"
        return buffer.toString();
    }
}

@redkale
Copy link
Contributor Author

redkale commented May 25, 2024

My framework source: https://github.com/redkale/redkale-plugins/tree/main/src/main/java/org/redkalex/source/parser
@manticore-projects
The function I designed is to define a SQL with full filtering conditions, and then dynamically build a new SQL with where conditions based on the existence of parameters.
Constructing the sql string into a Statement consumes performance and requires caching the Statement. In StatementDeParser, only the buffer can be dynamically controlled, so I want to dynamically adjust the buffer, not the AST Node.

public interface ForumInfoMapper extends BaseMapper<ForumInfo> {

    @Sql("SELECT f.forum_groupid, s.forum_section_color "
        + "FROM forum_info f, forum_section s "
        + " WHERE f.forumid = s.forumid AND "
        + "s.forum_sectionid = #{bean.forumSectionid} AND "
        + "f.forumid = #{bean.forumid} AND s.forum_section_color = #{bean.forumSectionColor}")
    public ForumResult findForumResult(ForumBean bean);

    @Sql("SELECT f.forum_groupid, s.forum_section_color "
        + "FROM forum_info f, forum_section s "
        + " WHERE f.forumid = s.forumid AND "
        + "s.forum_sectionid = #{bean.forumSectionid} AND "
        + "f.forumid = #{bean.forumid} AND s.forum_section_color = #{bean.forumSectionColor}")
    public CompletableFuture<ForumResult> findForumResultAsync(ForumBean bean);
}

@manticore-projects
Copy link
Contributor

QA still failing.
Run './gradlew :spotlessApply' to fix these violations.

@manticore-projects
Copy link
Contributor

Before you push, please run the QA locally via gradle check.

@manticore-projects
Copy link
Contributor

Although I still believe that your approach is not best practise. Rather than tampering with the WHERE clause I would rewrite the query like:

select *
from t
where ( :param1 is null or column1= :param1) AND ( :param2 IS NULL or column2 = :param2 )

Then you can set the parameters :param1 or :param2 to NULL when not provided in your Parameter Map.
You may want to have a look at https://manticore-projects.com/MJdbcUtils/index.html which I wrote for exactly this purpose.

@redkale redkale closed this May 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants