Skip to content

Commit 014b529

Browse files
authored
Merge branch 'IvorySQL:master' into master
2 parents 25caa78 + 3b6b0f1 commit 014b529

14 files changed

Lines changed: 664 additions & 0 deletions

File tree

CN/modules/ROOT/nav.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
** xref:master/oracle_compatibility/compat_read_only_view.adoc[20、视图只读]
3030
** xref:master/oracle_compatibility/with_function_procedure.adoc[21、WITH FUNCTION/PROCEDURE]
3131
** xref:master/oracle_compatibility/compat_create_index_online.adoc[22、索引 ONLINE 参数]
32+
** xref:master/oracle_compatibility/compat_stragg.adoc[23、STRAGG 函数]
3233
* 容器化与云服务
3334
** 容器化指南
3435
*** xref:master/containerization/k8s_deployment.adoc[K8S部署]
@@ -102,6 +103,7 @@
102103
**** xref:master/oracle_builtin_functions/sys_context.adoc[sys_context]
103104
**** xref:master/oracle_builtin_functions/userenv.adoc[userenv]
104105
**** xref:master/oracle_builtin_functions/rawtohex.adoc[rawtohex]
106+
**** xref:master/oracle_builtin_functions/stragg.adoc[stragg]
105107
*** xref:master/gb18030.adoc[国标GB18030]
106108
* 参考指南
107109
** xref:master/tools_reference.adoc[工具参考]

CN/modules/ROOT/pages/master/cloud_platform/ivorysql_cloud_installation.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,8 @@ https://github.com/IvorySQL/ivory-operator/tree/IVYO_REL_5_STABLE[https://github
364364

365365
如果服务器可以直接访问到docker hub,可以跳过该章节。否则需要在所有的K8S集群节点提前load 如下docker镜像
366366

367+
TIP: 国内用户也可使用 IvorySQL 社区的 Harbor 镜像服务拉取以下镜像,将 `docker.io/` 替换为 `registry.highgo.com/` 即可。
368+
367369
[literal]
368370
----
369371
docker.io/ivorysql/pgadmin:ubi8-9.9-5.0-1

CN/modules/ROOT/pages/master/containerization/docker_podman_deployment.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
= Docker & Podman 部署IvorySQL
66

7+
[TIP]
8+
====
9+
除 Docker Hub 外,IvorySQL 社区还为国内用户提供 Harbor 镜像服务。将本章节中的镜像地址前缀替换为 `registry.highgo.com/` 即可使用,例如 `registry.highgo.com/ivorysql/ivorysql:5.0-ubi8`。
10+
====
11+
712
== docker方式运行
813

914
** 从Docker Hub上获取IvorySQL镜像

CN/modules/ROOT/pages/master/getting-started/quick_start.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ ivorysql 3238 1551 0 20:35 pts/0 00:00:00 grep --color=auto postgres
116116
$ docker pull ivorysql/ivorysql:5.0-ubi8
117117
```
118118

119+
TIP: 国内用户也可使用 IvorySQL 社区的 Harbor 镜像服务加速拉取,例如:`docker pull registry.highgo.com/ivorysql/ivorysql:5.0-ubi8`。
120+
119121
** 运行IvorySQL
120122
```
121123
$ docker run --name ivorysql -p 5434:5432 -e IVORYSQL_PASSWORD=your_password -d ivorysql/ivorysql:5.0-ubi8

