暂无图片
暂无图片
5
暂无图片
暂无图片
暂无图片

cached plan must not change result type

原创 高云龙 2023-08-03
5646

“cached plan must not change result type” 这个错误的出现需要两个条件:业务使用了prepare语句 与 数据库做了DDL操作

什么是PREPARE

预备语句是服务端的对象,可以用于优化性能。在执行PREPARE语句的时候,指定的查询被解析、分析、重写。当随后发出EXECUTE语句的时候,预备语句被规划和执行。这种设计避免了重复解析、分析工作。PREPARE语句创建后在整个数据库会话期间一直存在,一旦创建成功,即便是在事务块中创建,事务回滚,PREPARE也不会删除。只能通过显式调用DEALLOCATE进行删除,另外,会话结束时PREPARE也会自动删除。

报错规避方法

当我们在数据库层面做了DDL(修改字段长度,字段类型,字段的数量)操作,且业务使用了prepare语句,那就会出现缓存中的执行计划与结果类型不匹配的情况,而cache plan的设计是要保证数据类型是固定的,不可改变的。

1、重启业务程序

prepare语句会随着连接会话的断开而自动删除,一般重大的数据库DDL操作都会伴随着业务程序的重启,所以涉及到业务重启的DDL操作并不会出现这个报错。

2、杀掉数据库会话连接

这个方法同重启业务差不多,但并不是所有的DDL操作都要重启业务程序,建议将idle状态的连接进行删除,通过交替删除的方式将连接全部断开一遍,这样相对重启业务来说,影响要小一些。

3、deallocate the prepare

deallocate 这种方式更适合于单prepare会话的情况,对于业务程序上来的多并发连接的情况并不适用,不如采取方法1和方法2,示例如下:

MogDB=# create table pret(id int,col1 varchar(8));
CREATE TABLE
MogDB=# \d+ pret
                                Table "public.pret"
 Column |         Type         | Modifiers | Storage  | Stats target | Description 
--------+----------------------+-----------+----------+--------------+-------------
 id     | integer              |           | plain    |              | 
 col1   | character varying(8) |           | extended |              | 
Has OIDs: no
Options: orientation=row, compression=no

MogDB=# prepare p1 as select * from pret where col1=$1;
PREPARE
MogDB=# insert into pret values(1,'c1'),(2,'c2');
INSERT 0 2
MogDB=# execute p1('c1');
 id | col1 
----+------
  1 | c1
(1 row)

--修改字段长度
MogDB=# alter table pret alter col1 set data type varchar(16);
ALTER TABLE
MogDB=# \d+ pret
                                Table "public.pret"
 Column |         Type          | Modifiers | Storage  | Stats target | Description 
--------+-----------------------+-----------+----------+--------------+-------------
 id     | integer               |           | plain    |              | 
 col1   | character varying(16) |           | extended |              | 
Has OIDs: no
Options: orientation=row, compression=no

--报错出现
MogDB=# execute p1('c1');
ERROR:  cached plan must not change result type

--取消prepare
MogDB=# deallocate p1;
DEALLOCATE

MogDB=# execute p1('c1');
ERROR:  prepared statement "p1" does not exist

--重新prepare
MogDB=# prepare p1 as select * from pret where col1=$1;
PREPARE
MogDB=# execute p1('c1');
 id | col1 
----+------
  1 | c1
(1 row)

4、使用类型转换

我们也可以使用类型转换的方式来定义prepare语句,这种方式是临时规避方式,不建议在应用程序中使用,示例如下:

MogDB=# \d+ pret
                                Table "public.pret"
 Column |         Type          | Modifiers | Storage  | Stats target | Description 
--------+-----------------------+-----------+----------+--------------+-------------
 id     | integer               |           | plain    |              | 
 col1   | character varying(16) |           | extended |              | 
Has OIDs: no
Options: orientation=row, compression=no

--预编译1
MogDB=# prepare p1 as select * from pret where col1=$1;
PREPARE
--预编译2
MogDB=# prepare p2 as select id::text,col1 from pret where col1=$1;
PREPARE

MogDB=# execute p1('c1');
 id | col1 
----+------
  1 | c1
(1 row)

MogDB=# execute p2('c1');
 id | col1 
----+------
 1  | c1
(1 row)

--修改字段类型
MogDB=# alter table pret alter column id type varchar(8);
ALTER TABLE
MogDB=# \d+ pret
                                Table "public.pret"
 Column |         Type          | Modifiers | Storage  | Stats target | Description 
--------+-----------------------+-----------+----------+--------------+-------------
 id     | character varying(8)  |           | extended |              | 
 col1   | character varying(16) |           | extended |              | 
Has OIDs: no
Options: orientation=row, compression=no

再次执行预编译语句,查看效果,p1报错,但是p2正常
MogDB=# execute p1('c1');
ERROR:  cached plan must not change result type
MogDB=# execute p2('c1');
 id | col1 
----+------
 1  | c1
(1 row)
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论