象牙数据库 IvorySQL v2.3 特性浅析

象牙数据库 IvorySQL v2.3 特性浅析

严少安 Lv.6
ivorysql-logo.jpg

引言

第一次接触到 IvorySQL 数据库是在编写 2022年5月中国数据库排行榜 的时候,那时候 IvorySQL 刚上榜,时隔一年多,IvorySQL 的排名已上升不少,更是在最近发布的 2023开源数据库排行 中,跻身三十强。过去一年,IvorySQL 在 Github 上的源码仓库 Star 数,从 90 上升到今天的 560,真的很棒!

ivorysqlstar.png

象牙数据库 IvorySQL

IvorySQL (Ivory 译为,象牙) 是由瀚高 (Highgo) 发起的基于 PostgreSQL 的衍生开源项目,采用 Apache-2.0 license,100% 兼容原生 PostgreSQL。IvorySQL 导入并增强了 Orafce 扩展插件,以此支持、兼容 Oracle 的诸多特性,如 PACKAGE、数据类型、转换函数等。IvorySQL 增加了 compatible_mode 开关,可以切换 PostgreSQL 和 Oracle 兼容模式。在 Oracle 兼容性方面,IvorySQL 还增加了独有的 PL/iSQL 过程语言,以此来支持 Oracle 的 PL/SQL 语法和 Oracle 风格的 PACKAGES。在即将发布的 IvorySQL v3.0 中,将支持双端口,即端口 5333 用于 IvorySQL 连接,端口 1521 用于 Oracle 模式连接。

2021 年 12 月 15 日,发布了 IvorySQL 的第一个版本。今年 6 月 28 日,发布了 IvorySQL 2.3,下图为目前 IvorySQL 的发布时间线。

ivorysql-timeline.png

IvorySQL 源码编译

IvorySQL v2.3 是基于 PostgreSQL 15.3 开发的,所以 IvorySQL 编译过程大致与 PG 15 类似,前段时间写过一篇 PG 15 源码编译的文章 《【PG15】【番外篇】自定义 Dockerfile 构建 PostgreSQL 15 编译版 Docker 镜像》 ,这里只再强调一下区别,详细过程不再复述。

  1. 下载 IvorySQL 2.3 的源码包
1
wget https://github.com/IvorySQL/IvorySQL/archive/refs/tags/Ivory_REL_$PG_VERSION.tar.gz
  1. IvorySQL 的默认监听端口为 5333,以区别于 PostgreSQL 的 5432。服务启动后,在日志文件中可看到监听端口,或直接查看 postgres 进程的端口也可以看到 5333。