CN/modules/ROOT/pages/master/installation_guide.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ $ sudo dnf install -y ivorysql5-5.0
4343
$ docker pull ivorysql/ivorysql:5.0-ubi8
4444
```
4545

46+
TIP: 国内用户也可使用 IvorySQL 社区的 Harbor 镜像服务加速拉取,将上述地址替换为 `registry.highgo.com/ivorysql/ivorysql:5.0-ubi8` 即可。
47+
4648
** 运行IvorySQL
4749
```
4850
$ docker run --name ivorysql -p 5434:5432 -e IVORYSQL_PASSWORD=your_password -d ivorysql/ivorysql:5.0-ubi8
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
:sectnums:
2+
:sectnumlevels: 5
3+
4+
:imagesdir: ./_images
5+
6+
= STRAGG 聚合函数的实现
7+
8+
== 目的
9+
10+
IvorySQL 在 `contrib/ivorysql_ora` 扩展中新增 `sys.stragg(text)` 聚合函数,
11+
实现与 Oracle 同名函数一致的字符串聚合行为:将组内所有非 NULL 值用逗号连接为一个字符串。
12+
13+
== 实现说明
14+
15+
=== 状态布局设计
16+
17+
STRAGG 的聚合状态复用 PostgreSQL 内置 `string_agg` 所使用的 `StringInfo` 布局,
18+
从而可以直接引用 `string_agg_finalfn`、`string_agg_combine`、
19+
`string_agg_serialize`、`string_agg_deserialize` 四个内置函数,
20+
无需另行实现 finalize、并行合并及序列化逻辑。
21+
22+
`StringInfo` 状态的约定如下:
23+
24+
[source,c]
25+
----
26+
/*
27+
* data = "," + val1 + "," + val2 + ...
28+
* 首元素前也预置一个逗号,便于 finalfn 统一处理
29+
* cursor = 1
30+
* 记录前导分隔符的字节长度(逗号占 1 字节)
31+
* string_agg_finalfn 返回 &data[cursor],即自动去掉前导逗号
32+
*/
33+
----
34+
35+
=== 转换函数(`stragg_transfn`)
36+
37+
转换函数位于
38+
`contrib/ivorysql_ora/src/builtin_functions/misc_functions.c`。
39+
40+
[source,c]
41+
----
42+
PG_FUNCTION_INFO_V1(stragg_transfn);
43+
44+
Datum
45+
stragg_transfn(PG_FUNCTION_ARGS)
46+
{
47+
StringInfo state;
48+
MemoryContext aggcontext;
49+
MemoryContext oldcontext;
50+
51+
if (!AggCheckCallContext(fcinfo, &aggcontext))
52+
elog(ERROR, "stragg_transfn called in non-aggregate context");
53+
54+
state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0);
55+
56+
/* 跳过 NULL 输入,与 Oracle STRAGG 行为一致 */
57+
if (!PG_ARGISNULL(1))
58+
{
59+
text *value = PG_GETARG_TEXT_PP(1);
60+
61+
if (state == NULL)
62+
{
63+
oldcontext = MemoryContextSwitchTo(aggcontext);
64+
state = makeStringInfo();
65+
MemoryContextSwitchTo(oldcontext);
66+
67+
/* 首个值:预置分隔符,cursor 记录其长度 */
68+
appendStringInfoChar(state, ',');
69+
state->cursor = 1;
70+
}
71+
else
72+
{
73+
appendStringInfoChar(state, ',');
74+
}
75+
76+
appendBinaryStringInfo(state, VARDATA_ANY(value), VARSIZE_ANY_EXHDR(value));
77+
}
78+
79+
if (state)
80+
PG_RETURN_POINTER(state);
81+
PG_RETURN_NULL();
82+
}
83+
----
84+
85+
关键设计点:
86+
87+
* 状态在聚合上下文(`aggcontext`)中分配,生命周期覆盖整个聚合过程。
88+
* `StringInfo` 内部缓冲区按需翻倍扩容,追加操作均摊 O(1),总时间复杂度 O(N),
89+
优于纯 SQL 拼接方案的 O(N²)。
90+
* NULL 输入由 `PG_ARGISNULL(1)` 判断并跳过,状态不受污染。
91+
92+
=== SQL 定义
93+
94+
转换函数和聚合定义位于
95+
`contrib/ivorysql_ora/src/builtin_functions/builtin_functions--1.0.sql`。
96+
97+
[source,sql]
98+
----
99+
CREATE FUNCTION sys.stragg_transfn(internal, text)
100+
RETURNS internal
101+
AS 'MODULE_PATHNAME', 'stragg_transfn'
102+
LANGUAGE C
103+
CALLED ON NULL INPUT
104+
PARALLEL SAFE;
105+
106+
CREATE AGGREGATE sys.stragg(text) (
107+
SFUNC = sys.stragg_transfn,
108+
STYPE = internal,
109+
FINALFUNC = string_agg_finalfn,
110+
COMBINEFUNC = string_agg_combine,
111+
SERIALFUNC = string_agg_serialize,
112+
DESERIALFUNC = string_agg_deserialize,
113+
PARALLEL = SAFE
114+
);
115+
----
116+
117+
`FINALFUNC`、`COMBINEFUNC`、`SERIALFUNC`、`DESERIALFUNC` 均直接引用 PostgreSQL
118+
内置的 `string_agg` 系列函数,因为 STRAGG 与 `string_agg` 使用完全相同的
119+
`StringInfo` 状态格式。
120+
121+
=== 并行聚合支持
122+
123+
通过指定 `COMBINEFUNC = string_agg_combine` 和序列化/反序列化函数,
124+
STRAGG 支持 PostgreSQL 的并行聚合执行路径。各并行工作进程独立维护局部
125+
`StringInfo` 状态,最终由 leader 进程通过 `string_agg_combine` 合并。
126+
127+
=== 回归测试
128+
129+
测试文件:`contrib/ivorysql_ora/sql/ora_stragg.sql`,
130+
对应预期输出:`contrib/ivorysql_ora/expected/ora_stragg.out`。
131+
132+
在 `Makefile` 的 `ORA_REGRESS` 列表中已加入 `ora_stragg`,
133+
可通过以下命令运行:
134+
135+
[source,bash]
136+
----
137+
cd contrib/ivorysql_ora
138+
make installcheck
139+
----
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
:sectnums:
2+
:sectnumlevels: 5
3+
4+
:imagesdir: ./_images
5+
6+
= STRAGG 聚合函数
7+
8+
== 目的
9+
10+
本文档说明 IvorySQL 中 `sys.stragg` 聚合函数的功能,该函数以兼容 Oracle 的方式
11+
将组内多行字符串值拼接为一个逗号分隔的字符串。
12+
13+
== 功能说明
14+
15+
* `sys.stragg(text)` 是一个聚合函数,将同一分组中所有非 NULL 的文本值用逗号(`,`)连接。
16+
* NULL 值被自动忽略,不出现在结果中,也不产生多余的逗号。
17+
* 分组内所有值均为 NULL 或分组为空集时,返回 `NULL`(而非空字符串)。
18+
* 不保证输出顺序;如需确定顺序,可在聚合调用中使用 `ORDER BY` 子句(PostgreSQL 扩展语法)。
19+
* 函数定义于 `sys` 模式,在 Oracle 兼容模式(不需要sys前缀)和 PG 模式下均可使用。
20+
21+
== 语法
22+
23+
[source,sql]
24+
----
25+
sys.stragg(expr [ORDER BY sort_expr [ASC | DESC] [, ...]])
26+
----
27+
28+
[cols="1,3"]
29+
|===
30+
|参数 |说明
31+
32+
|`expr`
33+
|任意可隐式转换为 `text` 的表达式;NULL 值将被跳过。
34+
35+
|`ORDER BY`
36+
|可选,指定组内拼接顺序。省略时顺序不确定。
37+
|===
38+
39+
返回类型:`text`
40+
41+
== 测试用例
42+
43+
=== 测试环境准备
44+
45+
[source,sql]
46+
----
47+
CREATE TABLE stragg_test (dept TEXT, name TEXT);
48+
INSERT INTO stragg_test VALUES ('HR', 'Alice');
49+
INSERT INTO stragg_test VALUES ('HR', 'Bob');
50+
INSERT INTO stragg_test VALUES ('HR', 'Carol');
51+
INSERT INTO stragg_test VALUES ('IT', 'Dave');
52+
INSERT INTO stragg_test VALUES ('IT', 'Eve');
53+
----
54+
55+
=== 基础聚合
56+
57+
[source,sql]
58+
----
59+
-- 单组聚合,使用 ORDER BY 保证结果确定
60+
SELECT sys.stragg(name ORDER BY name) FROM stragg_test WHERE dept = 'HR';
61+
-- 期望:Alice,Bob,Carol
62+
63+
-- 配合 GROUP BY 按部门分组
64+
SELECT dept, sys.stragg(name ORDER BY name)
65+
FROM stragg_test
66+
GROUP BY dept
67+
ORDER BY dept;
68+
-- 期望:
69+
-- HR | Alice,Bob,Carol
70+
-- IT | Dave,Eve
71+
----
72+
73+
=== NULL 值处理
74+
75+
[source,sql]
76+
----
77+
-- 插入一行 NULL 值
78+
INSERT INTO stragg_test VALUES ('HR', NULL);
79+
80+
-- NULL 被忽略,结果与无 NULL 时相同
81+
SELECT sys.stragg(name ORDER BY name) FROM stragg_test WHERE dept = 'HR';
82+
-- 期望:Alice,Bob,Carol
83+
84+
-- 所有值均为 NULL 时返回 NULL
85+
SELECT sys.stragg(name) IS NULL FROM stragg_test WHERE name IS NULL;
86+
-- 期望:t
87+
----
88+
89+
=== 空集处理
90+
91+
[source,sql]
92+
----
93+
-- 无匹配行时返回 NULL
94+
SELECT sys.stragg(name) IS NULL FROM stragg_test WHERE dept = 'NONE';
95+
-- 期望:t
96+
----
97+
98+
=== 单值
99+
100+
[source,sql]
101+
----
102+
-- 组内只有一个值时,结果中不含逗号
103+
SELECT sys.stragg(name) FROM stragg_test WHERE dept = 'IT' AND name = 'Dave';
104+
-- 期望:Dave
105+
----
106+
107+
=== PG 兼容模式下使用
108+
109+
[source,sql]
110+
----
111+
SET ivorysql.compatible_mode = pg;
112+
SELECT sys.stragg(name ORDER BY name) FROM stragg_test WHERE dept = 'HR';
113+
-- 期望:Alice,Bob,Carol
114+
RESET ivorysql.compatible_mode;
115+
----
116+
117+
=== 测试环境清理
118+
119+
[source,sql]
120+
----
121+
DROP TABLE stragg_test;
122+
----
123+
124+
== 与 Oracle 的行为差异
125+
126+
[cols="1,2,2"]
127+
|===
128+
|场景 |Oracle STRAGG |IvorySQL sys.stragg
129+
130+
|空集
131+
|`NULL`
132+
|`NULL`(一致)
133+
134+
|全 NULL 分组
135+
|`NULL`
136+
|`NULL`(一致)
137+
138+
|NULL 值
139+
|忽略
140+
|忽略(一致)
141+
142+
|拼接顺序
143+
|不确定
144+
|不确定;可用 `ORDER BY` 子句指定
145+
146+
|`ORDER BY` 子句
147+
|不支持(官方无此语法)
148+
|支持(PostgreSQL 聚合扩展语法)
149+
|===
150+
151+
== 与 LISTAGG 的比较
152+
153+
[cols="1,2,2"]
154+
|===
155+
|特性 |`LISTAGG`(Oracle / IvorySQL)|`STRAGG`(Oracle / IvorySQL)
156+
157+
|分隔符
158+
|可指定任意分隔符
159+
|固定为逗号
160+
161+
|`ORDER BY`
162+
|通过 `WITHIN GROUP (ORDER BY ...)` 指定
163+
|不保证顺序(IvorySQL 支持 PostgreSQL 聚合 `ORDER BY`)
164+
165+
|结果长度限制
166+
|Oracle 限制 4000 字节(IvorySQL 通过 `ora_listagg_check` 检查)
167+
|无限制
168+
169+
|使用场景
170+
|需要精确控制分隔符和顺序的场合
171+
|快速拼接,历史遗留代码迁移
172+
|===

