为什么PL / pgSQL函数会有副作用,而SQL函数却没有?

时间:2018-06-23 20:57:23

标签: sql postgresql function plpgsql

PostgreSQL document说:

  

SQL函数的整个主体在执行任何函数之前都会被分析。尽管SQL函数可以包含个命令,这些命令会更改   系统目录(例如CREATE TABLE),这些命令的效果   在后面的命令的解析分析中将不可见   功能。因此,例如CREATE TABLE foo (...); INSERT INTO foo VALUES(...);   如果打包成一个,将无法按预期工作   SQL函数,因为解析INSERT命令时foo还不存在。

     

建议使用PL / pgSQL代替SQL函数   在这种情况下。

  • 为什么“在这种情况下,建议使用PL / pgSQL代替SQL函数”,其中PL / pgSQL或SQL函数包含更改系统目录的命令,例如CREATE TABLE foo (...); INSERT INTO foo VALUES(...);

  • “ SQL函数的主体在执行任何函数之前就已解析”。 PL / pgSQL函数不是真的吗?在解析和执行其主体中的命令方面,SQL函数和PL / pgSQL函数之间有什么区别?

2 个答案:

答案 0 :(得分:1)

Plpgsql函数在定义时进行解析和语法检查,然后在第一次执行时生成一个计划。

https://www.postgresql.org/docs/current/static/plpgsql-implementation.html#PLPGSQL-PLAN-CACHING

然后使用给定的参数执行该计划。

临时文件似乎可以按预期工作,除了第一次执行时已经存在的文件。

如其中提到的那样,使用动态SQL(EXECUTE)是一种挫败计划程序的方法,允许访问任意表。

https://www.postgresql.org/docs/current/static/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN

答案 1 :(得分:1)

您自己加粗了key sentence in the manual

  

在执行任何SQL函数之前,都会对其进行分析。

另请参阅The Parser Stage in the manual

它包括两个主要部分:解析器转换过程Quoting the manual:

  

转换过程将解析器返回的树作为   输入并进行语义解释以了解哪个   查询引用表,函数和运算符。

如果SQL函数包含以下命令:

CREATE TABLE foo (...);
INSERT INTO foo VALUES(...);

两个语句实际上是在同一时间计划的(基于系统目录的相同快照)。因此,INSERT无法看到由上一个CREATE命令创建的表“ foo”。这会产生以下问题之一

  1. 如果还没有名为“ foo” in your search_patch other 表,则Postgres在尝试创建功能时会抱怨

    ERROR:  relation "foo" does not exist
    
  2. 如果您的search_patch中已经存在另一个名为“ foo”的表(并且您没有使用冲突的列名),则Postgres将基于该预先存在的表来计划INSERT 。如果任何值导致(错误!)表中的冲突,通常会在执行时错误。或者,如果运气不好,它甚至可能在没有错误消息的情况下写入该表!非常狡猾的错误。

使用 PL / pgSQL 函数不会发生这种情况,因为它会将SQL命令像已准备好的语句一样对待,并按 顺序执行。因此,每个语句都可以查看先前语句中创建的对象。

因此,从未访问过的语句甚至从未计划过-与SQL函数不同。语句的执行计划可以缓存在同一会话中-与SQL函数不同。 Read details about plan caching in PL/pgSQL functions in the manual here.
对于某些用例,每种方法都具有优势。进一步阅读: