Skip to content

Commit b9cad5e

Browse files
authored
Merge branch 'master' into addon
2 parents 597b442 + 40dc36c commit b9cad5e

File tree

12 files changed

+618
-94
lines changed

12 files changed

+618
-94
lines changed

CN/modules/ROOT/nav.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,12 @@
3333
**** xref:master/6.3.6.adoc[函数与存储过程]
3434
**** xref:master/6.3.7.adoc[嵌套子函数]
3535
**** xref:master/6.3.8.adoc[Force View]
36+
**** xref:master/6.3.9.adoc[大小写转换]
3637
*** xref:master/6.4.adoc[国标GB18030]
38+
*** 内置函数
39+
**** xref:master/6.4.1.adoc[sys_context]
40+
**** xref:master/6.4.2.adoc[userenv]
41+
*** xref:master/6.5.adoc[国标GB18030]
3742
** Oracle兼容功能列表
3843
*** xref:master/7.1.adoc[1、框架设计]
3944
*** xref:master/7.2.adoc[2、GUC框架]
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
:sectnums:
2+
:sectnumlevels: 5
3+
4+
:imagesdir: ./_images
5+
6+
= 引用标识符大小写转换
7+
8+
== 目的
9+
10+
为了满足Oracle的引用标识符大小写兼容,ivorysql设计了三种引用标识符的大小写转换模式。
11+
12+
== 实现说明
13+
14+
如果在数据库初始化时附加了参数 `-C`,值可以为 `normal/interchange/lowercase`,则代码中 `Intidb.c->main()` 函数会处理该参数,根据参数值设置全局变量 `caseswitchmode`。然后 `initdb` 命令会以 `-boot` 模式启动一个 `psotgres` 进程用于设置 `template1` 模板数据库,同时赋予参数 `-C ivorysql.identifier_case_switch=caseswitchmode` 给新进程。
15+
16+
这个新启动的后端进程会通过下面的代码将 `identifier_case_switch` 信息写入 `pg_control` 文件:
17+
18+
```
19+
BootstrapModeMain() -> BootStrapXLOG();
20+
/* save database compatible level value */
21+
ControlFile->dbmode = bootstrap_database_mode;
22+
ControlFile->casemode = identifier_case_switch;
23+
24+
/* some additional ControlFile fields are set in WriteControlFile() */
25+
WriteControlFile();
26+
```
27+
28+
当用户使用 `pg_ctl` 命令启动数据库时,`postmaster` 进程会读取 `pg_control` 文件的内容,代码调用路径为:
29+
30+
```
31+
PostmasterMain()-->SetCaseGucOption()-->GetCaseSwitchModeFromControl()
32+
```
33+
34+
读取到参数值后调用 `SetConfigOption()` 函数进行赋值。
35+
36+
在每个新的后端进程开始的时候,由于是从 `postmaster` 进程 `fork` 而来,会自动拥有相同的 `ivorysql.identifier_case_switch` 参数值。首先处理 `startup` 包,其中如果包含 `database` 或者 `user` 这两个参数,则根据 `identifier_case_switch` 对参数值做相应的处理。
37+
38+
源代码为:
39+
40+
```
41+
BackendMain()->BackendInitialize()-->ProcessStartupPacket()
42+
```
43+
44+
另外,在处理用户的 SQL 语句时,如果包含标识符,也会进行同样的处理,代码分布在:
45+
SplitIdentifierString(),quoteOneName() 和 SplitGUCList() 这几个函数中。
46+
47+
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
2+
:sectnums:
3+
:sectnumlevels: 5
4+
5+
6+
= **功能概述**
7+
8+
IvorySQL提供兼容Oracle内置函数`SYS_CONTEXT('namespace', 'parameter' [, length ])`,
9+
返回当前时刻与给定上下文关联参数的值,可以在SQL和PLSQL语言中使用。
10+
11+
提供以下命名空间:
12+
13+
* USERENV​ - 用于描述当前会话。
14+
15+
* SYS_SESSION_ROLES​ - 指示某个特定角色在当前会话中是否已启用。
16+
17+
== 实现原理
18+
19+
SYS_CONTEXT的实现原理是通过动态查询系统表与postgres的内置函数,确保结果的实时性,
20+
使用 SECURITY INVOKER 确保函数以调用者的权限执行,避免权限提升问题。
21+
该方法的实现逻辑如下:
22+
```sql
23+
CREATE OR REPLACE FUNCTION sys.sys_context(a varchar2, b varchar2)
24+
RETURNS varchar2 AS $$
25+
DECLARE
26+
res varchar2;
27+
BEGIN
28+
IF upper(a) = 'USERENV' THEN
29+
CASE upper(b)
30+
WHEN 'CURRENT_SCHEMA' THEN
31+
SELECT current_schema() INTO res;
32+
WHEN 'LANG' THEN
33+
SELECT sys.get_lang() INTO res;
34+
...
35+
ELSE
36+
RAISE EXCEPTION 'invalid USERENV parameter: %', b;
37+
END CASE;
38+
ELSIF upper(a) = 'SYS_SESSION_ROLES' THEN
39+
CASE upper(b)
40+
WHEN 'LOGIN' THEN
41+
SELECT CASE WHEN rolcanlogin = 't' THEN 'TRUE' ELSE 'FALSE' END INTO res FROM pg_roles WHERE oid = current_user::regrole::oid;
42+
...
43+
ELSE
44+
RAISE EXCEPTION 'invalid SYS_SESSION_ROLES parameter: %', b;
45+
END CASE;
46+
ELSE
47+
SELECT current_setting(a||'.'||b, true) INTO res;
48+
END IF;
49+
RETURN res;
50+
END;
51+
$$ LANGUAGE plisql SECURITY INVOKER;
52+
```
53+
== 命名空间USERENV支持的参数
54+
[cols="2,8"]
55+
|====
56+
|*参数名称*|*返回值*
57+
|CURRENT_SCHEMA | 当前活动默认模式的名称。在会话期间,可以通过 ALTER SESSION SET CURRENT_SCHEMA 语句更改该值。该值也可能在会话期间发生变化,以反映任何活动定义者权限对象的所有者。如果直接在视图定义的正文中使用,那么将返回在执行使用视图的游标时使用的默认模式。
58+
|CURRENT_SCHEMAID | 当前活动的默认模式的标识符。
59+
|SESSION_USER | 当前session的用户
60+
会话用户(登录用户)的名称。在数据库会话期间,随着 Real Application Security 会话的附加或分离,该名称可能会发生变化。对于企业用户,返回模式。对于其他用户,返回数据库用户名。如果数据库会话当前附加了 Real Application Security 会话,则返回用户 XS$NULL。
61+
|SESSION_USERID | 当前session的用户ID。
62+
|PROXY_USER | 代表 SESSION_USER 打开当前会话的数据库用户名称。
63+
|PROXY_USERID | 代表 SESSION_USER 打开当前会话的数据库用户的标识符。
64+
|CURRENT_USER | 当前会话中执行操作的用户。
65+
|CURRENT_USERID | 当前会话中执行操作的用户标识符。
66+
|CURRENT_EDITION_NAME | 当前版本的名字。
67+
|CLIENT_PROGRAM_NAME | 客户端程序的名称。
68+
|====
69+
[cols="2,8"]
70+
|====
71+
|*参数名称*|*返回值*
72+
|IP_ADDRESS | 客户端的IP地址。
73+
|HOST | 客户端主机的名称。
74+
|ISDBA | 如果当前用户是数据库管理员,则返回TRUE。
75+
|LANG | 语言的缩写名称,比现有的 “LANGUAGE ”参数更简短。
76+
|LANGUAGE | 当前会话的使用的语言和地区,以及数据库字符集,格式为:language_territory.characterset。
77+
|NLS_DATE_FORMAT | 当前session的日期格式。
78+
|PLATFORM_SLASH | 在用户的平台上用作文件路径分隔符的斜线字符(Unix或Linux是’/’,Windows是’\’)。
79+
|DB_NAME | 当前连接的CDB的名称。
80+
|SID | 当前会话的系统标识符。
81+
|SESSIONID | 审计会话标识符。不能在分布式 SQL 语句中使用此属性。
82+
|CLIENT_INFO | 返回最多 64 字节的用户会话信息,应用程序可使用 DBMS_APPLICATION_INFO 包存储这些信息。
83+
|ENTRYID | 当前审计条目编号。审计条目编号序列在细粒度审计记录和常规审计记录之间共享。不能在分布式 SQL 语句中使用此属性。只有通过标准或细粒度审计的审计处理程序才能看到正确的审计条目标识符。
84+
|TERMINAL | 当前会话客户端的操作系统标识符。在分布式 SQL 语句中,该属性返回本地会话的标识符。在分布式环境中,仅支持远程 SELECT 语句,不支持远程 INSERT、UPDATE 或 DELETE 操作。(此参数的返回长度可能因操作系统而异)。
85+
|====
86+
== 命名空间SYS_SESSION_ROLES支持的参数
87+
[cols="2,8"]
88+
|====
89+
|*参数名称*|*返回值*
90+
|DBA | 如果当前用户是数据库管理员,则返回TRUE。
91+
|LOGIN | 如果当前用户是登录角色,则返回TRUE。
92+
|CREATEROLE | 如果当前session的用户具有创建角色的权限,则返回TRUE。
93+
|CREATEDB | 如果当前session的用户具有创建数据库的权限,则返回TRUE。
94+
|====
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
2+
:sectnums:
3+
:sectnumlevels: 5
4+
5+
6+
= **功能概述**
7+
8+
IvorySQL提供兼容Oracle内置函数`USERENV('parameter')`,用于返回当前会话的相关信息。
9+
这是一个遗留函数,建议使用 SYS_CONTEXT 函数及其内置的 USERENV 命名空间来获取当前功能。
10+
11+
== 实现原理
12+
13+
`USERENV` 通过Bison规则解析函数调用,检查参数合法性并映射到对应的具体SQL函数。
14+
解析规则在 `ora_gram.y` 中实现,逻辑如下:
15+
```c
16+
USERENV '(' Sconst ')'
17+
{
18+
char *normalized_param = downcase_identifier($3, strlen($3), true, true);
19+
20+
#define CHECK_AND_CALL(param, func_name) \
21+
if (strcmp(normalized_param, param) == 0) \
22+
$$ = (Node *) makeFuncCall(OracleSystemFuncName(func_name), NIL, COERCE_EXPLICIT_CALL, @1);
23+
24+
CHECK_AND_CALL("client_info", "get_client_info")
25+
else CHECK_AND_CALL("entryid", "get_entryid")
26+
else CHECK_AND_CALL("terminal", "get_terminal")
27+
else CHECK_AND_CALL("isdba", "get_isdba")
28+
else CHECK_AND_CALL("lang", "get_lang")
29+
else CHECK_AND_CALL("language", "get_language")
30+
else CHECK_AND_CALL("sessionid", "get_sessionid")
31+
else CHECK_AND_CALL("sid", "get_sid")
32+
else
33+
ereport(ERROR,
34+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
35+
errmsg("invalid USERENV parameter: \"%s\".", $3)));
36+
#undef CHECK_AND_CALL
37+
}
38+
```
39+
具体功能则是在 `builtin_functions--1.0.sql` 中实现。例如:
40+
```sql
41+
CREATE OR REPLACE FUNCTION sys.get_language()
42+
RETURNS varchar2
43+
AS $$
44+
SELECT (regexp_split_to_array(current_setting('lc_monetary'), '\.'))[1]||'.'||pg_client_encoding();
45+
$$ LANGUAGE sql STRICT;
46+
```
47+
48+
== USERENV支持的参数
49+
[cols="2,8"]
50+
|====
51+
|*参数名称*|*返回值*
52+
|sid | 当前session的用户ID。
53+
|sessionid | 返回审计会话标识符。
54+
|language | 返回数据库当前会话的语言、地域和字符集。
55+
|lang | 返回ISO缩写语言名称。
56+
|isdba | 如果用户已经被认证为管理员;或者是通过操作系统或口令文件具有管理员特权的,返回“TRUE",否则返回"FALSE"。
57+
|====

0 commit comments

Comments
 (0)