EN/modules/ROOT/nav.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
** xref:master/oracle_compatibility/compat_read_only_view.adoc[20、Read Only View]
3030
** xref:master/oracle_compatibility/with_function_procedure_en.adoc[21、WITH FUNCTION/PROCEDURE]
3131
** xref:master/oracle_compatibility/compat_create_index_online.adoc[22、ONLINE Parameter for CREATE INDEX]
32+
** xref:master/oracle_compatibility/compat_stragg.adoc[23、STRAGG function]
3233
* Containerization and Cloud Service
3334
** Containerization
3435
*** xref:master/containerization/k8s_deployment.adoc[K8S deployment]
@@ -102,6 +103,7 @@
102103
*** xref:master/oracle_builtin_functions/sys_context.adoc[sys_context]
103104
*** xref:master/oracle_builtin_functions/userenv.adoc[userenv]
104105
*** xref:master/oracle_builtin_functions/rawtohex.adoc[rawtohex]
106+
*** xref:master/oracle_builtin_functions/stragg.adoc[stragg]
105107
** xref:master/gb18030.adoc[GB18030 Character Set]
106108
* Reference
107109
** xref:master/tools_reference.adoc[Tool Reference]

EN/modules/ROOT/pages/master/cloud_platform/ivorysql_cloud_installation.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,8 @@ https://github.com/IvorySQL/ivory-operator/tree/IVYO_REL_5_STABLE[https://github
363363

364364
If your servers have direct access to Docker Hub, you can skip this step. Otherwise, preload the following images on every node in the Kubernetes cluster.
365365

366+
TIP: Users in China can pull the following images from the IvorySQL community Harbor registry by replacing the `docker.io/` prefix with `registry.highgo.com/`.
367+
366368
[literal]
367369
----
368370
docker.io/ivorysql/pgadmin:ubi8-9.9-5.0-1

0 commit comments

Comments
 (0)