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

多表关联模糊查询时,查询条件顺序错乱bug #445

Closed
transtone opened this issue Sep 20, 2022 · 21 comments
Closed

多表关联模糊查询时,查询条件顺序错乱bug #445

transtone opened this issue Sep 20, 2022 · 21 comments
Labels

Comments

@transtone
Copy link
Contributor

transtone commented Sep 20, 2022

环境信息

系统: Windows 10
JDK: 1.8.0_17
数据库: postgresql-12
APIJSON: 5.2.0
APIJSON-framework: 5.2.0
项目:https://github.com/APIJSON/APIJSON-Demo/tree/master/APIJSON-Java-Server/APIJSONDemo-Druid

问题描述

在使用临时表进行多表关联查询,执行如下sql时,查询条件的顺序会颠倒,导致查询报错。

{
    "[]": {
        "Comment": {  
            "name$": "%a%",
            "content$": "%a%",
            "@combine": "name$,content$", 
            "@from@": {  
                "from": "Comment",
                "join": "&/User/id@",
                "Comment": {},
                "User": {
                    "id@": "/Comment/userId",
		    "sex": 1, // 这个条件会和全局条件相调换
                    "@column": "name,sex"  
                }
            }
        }
    },
    "@explain": true
}

执行时sql参数顺序乱了,sex=1 变成了 sex=%a%
image

explain返回的sql是正确的
image

@transtone transtone changed the title 多表关联模糊查询时,子查询类型报错 多表关联模糊查询时,临时表查询类型报错 Sep 20, 2022
@transtone transtone changed the title 多表关联模糊查询时,临时表查询类型报错 多表关联模糊查询时,查询条件顺序错乱 Sep 21, 2022
@transtone transtone changed the title 多表关联模糊查询时,查询条件顺序错乱 多表关联模糊查询时,查询条件顺序错乱bug Sep 21, 2022
@TommyLemon
Copy link
Collaborator

@transtone
Copy link
Contributor Author

transtone commented Sep 21, 2022

已经升级过,当前版本是 apijson-framework-5.2.0

        <dependency>
            <groupId>com.github.APIJSON</groupId>
            <artifactId>apijson-framework</artifactId>
            <version>5.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.APIJSON</groupId>
            <artifactId>apijson-column</artifactId>
            <version>1.2.5</version>
        </dependency>
        <dependency>
            <groupId>com.github.APIJSON</groupId>
            <artifactId>apijson-router</artifactId>
            <version>1.0.5</version>
        </dependency>

@TommyLemon
Copy link
Collaborator

TommyLemon commented Sep 21, 2022

有没有哪里单独指定了 APIJSON 5.1.5?
apijson-framework 和 APIJSON 是两个项目

@transtone
Copy link
Contributor Author

transtone commented Sep 21, 2022

有没有哪里单独指定了 APIJSON 5.1.5? apijson-framework 和 APIJSON 是两个项目

没有单独指定。是两个项目,但是apijson-framework 是需要引用 APIJSON 的,
IDEA debug 的显示不会错的吧。

@TommyLemon
Copy link
Collaborator

TommyLemon commented Sep 21, 2022

感谢反馈,外查询和子查询都有条件时触发了这个 bug。

这里得把 preparedValueList 反过来装
https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L4027-L4030

image

参考 concatJoinWhereString,先外查询 preparedValueList、子查询的 preparedValueList 取出来用一个变量暂存,然后清空,再用子查询的 subPreparedValueList.add(preparedValueList) 然后 setPreparedValueList
https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2924-L3049
image

可以改了后提个 PR 贡献代码,开源要大家一起参与才会更美好,并可持续发展~
image

https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%80%E5%AE%9A%E8%A6%81%E8%B4%A1%E7%8C%AE%E4%BB%A3%E7%A0%81

@transtone
Copy link
Contributor Author

transtone commented Sep 21, 2022

已提交,如可用请合并。
如果不是因为explain返回的sql是正确的,还发现不了这个问题。
还一度想添加一个接口,直接获取 config.getSQL() 的值,手动执行呢。

@TommyLemon TommyLemon added the help wanted 请求帮助 label Sep 25, 2022
@transtone
Copy link
Contributor Author

这里得把 preparedValueList 反过来装 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L4027-L4030

参考 concatJoinWhereString,先外查询 preparedValueList、子查询的 preparedValueList 取出来用一个变量暂存,然后清空,再用子查询的 subPreparedValueList.add(preparedValueList) 然后 setPreparedValueList https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2924-L3049

getConditionString 这个函数是反复调用的,也就是说 getSubqueryString 会执行多次,依据什么来判断当前条件包含外查询和子查询,并且需要反装呢?

@TommyLemon
Copy link
Collaborator

TommyLemon commented Sep 30, 2022

这里得把 preparedValueList 反过来装 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L4027-L4030
参考 concatJoinWhereString,先外查询 preparedValueList、子查询的 preparedValueList 取出来用一个变量暂存,然后清空,再用子查询的 subPreparedValueList.add(preparedValueList) 然后 setPreparedValueList https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2924-L3049

