论lightdb/postgresql中的search_path及实现兼容性管理

  上一篇介绍了lightdb/postgresql public、pg_catalog schema的区别及pg_namespace概念,因为最近几个版本开发下来,遇到了很多兼容性挑战。所以这一节来专门讨论一下search_path,以及它是如何实现版本隔离和理论上可实现的租户隔离(实际上,lightdb在22.1-22.2实现了,但是不够放心22.3临时取消了,22.4大概率会加回来,因为这涉及到database级别immutable,所以比较复杂)。

  search_path的用途是在语义分析的时候,确定函数、操作符、表等对象时让哪个命名空间中的生效。在Lightdb中,默认情况下,其取值为 "$user", public, lt_catalog。oracle模式,一般建议用户设置为"$user", public, lt_catalog, oracle。mysql模式则设置为"$user", public, lt_catalog, mysql。如果用户要添加一些额外的mysql/oracle兼容函数,可以考虑创建一个新的namepace,如$user_mysql_ext,然后在其中创建兼容函数,并添加到search_path的最后。这样当lightdb新版实现了兼容函数时,因为它会被添加到lt_catalog/mysql/oracle至少之一下,内置版本既能覆盖自定义版本,也不会带来额外的兼容性问题。可以从机制上很好的解决该兼容问题。

  虽然search_path能够解决该问题,但是由于它太灵活,一定要避免随意设置,比如避免设置同时包含oracle和mysql,当然lightdb在内部就进行了规避,如果是开源postgresql,这是需要注意的。

  对于oracle兼容,上述机制对自定义函数、存储过程、视图等都可以比较好的解决。但是对于oracle包(package),无论是内置包还是用户自定义包,事情要复杂一点。在lightdb中,oracle内置包按照schema存储,走search_path机制,自定义包是在lt_package中。包括集合类型、自定义类型、sys_refcursor、函数、过程等。

  null以及特殊的一些函数如substring负数索引等存在一词多义的情况是兼容的主要问题,如下:

2022-11-14 20:04:19.330824T,DBeaver 22.0.2 - Metadata <lt_test>,zjh,lt_test,10.188.137.149(52068),client backend,PARSE,00000,2022-11-14 20:04:19 CST,0,180444,LOG:  duration: 0.636 ms  parse <unnamed>: SELECT t.oid,t.*,c.relkind,format_type(nullif(t.typbasetype, 0), t.typtypmod) as base_type_name, d.description
    FROM pg_catalog.pg_type t
    LEFT OUTER JOIN pg_catalog.pg_type et ON et.oid=t.typelem 
    LEFT OUTER JOIN pg_catalog.pg_class c ON c.oid=t.typrelid
    LEFT OUTER JOIN pg_catalog.pg_description d ON t.oid=d.objoid
    WHERE t.typname IS NOT NULL
    AND (c.relkind IS NULL OR c.relkind = 'c') AND (et.typcategory IS NULL OR et.typcategory <> 'C')

  因为不止postgres库会被第三方工具访问,业务库也可能会,如上所示,而且是三方初始化的一部分,无法修改SQL语句(通过rewrite插件也不行,因为不知道哪些是初始化的一部分,哪些不是)。所以还不能简单的将库的默认兼容改成oracle或mysql,否则就可能结果不正确。所以通过search_path执行是个正确的策略,不过幸好jdbc和libpq都可以通过option -c选项设置。

jdbc.url=jdbc:postgresql://124.220.47.210:11345/defaultdb?options=-c%20search_path=\"$user\",public,oracle,lt_catalog%20-c%20statement_timeout=90000&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&lightdbSyntaxCompatibleType=oracle
  如果是mysql,上面的oracle改成mysql即可。

  对于is null,在oracle下如下:

zjh@lt_test=# set lightdb_syntax_compatible_type = "Oracle";
SET
zjh@lt_test=# select '' is null;
 ?column? 
----------
 t
(1 row)

  在pg/mysql模式下,如下:

zjh@lt_test=# select '' is null;
 ?column? 
----------
 f
(1 row)

  所以,可能就会出现不同的结果。

posted @ 2022-09-04 21:12  zhjh256  阅读(299)  评论(0编辑  收藏  举报