diff --git a/CN/modules/ROOT/nav.adoc b/CN/modules/ROOT/nav.adoc index 81c31d3d..f34da970 100644 --- a/CN/modules/ROOT/nav.adoc +++ b/CN/modules/ROOT/nav.adoc @@ -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框架] @@ -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语法快速参考] diff --git a/CN/modules/ROOT/pages/master/6.3.4.adoc b/CN/modules/ROOT/pages/master/6.3.4.adoc new file mode 100644 index 00000000..c4f75dc1 --- /dev/null +++ b/CN/modules/ROOT/pages/master/6.3.4.adoc @@ -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 处理; diff --git a/CN/modules/ROOT/pages/master/7.16.adoc b/CN/modules/ROOT/pages/master/7.16.adoc new file mode 100644 index 00000000..15c83c4e --- /dev/null +++ b/CN/modules/ROOT/pages/master/7.16.adoc @@ -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) +```