Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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]
** IvorySQL贡献指南
*** xref:master/8.1.adoc[社区贡献指南]
*** xref:master/8.2.adoc[asciidoc语法快速参考]
Expand Down
104 changes: 104 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,104 @@
: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 为 N/A(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 …];
```

如果语句“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)
```
Loading