Skip to content

Commit 3f166bb

Browse files
authored
Merge pull request #145 from bigplaice/%rowtype
Add Chinese doc for %Rowtype feature
2 parents 5f770f8 + 9c63a41 commit 3f166bb

File tree

3 files changed

+300
-1
lines changed

3 files changed

+300
-1
lines changed

CN/modules/ROOT/nav.adoc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
**** xref:master/6.3.1.adoc[like]
2525
**** xref:master/6.3.3.adoc[RowID]
2626
**** xref:master/6.3.2.adoc[OUT 参数]
27-
**** xref:master/6.4.adoc[国标GB18030]
27+
**** xref:master/6.3.4.adoc[%TYPE、%ROWTYPE]
28+
*** xref:master/6.4.adoc[国标GB18030]
2829
** Oracle兼容功能列表
2930
*** xref:master/7.1.adoc[1、框架设计]
3031
*** xref:master/7.2.adoc[2、GUC框架]
@@ -41,6 +42,7 @@
4142
*** xref:master/7.13.adoc[13、不可见列]
4243
*** xref:master/7.14.adoc[14、RowID]
4344
*** xref:master/7.15.adoc[15、OUT 参数]
45+
*** xref:master/7.16.adoc[16、%TYPE、%ROWTYPE]
4446
** IvorySQL贡献指南
4547
*** xref:master/8.1.adoc[社区贡献指南]
4648
*** xref:master/8.2.adoc[asciidoc语法快速参考]
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
:sectnums:
2+
:sectnumlevels: 5
3+
4+
:imagesdir: ./_images
5+
6+
= %ROWTYPE、%TYPE
7+
8+
== 目的
9+
10+
IvorySQL提供了兼容Oracle的plsql数据类型功能,包括%TYPE、%ROWTYPE。
11+
12+
== 实现说明
13+
14+
=== 引用类型发生变化时,如何确保%TYPE声明的变量也随之相应改变
15+
16+
这是一个被动的过程。 当前的实现方案是记录函数(存储过程)与 tablename.columname 的依赖关系。当引用类型发生变化时,根据依赖关系,让函数(存储过程)缓存失效。这样一来,函数被调用时就会执行强制编译。从而确保函数获取最新的变量类型。
17+
18+
在系统表 pg_proc 中添加一个字段 prostatus 用来表示函数(存储过程)状态,有3种状态: validate(v),invalidate(i),N/A(n)。函数编译成功后,这个状态会被设置为valid。
19+
20+
在解析函数内容时,plisql_parse_cwordtype,plisql_parse_wordrowtype,plisql_parse_cwordrowtype 等函数识别出%TYPE, %ROWTYPE引用的对象,并记录在 plisql_referenced_object 链表中,最后添加到 pg_depend 系统表中。
21+
22+
增加一种新的依赖类型 DEPENDENCY_TYPE = 't',表示%TYPE或%ROWTYPE依赖。在添加对象引用关系到pg_depend系统表时,设置依赖类型为 't'。
23+
24+
对表进行操作时(修改表类型、删除表等),查看系统表pg_depend,如果存在依赖类型deptype=’t’,并且依赖对象是函数,则调用函数 plisql_free_function 删除函数缓存,并修改 pg_proc 系统表中的函数状态prostatus 为 N/A(n)。
25+
26+
27+
=== %TYPE声明的变量继承引用变量的约束
28+
29+
在 PLiSQL_type 结构体中添加成员 bool notnull;
30+
31+
```
32+
/*
33+
* Postgres data type
34+
*/
35+
typedef struct PLiSQL_type
36+
{
37+
bool notnull; /* the type is built by variable%type,
38+
* isnull or notnull of the variable */
39+
```
40+
41+
在负责解析%TYPE类型函数的plisql_parse_wordtype或plisql_parse_cwordtype函数中,判断如果引用变量类型指定了NOT NULL约束,为返回的datatype的成员 bool notnull 属性赋值为true。
42+
在pl_gram.y 文件的 decl_statement 语法中,根据PLiSQL_type 的成员 bool notnull 为变量PLiSQL_variable *var的notnull属性赋值。这样就继承了引用变量的约束。
43+
44+
45+
=== 表名%ROWTYPE或视图名%ROWTYPE作为函数或存储过程的参数类型和函数返回值类型
46+
47+
在 ora_gram.y 中为 func_type 添加%ROWTYPE支持。
48+
49+
```
50+
| type_function_name attrs '%' ROWTYPE
51+
{
52+
$$ = makeTypeNameFromNameList(lcons(makeString($1), $2));
53+
$$->row_type = true;
54+
$$->location = @1;
55+
}
56+
```
57+
58+
在 TypeName 结构体中添加成员 bool row_type 用来标记是否指定了%ROWTYPE。
59+
60+
```
61+
typedef struct TypeName
62+
{
63+
bool pct_type; /* %TYPE specified? */
64+
bool row_type; /* %ROWTYPE specified? */
65+
```
66+
67+
在 LookupTypeName 函数中,如果 TypeName 的成员 row_type 为 TRUE,则根据TypeName的成员names中获得schema名与表名,然后获取表的typeoid。
68+
69+
=== INSERT语句增强
70+
71+
在 ora_gram.y 中添加新的语法,支持 VALUES 后面不带括号’(‘。
72+
73+
```
74+
values_clause_no_parens:
75+
VALUES columnref
76+
{
77+
SelectStmt *n = makeNode(SelectStmt);
78+
n->valuesLists = list_make1(list_make1($2));
79+
n->valuesIsrow = true;
80+
$$ = (Node *) n;
81+
}
82+
```
83+
84+
为结构体 SelectStmt 添加一个字段 bool valuesIsrow 表示 values 是一个 row。
85+
86+
在为insert语句做 transform 时,也就是函数 transformInsertStmt 中,在处理 INSERT ... VALUES 语句时,如果 valuesIsrow 为true,调用新函数 transformRowExpression,将 row_variable 转换成等价的 row_variable.field1,...,row_variable.fieldN。
87+
88+
=== UPDATE语句增强
89+
90+
在UPDATE语句做transform时候,也就是transformUpdateStmt的时候,如果是Oralce兼容模式,调用新添加的 transformIvyUpdateTargetList 函数。
91+
在这个新函数中,对于参数origTlist(即targetList)中没有名字为row的情况,按原来UPDATE的transform流程执行 transformUpdateTargetList 函数。
92+
93+
对参数origTlist中有名字为row的情况:因为PostgreSQL中row可以作为列名,而Oracle 中row是保留关键字,不可以作为列名,所以需要区分row是否是表中的列,如果row不是要更新的表中的列,则调用新函数 transformUpdateRowTargetList 把语句
94+
```
95+
UPDATE table_name SET ROW = row_variable [WHERE …];
96+
```
97+
转换成等价的
98+
```
99+
UPDATE table_name SET table_name.column1 = row_variable.column1, table_name.column2 = row_variable.filed2,… table_name.columnN = row_variable.columnN [WHERE …];
100+
```
101+
102+
如果语句“UPDATE table_name SET ROW = row_variable”中的变量 row_variable不是组合类型,就按原来UPDATE的transform流程执行 transformUpdateTargetList 函数;
103+
如果语句中的变量 row_variable 是组合类型,表中的名为row的那一列也是组合类型,并且两者的类型Oid也一样,则按原来UPDATE的transform流程执行 transformUpdateTargetList 函数;
104+
其他所有情况,调用新函数 transformUpdateRowTargetList 处理;
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
:sectnums:
2+
:sectnumlevels: 5
3+
4+
:imagesdir: ./_images
5+
6+
= %ROWTYPE、%TYPE
7+
8+
== 目的
9+
10+
IvorySQL提供了兼容Oracle的plsql数据类型功能,包括%TYPE、%ROWTYPE。
11+
12+
本文档旨在为使用人员介绍%TYPE、%ROWTYPE的功能。
13+
14+
== 功能说明
15+
16+
IvorySQL提供了兼容Oracle的%TYPE、%ROWTYPE功能,包括如下内容。
17+
18+
=== 引用发生改变,%TYPE或%ROWTYPE声明的变量也相应改变。
19+
20+
创建表以及函数。
21+
```
22+
CREATE TABLE t1(id int, name varchar(20));
23+
24+
--function's parameter datatype is tablename.columnname%TYPE
25+
CREATE OR REPLACE FUNCTION fun1(v t1.id%TYPE) RETURN varchar AS
26+
BEGIN
27+
RETURN v;
28+
END;
29+
/
30+
```
31+
32+
函数状态是valid,并且执行函数能够成功。
33+
```
34+
SELECT prostatus FROM pg_proc WHERE proname like 'fun1'; --v
35+
prostatus
36+
-----------
37+
v
38+
(1 row)
39+
40+
SELECT fun1(1) FROM dual;
41+
fun1
42+
------
43+
1
44+
(1 row)
45+
```
46+
47+
修改引用的先前定义的声明,函数状态变为invalid,但是函数能够执行成功。
48+
```
49+
ALTER TABLE t1 ALTER COLUMN id TYPE varchar(20);
50+
51+
SELECT prostatus FROM pg_proc WHERE proname like 'fun1'; --n
52+
prostatus
53+
-----------
54+
n
55+
(1 row)
56+
57+
--after changing the column id type from int to varchar, call the function again
58+
SELECT fun1('a') FROM dual; --successfully
59+
fun1
60+
------
61+
a
62+
(1 row)
63+
```
64+
65+
重新编译函数,状态重新变成valid。
66+
```
67+
ALTER FUNCTION fun1 COMPILE;
68+
SELECT prostatus FROM pg_proc WHERE proname like 'fun1'; --v
69+
prostatus
70+
-----------
71+
v
72+
(1 row)
73+
```
74+
75+
=== %TYPE声明的变量继承引用变量的约束
76+
77+
示例:
78+
```
79+
--the following testcase will fail
80+
DECLARE
81+
name VARCHAR(25) NOT NULL := 'Niu';
82+
surname name%TYPE ;
83+
BEGIN
84+
raise notice 'name=%' ,name;
85+
raise notice 'surname=%' ,surname;
86+
END;
87+
/
88+
89+
ERROR: variable "surname" must have a default value, since it's declared NOT NULL
90+
LINE 3: surname name%TYPE ;
91+
^
92+
```
93+
94+
=== 表名%ROWTYPE或视图名%ROWTYPE作为函数或存储过程的参数类型和函数返回值类型
95+
96+
示例:
97+
```
98+
CREATE TABLE employees(first_name varchar(20) not null,
99+
last_name varchar(20) not null,
100+
phone_number varchar(50));
101+
102+
INSERT INTO employees VALUES ('Steven','Niu','1-650-555-1234');
103+
104+
CREATE OR REPLACE PROCEDURE p0(v employees%ROWTYPE) AS
105+
BEGIN
106+
raise notice 'v.first_name = %, v.last_name = %, v.phone_number = %',
107+
v.first_name, v.last_name, v.phone_number;
108+
END;
109+
/
110+
111+
DECLARE
112+
a employees%ROWTYPE;
113+
BEGIN
114+
select * into a from employees ;
115+
raise notice 'a=%', a;
116+
call p0(a);
117+
END;
118+
/
119+
120+
NOTICE: a=(Steven,Niu,1-650-555-1234)
121+
NOTICE: v.first_name = Steven, v.last_name = Niu, v.phone_number = 1-650-555-1234
122+
123+
\df p0
124+
List of functions
125+
Schema | Name | Result data type | Argument data types | Type
126+
--------+------+------------------+------------------------+------
127+
public | p0 | | IN v employees%ROWTYPE | proc
128+
(1 row)
129+
```
130+
131+
=== INSERT语句增强
132+
133+
INSERT语句增强支持把一个%ROWTYPE声明的变量插入表中。
134+
135+
语法为:
136+
```
137+
INSERT INTO table_name VALUES row_variable ;
138+
```
139+
140+
示例:
141+
```
142+
CREATE TABLE t1(id int, name varchar(20));
143+
144+
DECLARE
145+
v1 t1%ROWTYPE;
146+
BEGIN
147+
FOR i IN 1 .. 5 LOOP
148+
v1.id := i;
149+
v1.name := 'a' || i;
150+
INSERT INTO t1 VALUES v1;
151+
END LOOP;
152+
END;
153+
/
154+
155+
156+
SELECT * FROM t1;
157+
id | name
158+
----+------
159+
1 | a1
160+
2 | a2
161+
3 | a3
162+
4 | a4
163+
5 | a5
164+
(5 rows)
165+
```
166+
167+
=== UPDATE语句增强
168+
169+
示例:
170+
```
171+
CREATE TABLE t1(id int, name varchar(20));
172+
173+
DELETE FROM t1;
174+
175+
DECLARE
176+
v1 t1%ROWTYPE;
177+
v2 t1%ROWTYPE;
178+
BEGIN
179+
v1.id := 11;
180+
v1.name := 'abc';
181+
INSERT INTO t1 VALUES v1;
182+
v2.id := 22;
183+
v2.name := 'new';
184+
UPDATE t1 SET ROW = v2;
185+
END;
186+
/
187+
188+
SELECT * FROM t1;
189+
id | name
190+
----+------
191+
22 | new
192+
(1 row)
193+
```

0 commit comments

Comments
 (0)