%ROWTYPE、%TYPE
2. 实现说明
2.1. 引用类型发生变化时,如何确保%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)。
2.2. %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属性赋值。这样就继承了引用变量的约束。
2.3. 表名%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。
2.4. 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。
2.5. UPDATE语句增强
在UPDATE语句做transform时候,也就是transformUpdateStmt的时候,如果是Oracle兼容模式,调用新添加的 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 处理;