Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CN/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
**** xref:master/6.3.1.adoc[like]
**** xref:master/6.3.3.adoc[RowID]
**** xref:master/6.3.2.adoc[OUT 参数]
**** xref:master/6.4.adoc[国标GB18030]
**** xref:master/6.3.4.adoc[%TYPE、%ROWTYPE]
*** xref:master/6.4.adoc[国标GB18030]
** Oracle兼容功能列表
*** xref:master/7.1.adoc[1、框架设计]
*** xref:master/7.2.adoc[2、GUC框架]
Expand All @@ -41,6 +42,7 @@
*** xref:master/7.13.adoc[13、不可见列]
*** xref:master/7.14.adoc[14、RowID]
*** xref:master/7.15.adoc[15、OUT 参数]
*** xref:master/7.16.adoc[16、%TYPE、%ROWTYPE]
** xref:master/8.adoc[社区贡献指南]
** xref:master/9.adoc[工具参考]
** xref:master/10.adoc[FAQ]
Expand Down
101 changes: 101 additions & 0 deletions CN/modules/ROOT/pages/master/6.3.4.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
:sectnums:
:sectnumlevels: 5

:imagesdir: ./_images

= %ROWTYPE、%TYPE

== 目的

IvorySQL提供了兼容Oracle的plsql数据类型功能,包括%TYPE、%ROWTYPE。

== 实现说明

=== 引用类型发生变化时,如何确保%TYPE声明的变量也随之相应改变

这是一个被动的过程。 当前的实现方案是记录函数(存储过程)与 tablename.columname 的依赖关系。当引用类型发生变化时,根据依赖关系,让函数(存储过程)缓存失效。这样一来,函数被调用时就会执行强制编译。从而确保函数获取最新的变量类型。

在系统表 pg_proc 中添加一个字段 prostatus 用来表示函数(存储过程)状态,有3种状态: validate(v),invalidate(i),N/A(n)。函数编译成功后,这个状态会被设置为valid。

在解析函数内容时,plisql_parse_cwordtype,plisql_parse_wordrowtype,plisql_parse_cwordrowtype 等函数识别出%TYPE, %ROWTYPE引用的对象,并记录在 plisql_referenced_object 链表中,最后添加到 pg_depend 系统表中。

增加一种新的依赖类型 DEPENDENCY_TYPE = 't',表示%TYPE或%ROWTYPE依赖。在添加对象引用关系到pg_depend系统表时,设置依赖类型为 't'。

对表进行操作时(修改表类型、删除表等),查看系统表pg_depend,如果存在依赖类型deptype=’t’,并且依赖对象是函数,则调用函数 plisql_free_function 删除函数缓存,并修改 pg_proc 系统表中的函数状态prostatus 为 NA(n)。


=== %TYPE声明的变量继承引用变量的约束

在 PLiSQL_type 结构体中添加成员 bool notnull;

```
/*
* Postgres data type
*/
typedef struct PLiSQL_type
{
bool notnull; /* the type is built by variable%type,
* isnull or notnull of the variable */
```

在负责解析%TYPE类型函数的plisql_parse_wordtype或plisql_parse_cwordtype函数中,判断如果引用变量类型指定了NOT NULL约束,为返回的datatype的成员 bool notnull 属性赋值为true。
在pl_gram.y 文件的 decl_statement 语法中,根据PLiSQL_type 的成员 bool notnull 为变量PLiSQL_variable *var的notnull属性赋值。这样就继承了引用变量的约束。


=== 表名%ROWTYPE或视图名%ROWTYPE作为函数或存储过程的参数类型和函数返回值类型

在 ora_gram.y 中为 func_type 添加%ROWTYPE支持。

```
| type_function_name attrs '%' ROWTYPE
{
$$ = makeTypeNameFromNameList(lcons(makeString($1), $2));
$$->row_type = true;
$$->location = @1;
}
```

在 TypeName 结构体中添加成员 bool row_type 用来标记是否指定了%ROWTYPE。

```
typedef struct TypeName
{
bool pct_type; /* %TYPE specified? */
bool row_type; /* %ROWTYPE specified? */
```

在 LookupTypeName 函数中,如果 TypeName 的成员 row_type 为 TRUE,则根据TypeName的成员names中获得schema名与表名,然后获取表的typeoid。

=== INSERT语句增强