getConditionString 这个函数是反复调用的,也就是说 getSubqueryString 会执行多次,依据什么来判断当前条件包含外查询和子查询,并且需要反装呢?

getFrom 值不为 null 就一定是有 FROM(SELECT ...) 这种子查询,
getSubqueryString 执行多次,其实是前端传参子查询数量的两倍,一倍用来生成完整 SQL,另一倍用来生成 WHERE id=? 这种预编译的 SQL,具体见 getSQL(boolean prepared)

@TommyLemon
Copy link
Collaborator

很抱歉,不会修。静待您的更新。 如果不是因为explain返回的sql是正确的,还发现不了这个问题。 还一度想添加一个接口,直接获取 config.getSQL() 的值,手动执行呢。

取两个列表、合并两个列表、设置两个列表,也就 5 行代码

@gxmanito
Copy link

这里得把 preparedValueList 反过来装 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L4027-L4030
参考 concatJoinWhereString,先外查询 preparedValueList、子查询的 preparedValueList 取出来用一个变量暂存,然后清空,再用子查询的 subPreparedValueList.add(preparedValueList) 然后 setPreparedValueList https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2924-L3049

getConditionString 这个函数是反复调用的,也就是说 getSubqueryString 会执行多次,依据什么来判断当前条件包含外查询和子查询,并且需要反装呢?

getFrom 值不为 null 就一定是有 FROM(SELECT ...) 这种子查询, getSubqueryString 执行多次,其实是前端传参子查询数量的两倍,一倍用来生成完整 SQL,另一倍用来生成 WHERE id=? 这种预编译的 SQL,具体见 getSQL(boolean prepared)

6.3.0版本,List subPvl = cfg.getPreparedValueList()一直是空集合,导致最终No value specified for parameter 4

@TommyLemon
Copy link
Collaborator

这里得把 preparedValueList 反过来装 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L4027-L4030
参考 concatJoinWhereString,先外查询 preparedValueList、子查询的 preparedValueList 取出来用一个变量暂存,然后清空,再用子查询的 subPreparedValueList.add(preparedValueList) 然后 setPreparedValueList https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2924-L3049

getConditionString 这个函数是反复调用的,也就是说 getSubqueryString 会执行多次,依据什么来判断当前条件包含外查询和子查询,并且需要反装呢?

getFrom 值不为 null 就一定是有 FROM(SELECT ...) 这种子查询, getSubqueryString 执行多次,其实是前端传参子查询数量的两倍,一倍用来生成完整 SQL,另一倍用来生成 WHERE id=? 这种预编译的 SQL,具体见 getSQL(boolean prepared)

6.3.0版本,List subPvl = cfg.getPreparedValueList()一直是空集合,导致最终No value specified for parameter 4

@gxmanito 升级 6.4+ 或 7.0.3 试试。还不行的话麻烦提供详细信息,方便排查问题

@gxmanito
Copy link

gxmanito commented Jul 25, 2024

#445 (comment)
@TommyLemon 项目使用的是springboot2.x和jdk8,升级6.4+ 或 7.0.3 会向下兼容吗?

另还有个多表join(left、inner、app均使用)其中查询有模糊查,会报错,把"deviceName$": "%设备名称%"条件去掉就没问题,打断点查看第一遍会先生成(SELECT point_code FROM base_norm_point WHERE ( (device_type_code != ?) ) ),导致占位符多出一个是201
image

@TommyLemon
Copy link
Collaborator

TommyLemon commented Jul 28, 2024

@gxmanito
升级到 6.4+ 后只支持 JDK 17+ 的版本,7.0+ 兼容 SpringBoot2 但 JDK 也必须是 17+

JOIN 带条件有占位符错误问题,我们再排查下,感谢反馈。

APP JOIN 和 LEFT JOIN 可以互换试试,结果应该一样,只是性能区别

@gxmanito
Copy link

#445 (comment)
可以试试两张表inner join且都有模糊查询,其中一张子表既有inner还有left join

@gxmanito
Copy link

#445 (comment)
另针对这个情况是这样复现的,当开启ENABLE_WITH_AS = true; 且子查询有where条件时,会报此错误No value specified for parameter 2
image

不加where不会报错
image

@TommyLemon
Copy link
Collaborator

TommyLemon commented Jul 28, 2024

@gxmanito WITH AS 确实有这个问题,可以先关掉,只是对性能有点影响

@TommyLemon
Copy link
Collaborator

TommyLemon commented Jul 28, 2024

@gxmanito 改用刚发布的 7.0.3-jdk1.8 或 7.0.3-jdk1.8.0.0 试试
https://github.com/Tencent/APIJSON/releases/tag/7.0.3-jdk1.8