ivory5333.png
  1. 源码包 (Ivory_REL_2_3.tar.gz) 和解压后的文件夹 (IvorySQL-Ivory_REL_2_3) 名称不一致,在编译过程中需要注意一下,希望官方可以将命名统一。(IvorySQL#332 )

  2. IvorySQL 编译时,使用命令 make 会将所有 contrib 扩展进行编译,而在 PostgreSQL 中需要使用 make world 才可以。

  3. IvorySQL 编译时,会默认将扩展插件 orafce 也进行编译,并在 IvorySQL 启动时默认 enable (1.x 是没有问题的,2.x 暂不会自动加载)。(IvorySQL#183 )

IvorySQL 特性

IvorySQL 数据库

IvorySQL 编译后,便可初始化数据库,并启动数据库。

1
2
initdb --pgdata=$PGDATA
pg_ctl -D $PGDATA -l logfile start

启动后,分别查看版本,用户,数据库,模式,扩展,兼容模式信息。

ivoryversion.png

可以看到,IvorySQL 数据库已默认创建超级用户 ivorysql,并以 template1 为模板创建数据库 ivorysql

IvorySQL: 表 pg_catalog.pg_proc 新增字段 proaccess

对比 IvorySQL v2.3 和 PostgreSQL v15.2 可以发现,在 IvorySQL 中,表 pg_catalog.pg_proc 增加了一个字段 proaccess

  • IvorySQL
ivorydiffproc.png
  • PostgreSQL
ivorydiffprocpg.png

IvorySQL: 新增表 pg_catalog.pg_variable

在 IvorySQL 中新增表 pg_catalog.pg_variable,这在 PostgreSQL 中是没有的。

1
2
3
postgres=# select * from pg_catalog.pg_variable;
ERROR: relation "pg_catalog.pg_variable" does not exist
LINE 1: select * from pg_catalog.pg_variable;

表结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ivorysql=# \d+ pg_catalog.pg_variable
Table "pg_catalog.pg_variable"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
----------------+--------------+-----------+----------+---------+----------+-------------+--------------+-------------
oid | oid | | not null | | plain | | |
varname | name | | not null | | plain | | |
varnamespace | oid | | not null | | plain | | |
vartype | oid | | not null | | plain | | |
vartypmod | integer | | not null | | plain | | |
varowner | oid | | not null | | plain | | |
varcollation | oid | | not null | | plain | | |
varisnotnull | boolean | | not null | | plain | | |
varisimmutable | boolean | | not null | | plain | | |
vareoxaction | "char" | | not null | | plain | | |
vardefexpr | pg_node_tree | C | | | extended | | |
varacl | aclitem[] | | | | extended | | |
varaccess | "char" | | | | plain | | |
Indexes:
"pg_variable_oid_index" PRIMARY KEY, btree (oid)
"pg_variable_varname_nsp_index" UNIQUE CONSTRAINT, btree (varname, varnamespace)
Access method: heap

IvorySQL: 兼容模式 compatible_mode

IvorySQL 中增加了配置项 compatible_mode 用于选择兼容 PostgreSQL 模式还是 Oracle 模式,默认值为 postgres,配置方法:

  1. 在初始化时,initdb 后接选项 -c, --compatible-mode = oracle,如果不指定,则默认使用 postgres

  2. 可以在配置文件中进行修改:

1
2
3
[ivorysql@centos7 data]$ grep mode postgresql.conf
# default configuration for compatibility mode
#compatible_mode = postgres # postgres, oracle
  1. 也可以在会话中进行修改:
1
2
3
4
5
6
7
8
ivorysql=# set compatible_mode = 'oracle' ;
SET

ivorysql=# show compatible_mode ;
compatible_mode
-----------------
oracle
(1 row)

IvorySQL: orafce 扩展

前文提到 IvorySQL 通过支持、增强 orafce 扩展从而对 Oracle 进行兼容,但是在 IvorySQL 2.3 中,这个插件并非开箱即用,而是需要手动安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ivorysql=# \dx
List of installed extensions
Name | Version | Schema | Description
---------+---------+------------+------------------------------
plisql | 1.0 | pg_catalog | PL/iSQL procedural language
plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
(2 rows)

ivorysql=# CREATE EXTENSION orafce;
CREATE EXTENSION
ivorysql=#
ivorysql=# \dx
List of installed extensions
Name | Version | Schema | Description
---------+---------+------------+-----------------------------------------------------------------------------------------------
orafce | 3.18 | public | Functions and operators that emulate a subset of functions and packages from the Oracle RDBMS
plisql | 1.0 | pg_catalog | PL/iSQL procedural language
plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
(3 rows)

至于不能“开箱即用”的原因,则是因为一个 Bug,具体可参考 issue:Initdb -c Oracle failed to load orafce automatically#183

IvorySQL: Oracle 兼容性 – 包 (PACKAGE)

这个特性在文章 IvorySQL功能点介绍–软件包 中已经介绍地很详细了,这里需要强调的是,如果想要使用 PACKAGE,那么首先需要将兼容模式切换到 oracle,并且需要确认已经安装扩展 orafce。

这里借用文中的案例做以演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- create table
CREATE TABLE books (
id INT,
title VARCHAR2(100),
author VARCHAR2(100),
price NUMBER);

-- insert data
INSERT INTO books VALUES (10, 'The Hobbit', 'J. R. R. Tolkien', 10.0);
INSERT INTO books VALUES (11, 'Winnie-the-Pooh', 'A. A. Milne', 5.0);
INSERT INTO books VALUES (12, 'Peter Pan', 'James Matthew Barrie', 4.0);
INSERT INTO books VALUES (13, 'Charlie and the Chocolate Factory', 'Roald Dahl', 5.0);
INSERT INTO books VALUES (14, 'SThe Jungle Book', 'Rudyard Kipling', 9.0);
INSERT INTO books VALUES (15, 'The Little White Bird', 'James Matthew Barrie', 3.0);

-- create package
CREATE OR REPLACE PACKAGE mybooks AS
CURSOR booksinfo IS SELECT * from BOOKS;

PROCEDURE list_books;
FUNCTION add_book(title VARCHAR2(100), author VARCHAR2(100),
price NUMBER) RETURN bool;
PROCEDURE remove_book(book_title VARCHAR2(100));
END;
/
ivorydpkg.png

BTW, 创建包时只能使用语法 CREATE [OR REPLACE] PACKAGE,目前尚不支持 CREATE [IF NOT EXISTS] PACKAGE 语法。

1
2
3
4
5
ivorysql=# drop package if exists mybooks;
ERROR: syntax error at or near "exists"
LINE 1: drop package if exists mybooks;
^
ivorysql=#

IvorySQL: 元命令 \dPKG

上图中已经演示 \dPKG 的用法,这个元命令是 IvorySQL 特有的,用来查看已经创建包的相关信息。

这里还有个小插曲,因为这条命令在官方文档中并无体现,所以翻了下源码,然后就发现了代码注释中的 typo,顺手提了个 PR #331 ,不知是否会顺利 merge。(2023-07-28, PR 已经 merge,IvorySQL 研发响应蛮快 👍‍)

此外,关于 PACKAGE 的信息也可直接查询表 pg_catalog.pg_package

1
2
3
4
5
6
7
8
9
10
11
12
ivorysql=# select * from pg_catalog.pg_package;
oid | pkgname | pkgnamespace | pkgowner | pkgsecdef | pkgspec | pkgbody | pkgaccessor | pkgacl
-------+---------+--------------+----------+-----------+-------------------------------------------------------------------+---------+-------------+--------
19041 | mybooks | 2200 | 10 | t | CREATE OR REPLACE PACKAGE mybooks AS +| | |
| | | | | CURSOR booksinfo IS SELECT * from BOOKS; +| | |
| | | | | +| | |
| | | | | PROCEDURE list_books; +| | |
| | | | | FUNCTION add_book(title VARCHAR2(100), author VARCHAR2(100), +| | |
| | | | | price NUMBER) RETURN bool; +| | |
| | | | | PROCEDURE remove_book(book_title VARCHAR2(100)); +| | |
| | | | | END | | |
(1 row)

IvorySQL: 全局唯一索引 (Global Unique Index)

IvorySQL 支持在分区表上创建跨分区的唯一性检查,索引创建成功后,在插入或者更改数据时,如果有重复值将报错。可用于对非分区键的其他列做唯一性检查。举例如下:

  • PG

可以正常创建分区表,但不支持 global 语法,也无法只对 v 列创建唯一索引,因为它不是分区键。

ivoryglobalindexpg1.png

只能在分区键 d 列创建分区索引 idx_g_u。所有测试数据均可写入,但可以看到 v 列存在重复值。

ivoryglobalindexpg2.png
  • IvorySQL

可以正常创建分区表,以及全局唯一索引 idx_g_u

ivoryglobalindexIvory1.png

接下来写入测试数据,第一部分正常写入,第二部分报错,提示重复的值违反唯一约束。查询表 tpg 中的数据,发现只有第一部分测试数据写入成功。

ivoryglobalindexIvory2.png

注:

在本例中,relkind 表示对象类型,relkind 的三个取值含义分别为:i 表示索引 (index),I 表示分区索引 (partitioned index),g 表示全局索引 (global index)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ivorysql=# \dt+ pg_class
********* QUERY **********
SELECT n.nspname as "Schema",
c.relname as "Name",
CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index' WHEN 'g' THEN 'global index' WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table' WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table' WHEN 'I' THEN 'partitioned index' END as "Type",
pg_catalog.pg_get_userbyid(c.relowner) as "Owner",
CASE c.relpersistence WHEN 'p' THEN 'permanent' WHEN 't' THEN 'temporary' WHEN 'u' THEN 'unlogged' END as "Persistence",
am.amname as "Access method",
pg_catalog.pg_size_pretty(pg_catalog.pg_table_size(c.oid)) as "Size",
pg_catalog.obj_description(c.oid, 'pg_class') as "Description"
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
WHERE c.relkind IN ('r','p','t','s','')
AND c.relname OPERATOR(pg_catalog.~) '^(pg_class)$' COLLATE pg_catalog.default
AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY 1,2;
**************************

List of relations
Schema | Name | Type | Owner | Persistence | Access method | Size | Description
------------+----------+-------+----------+-------------+---------------+--------+-------------
pg_catalog | pg_class | table | ivorysql | permanent | heap | 144 kB |
(1 row)

期待改进

  1. Github 上的 Release Notes 展示格式不够友好,建议调整一下,会显得更加专业。
  2. 最好能够搭建 IvorySQL 自己的 yum 仓库, highgo 上的信息不完全准确。
  3. IvorySQL 的文档中心亟需迭代更新,建议多增加“特色”功能介绍与演示,期待新版文档站点上线。

The End.

作为国产开源关系型数据库产品,在竞争如此激烈的竞“技”场,IvorySQL 要奋力加油啊!
期待 IvorySQL 3.0 的发布,并期望带来更多“惊喜”的新特性。

REF


https://www.modb.pro/db/1684535632080216064

  • Title: 象牙数据库 IvorySQL v2.3 特性浅析
  • Author: 严少安
  • Created at: 2023-07-27 20:17:26
  • Updated at: 2023-07-27 20:17:26
  • Link: https://shawnyan.cn/2023/ivorysql/ivorysql-v2.3-feature/
  • License: This work is licensed under CC BY-NC-SA 4.0.
 Comments