在 ora_gram.y 中添加新的语法,支持 VALUES 后面不带括号’(‘。

```
values_clause_no_parens:
VALUES columnref
{
SelectStmt *n = makeNode(SelectStmt);
n->valuesLists = list_make1(list_make1($2));
n->valuesIsrow = true;
$$ = (Node *) n;
}
```

为结构体 SelectStmt 添加一个字段 bool valuesIsrow 表示 values 是一个 row。

在为insert语句做 transform 时,也就是函数 transformInsertStmt 中,在处理 INSERT ... VALUES 语句时,如果 valuesIsrow 为true,调用新函数 transformRowExpression,将 row_variable 转换成等价的 row_variable.field1,...,row_variable.fieldN。

=== UPDATE语句增强

在UPDATE语句做transform时候,也就是transformUpdateStmt的时候,如果是Oralce兼容模式,调用新添加的 transformIvyUpdateTargetList 函数。
在这个新函数中,对于参数origTlist(即targetList)中没有名字为row的情况,按原来UPDATE的transform流程执行 transformUpdateTargetList 函数。

对参数origTlist中有名字为row的情况:因为PostgreSQL中row可以作为列名,而Oracle 中row是保留关键字,不可以作为列名,所以需要区分row是否是表中的列,如果row不是要更新的表中的列,则调用新函数 transformUpdateRowTargetList 把UPDATE table_name SET ROW = row_variable [WHERE …]; 语句转换成等价的 UPDATE table_name SET table_name.column1 = row_variable.column1, table_name.column2= row_variable.filed2,… table_name.columnN = row_variable.columnN [WHERE …];的transform形式。

如果语句“UPDATE table_name SET ROW = row_variable”中的变量 row_variable不是组合类型,就按原来UPDATE的transform流程执行 transformUpdateTargetList 函数;
如果语句中的变量 row_variable 是组合类型,表中的名为row的那一列也是组合类型,并且两者的类型Oid也一样,则按原来UPDATE的transform流程执行 transformUpdateTargetList 函数;
其他所有情况,调用新函数 transformUpdateRowTargetList 处理;

```


193 changes: 193 additions & 0 deletions CN/modules/ROOT/pages/master/7.16.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
:sectnums:
:sectnumlevels: 5

:imagesdir: ./_images

= %ROWTYPE、%TYPE

== 目的

IvorySQL提供了兼容Oracle的plsql数据类型功能,包括%TYPE、%ROWTYPE。

本文档旨在为使用人员介绍%TYPE、%ROWTYPE的功能。

== 功能说明

IvorySQL提供了兼容Oracle的%TYPE、%ROWTYPE功能,包括如下内容。

=== 引用发生改变,%TYPE或%ROWTYPE声明的变量也相应改变。

创建表以及函数。
```
CREATE TABLE t1(id int, name varchar(20));

--function's parameter datatype is tablename.columnname%TYPE
CREATE OR REPLACE FUNCTION fun1(v t1.id%TYPE) RETURN varchar AS
BEGIN
RETURN v;
END;
/
```

函数状态是valid,并且执行函数能够成功。
```
SELECT prostatus FROM pg_proc WHERE proname like 'fun1'; --v
prostatus
-----------
v
(1 row)

SELECT fun1(1) FROM dual;
fun1
------
1
(1 row)
```

修改引用的先前定义的声明,函数状态变为invalid,但是函数能够执行成功。
```
ALTER TABLE t1 ALTER COLUMN id TYPE varchar(20);

SELECT prostatus FROM pg_proc WHERE proname like 'fun1'; --n
prostatus
-----------
n
(1 row)

--after changing the column id type from int to varchar, call the function again
SELECT fun1('a') FROM dual; --successfully
fun1
------
a
(1 row)
```

重新编译函数,状态重新变成valid。
```
ALTER FUNCTION fun1 COMPILE;
SELECT prostatus FROM pg_proc WHERE proname like 'fun1'; --v
prostatus
-----------
v
(1 row)
```

=== %TYPE声明的变量继承引用变量的约束

示例:
```
--the following testcase will fail
DECLARE
name VARCHAR(25) NOT NULL := 'Niu';
surname name%TYPE ;
BEGIN
raise notice 'name=%' ,name;
raise notice 'surname=%' ,surname;
END;
/

ERROR: variable "surname" must have a default value, since it's declared NOT NULL
LINE 3: surname name%TYPE ;
^
```

=== 表名%ROWTYPE或视图名%ROWTYPE作为函数或存储过程的参数类型和函数返回值类型

示例:
```
CREATE TABLE employees(first_name varchar(20) not null,
last_name varchar(20) not null,
phone_number varchar(50));

INSERT INTO employees VALUES ('Steven','Niu','1-650-555-1234');

CREATE OR REPLACE PROCEDURE p0(v employees%ROWTYPE) AS
BEGIN
raise notice 'v.first_name = %, v.last_name = %, v.phone_number = %',
v.first_name, v.last_name, v.phone_number;
END;
/

DECLARE
a employees%ROWTYPE;
BEGIN
select * into a from employees ;
raise notice 'a=%', a;
call p0(a);
END;
/

NOTICE: a=(Steven,Niu,1-650-555-1234)
NOTICE: v.first_name = Steven, v.last_name = Niu, v.phone_number = 1-650-555-1234

\df p0
List of functions
Schema | Name | Result data type | Argument data types | Type
--------+------+------------------+------------------------+------
public | p0 | | IN v employees%ROWTYPE | proc
(1 row)
```

=== INSERT语句增强

INSERT语句增强支持把一个%ROWTYPE声明的变量插入表中。

语法为:
```
INSERT INTO table_name VALUES row_variable ;
```

示例:
```
CREATE TABLE t1(id int, name varchar(20));

DECLARE
v1 t1%ROWTYPE;
BEGIN
FOR i IN 1 .. 5 LOOP
v1.id := i;
v1.name := 'a' || i;
INSERT INTO t1 VALUES v1;
END LOOP;
END;
/


SELECT * FROM t1;
id | name
----+------
1 | a1
2 | a2
3 | a3
4 | a4
5 | a5
(5 rows)
```

=== UPDATE语句增强

示例:
```
CREATE TABLE t1(id int, name varchar(20));

DELETE FROM t1;

DECLARE
v1 t1%ROWTYPE;
v2 t1%ROWTYPE;
BEGIN
v1.id := 11;
v1.name := 'abc';
INSERT INTO t1 VALUES v1;
v2.id := 22;
v2.name := 'new';
UPDATE t1 SET ROW = v2;
END;
/

SELECT * FROM t1;
id | name
----+------
22 | new
(1 row)
```