如果有用到 apijson-framework,必须 6.3.0+,并且排除它依赖的 APIJSON ORM, apijson-column, apijson-router 都只能用 1.8.0+,并且排除它们依赖的 APIJSON ORM

        <dependency>
            <groupId>com.github.Tencent</groupId>
            <artifactId>APIJSON</artifactId>
            <version>7.0.3-jdk1.8</version>
        </dependency>
        <dependency>
            <groupId>com.github.APIJSON</groupId>
            <artifactId>apijson-framework</artifactId>
            <version>6.3.0</version>
            <exclusions>
                <exclusion>
                    <groupId>com.github.Tencent</groupId>
                    <artifactId>APIJSON</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.github.APIJSON</groupId>
            <artifactId>apijson-column</artifactId>
            <version>1.8.0</version>
            <exclusions>
                <exclusion>
                    <groupId>com.github.Tencent</groupId>
                    <artifactId>APIJSON</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.github.APIJSON</groupId>
            <artifactId>apijson-router</artifactId>
            <version>1.8.0</version>
            <exclusions>
                <exclusion>
                    <groupId>com.github.Tencent</groupId>
                    <artifactId>APIJSON</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

@TommyLemon
Copy link
Collaborator

TommyLemon commented Jul 28, 2024

@TommyLemon
Copy link
Collaborator

TommyLemon commented Jul 28, 2024

@gxmanito 复现了:
image

http://apijson.cn/api/?send=true&type=JSON&url=http%3A%2F%2Fapijson.cn%3A8080%2Fget&json={%22[]%22:{%22join%22:%22%26%2FUser%2C%26%2FPraise%2C%3C%2FComment%2C%3C%2FComment%3Ato%22,%22Moment%22:{%22content$%22:%22%25a%25%22,%22@column%22:%22id%2CuserId%2Ccontent%22},%22User%22:{%22sex%22:0,%22@column%22:%22id%2Cname%22,%22id@%22:%22%2FMoment%2FuserId%22},%22Praise%22:{%22momentId@%22:%22%2FMoment%2Fid%22,%22userId!%22:0},%22Comment%22:{%22momentId@%22:%22%2FMoment%2Fid%22,%22date$%22:%22%2520%25%22,%22@column%22:%22id%2CmomentId%2Ccontent%22},%22Comment:to%22:{%22momentId@%22:%22%2FMoment%2Fid%22,%22toId!%22:0,%22@column%22:%22id%2CtoId%2CmomentId%2Ccontent%22}},%22@explain%22:true}

删除这两个 INNER JOIN 对应任何一张表里的条件可以正常返回结果:

image

http://apijson.cn/api/?send=false&type=JSON&url=http%3A%2F%2Fapijson.cn%3A8080%2Fget&json={%22[]%22:{%22join%22:%22%26%2FUser%2C%26%2FPraise%2C%3C%2FComment%2C%3C%2FComment%3Ato%22,%22Moment%22:{%22content$%22:%22%25a%25%22,%22@column%22:%22id%2CuserId%2Ccontent%22},%22User%22:{%22@column%22:%22id%2Cname%22,%22id@%22:%22%2FMoment%2FuserId%22},%22Praise%22:{%22momentId@%22:%22%2FMoment%2Fid%22,%22userId!%22:0},%22Comment%22:{%22momentId@%22:%22%2FMoment%2Fid%22,%22date$%22:%22%2520%25%22,%22@column%22:%22id%2CmomentId%2Ccontent%22},%22Comment:to%22:{%22momentId@%22:%22%2FMoment%2Fid%22,%22toId!%22:0,%22@column%22:%22id%2CtoId%2CmomentId%2Ccontent%22}},%22@explain%22:true}

image https://apijson.cn/api/?send=false&type=JSON&url=http%3A%2F%2Fapijson.cn%3A8080%2Fget&json=%7B%0A%20%20%20%20%22%5B%5D%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22join%22%3A%20%22%26%2FUser%2C%26%2FPraise%2C%3C%2FComment%2C%3C%2FComment%3Ato%22%2C%0A%20%20%20%20%20%20%20%20%22Moment%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22content%24%22%3A%20%22%25a%25%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%40column%22%3A%20%22id%2CuserId%2Ccontent%22%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%22User%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22sex%22%3A%200%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%40column%22%3A%20%22id%2Cname%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22id%40%22%3A%20%22%2FMoment%2FuserId%22%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%22Praise%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22momentId%40%22%3A%20%22%2FMoment%2Fid%22%2C%20%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%22Comment%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22momentId%40%22%3A%20%22%2FMoment%2Fid%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22date%24%22%3A%20%22%2520%25%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%40column%22%3A%20%22id%2CmomentId%2Ccontent%22%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%22Comment%3Ato%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22momentId%40%22%3A%20%22%2FMoment%2Fid%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22toId!%22%3A%200%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%40column%22%3A%20%22id%2CtoId%2CmomentId%2Ccontent%22%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22%40explain%22%3A%20true%0A%7D

这个得继续排查是 AbstractSQLConfig 哪步出错的。
每个条件最后都会调用 getValue,里面 preparedValueList.add(value) 发生在同步拼接 JOIN SQL 片段时候
image

可能 getJoinString 对 LEFT/RIGHT 等 OUTER JOIN 处理条件时清空现有副表的 preparedValueList,然后加到主表,这块有问题
image

image image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants