alibaba / nacos

an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications.
https://nacos.io
Apache License 2.0
30k stars 12.8k forks source link

Unable to query historical details in the configuration of the extended multi data source plugin(多数据源插件扩展后配置中历史详情无法查询) #11218

Open sangyuan6122 opened 11 months ago

sangyuan6122 commented 11 months ago

BUG描述 使用多数据源插件扩展Oracle数据库支持后,public空间下的配置历史版本点击详情报错"Please check dataId, group or namespaceId.",其余命名空间下对应功能正常。 初步分析: 当使用默认命名空间public时,前端 tenant参数会给默认值空字符串,而对应的oracle库中tenant只能查出null,两者在代码中进行了相等判断,这里肯定不等,所以报错。 1)HistoryController类中getConfigHistoryInfo方法请求参数中tenant默认(public)为空字符串而则非null(很多处入参都如此操作); 2)Oralce数据库对空字符串和null没有区别查询出来的字段值都是null; 3)在HistoryService类中getConfigHistoryInfo调用了checkHistoryInfoPermission方法,此方法需入参tenant需等于数据库查出来的对应数据的tenant值,而Oracle库中查出来的是null和空字符串不相等,所以方法报错; 建议: 1)checkHistoryInfoPermission中对数据库中查询出tenant进行修正,如修改为:

private void checkHistoryInfoPermission(ConfigHistoryInfo configHistoryInfo, String dataId, String group,
            String namespaceId) throws AccessException {
        if (!Objects.equals(configHistoryInfo.getDataId(), dataId)
                || !Objects.equals(configHistoryInfo.getGroup(), group)
                || !Objects.equals(configHistoryInfo.getTenant() == null ? StringUtils.EMPTY : configHistoryInfo.getTenant(),namespaceId)) {
            throw new AccessException("Please check dataId, group or namespaceId.");
        }
    }

Desktop (please complete the following information):

sangyuan6122 commented 11 months ago

@i will solve it@

KomachiSion commented 11 months ago

多个PR中修复该问题, 但是我看了之后认为这是oracle插件的问题,需要在oracle插件中修复。

configHistoryInfo是数据库查出来的内容,tenant为null说明存储的namespace就是null, 如果是数据源插件的特殊处理, 应该在插件中修复。

KomachiSion commented 11 months ago

PR我都先关闭, 现在issue中讨论清楚

sangyuan6122 commented 11 months ago

多个PR中修复该问题, 但是我看了之后认为这是oracle插件的问题,需要在oracle插件中修复。

configHistoryInfo是数据库查出来的内容,tenant为null说明存储的namespace就是null, 如果是数据源插件的特殊处理, 应该在插件中修复。

现有的代码HistoryController中的getConfigHistoryInfo方法已经把输入为null的tenant修正为空字符串(个人理解),如下:

    @GetMapping
    @Secured(action = ActionTypes.READ, signType = SignType.CONFIG)
    public ConfigHistoryInfo getConfigHistoryInfo(@RequestParam("dataId") String dataId,
            @RequestParam("group") String group,
            @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant,
            @RequestParam("nid") Long nid) throws AccessException {
        return historyService.getConfigHistoryInfo(dataId, group, tenant, nid);
    }

多数据源插件的功能只能提供对应数据库的sql语句,并不能去修改Sql查出来的结果,而通过Sql语句在Oracle中查出来的只能是null,没有空字符串的概念。例如:select '' "A",null "B" from dual 这里属性A、B查出来都是null,这样情况插件中无法修复吧,怎么解决当前问题呢?

KomachiSion commented 11 months ago

api接口处的定义是nacos的openapi定义, null和''可以做等义。

但是configHistoryInfo是数据库查询的结果,既数据库的存储内容, 做了null=‘’的转译,会影响其他数据库的判断条件,因为其他数据库这两者不等价(只有Oracle等价),所以不能再PR那里中做处理, 需要让Oracle插件来做处理。

KomachiSion commented 11 months ago

换句话说, 从nacos的视角里, 这个值传给数据库不可能为null, 数据库存储的值也必定不为null, 既然是Oracle自己做的特殊处理,不管出于什么原因, Oracle需要把数据按照原样返还给nacos层面,这样才能够接耦清楚。

所以这个数据必须要感知Oracle的情况,才能做null-》‘’的等价转换,对nacos的处理过程中来看, 这个值输入时是‘’, 查询出来也应该是''

KomachiSion commented 11 months ago

换句话说, 从nacos的视角里, 这个值传给数据库不可能为null, 数据库存储的值也必定不为null, 既然是Oracle自己做的特殊处理,不管出于什么原因, Oracle需要把数据按照原样返还给nacos层面,这样才能够接耦清楚。

所以这个数据必须要感知Oracle的情况,才能做null-》‘’的等价转换,对nacos的处理过程中来看, 这个值输入时是‘’, 查询出来也应该是''

所以需要在Oracle插件层面做适配。

sangyuan6122 commented 11 months ago

传给数据库的值不可能为null这点没问题,但是Oracle存长度为0的字符串确实是null,这也是事实。当前从插件层面去解决确实最优,实际情况插件层面我们只能去修改对应的sql语句,无法对语句的结果进行修正。 configHistoryInfo做null=''的转义,应该是目前代价最小的方式,nacos存的时候已经明确无论是null还是''存的都是'',这里已经做了转义。而如果数据库查出来是''自然没问题,说明数据库也支持'',否则可能对接的数据库不支持长度为0的字符串,仅支持null,这里去做个容错是否可以呢 讨论这些还是希望能找一个解决方案,如果是插件层面解决具体怎么做呢?我们这边的客户用的都是Oracle,所以希望能用多数据源去做适配,能否加钉钉:sangyuan6122 详细聊下呢,拜谢!

KomachiSion commented 11 months ago

如果是做单独一个工程项目, 以代价最小的方式来实现没有问题, 但是这不是单个工程,而是针对所有数据库的场景, 单纯针对Oracle在通用核心处做特殊适配是不合理的, 主要是这个适配改变了数据库语义,这是不可行的。

既然是Oracle导致的问题,就应该去修复Oracle或者是Oracle的适配层,也即Oracle的插件层, 方法我并不清楚,因为Oracle的插件是由社区自行贡献的。

比如是否可以改下SQL,让Oracle返回的时候如果是null的话转换成''呢?

huangkemingyyds commented 11 months ago

若假如我再多一种数据源呢,是不是又得回来改这个方法。应该是Oracle插件里找到对应的sql,做修改比较合适,在插件层面做适配。

sangyuan6122 commented 10 months ago

如果能在多数据源插件层面做适配这个issue肯定就不提了,无论对sql语句进行怎样的处理,都不行,建议大佬们亲测下 例如:select nvl(tenant_id,'') 、select to_char(nvl(tenant_id,''))、select nvl(tenant_id,CHR(0))

KomachiSion commented 10 months ago

这肯定是能做适配的,如果不能适配就讨论通用的方法, 比如插件是否再开放一个resultHandler之类的东西,让插件来做这种结果转化。

但是肯定不能在core里去做这种改变语义的内容。

sangyuan6122 commented 10 months ago

插件中尝试了各种方法肯定是不能适配,或者给一个能适配的方法,主要是解决这个问题

huangkemingyyds commented 10 months ago

我认为可以基于PaginationHelper ,根据数据源 扩展出一个Oracle的实现类,并重写sql。在对应业务代码里,使用该实现类。