PostgreSQL V14中更好的SQL函数

Posted PostgreSQLChina

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PostgreSQL V14中更好的SQL函数相关的知识,希望对你有一定的参考价值。

SQL 函数作为一种方便的快捷方式,一直为人所知和受到重视。PostgreSQL v14 引入了一种新的、更好的编写 SQL 函数的方法。本文将展示新语法的优点。

SQL 函数的示例

让我们使用“经典”语法创建一个简单的 SQL 函数示例,以便我们有一些演示材料:

CREATE EXTENSION unaccent;
 
CREATE FUNCTION mangle(t text) RETURNS text
   LANGUAGE sql
   AS 'SELECT lower(unaccent(t))';
``

您可以像使用其他数据库函数一样使用新函数:

SELECT mangle('Schön dumm');
 
   mangle  
════════════
 schon dumm
(1 row)
``

为什么选择 SQL 函数?

你可能会问 SQL 函数有什么好处。毕竟,数据库函数的主要目的是能够在数据库中运行过程代码,这是 SQL 无法做到的。但是 SQL 函数有它们的用途:

  • 不同 SQL 语句中频繁使用的表达式的代码重用;

  • 通过将部分代码分解为具有有意义名称的函数来使 SQL 语句更具可读性;

  • 于语法原因需要函数,例如CREATE AGGREGATE或CREATE OPERATOR。

此外,可以内联简单的 SQL 函数,即优化器可以在查询计划时将函数调用替换为函数定义。这可以使 SQL 函数异常高效:它消除了实际函数调用的开销。因为大部分函数是优化器的黑匣子,用函数的定义替换函数通常会给你更好的估计。

如果我们EXPLAIN (VERBOSE)在示例函数上使用,我们可以看到函数内联:

EXPLAIN (VERBOSE, COSTS OFF) SELECT mangle('Schön dumm');
 
                  QUERY PLAN                  
══════════════════════════════════════
 Result
   Output: lower(unaccent('Schön dumm'::text))
(2 rows)
``

PostgreSQL函数的缺点

PostgreSQL 函数很棒。一个好的方面是您不限于单一的编程语言。PostgreSQL 支持用 SQL、C、PL/pgSQL(Oracle 的 PL/SQL 的克隆)、Perl、Python 和 Tcl 编写的函数,开箱即用。

但这还不是全部:在 PostgreSQL 中,您可以编写一个插件,允许您在数据库中使用您选择的任何语言。为了实现这种灵活性,PostgreSQL函数的函数体只是一个字符串常量,当 PostgreSQL 执行函数时,过程语言的调用处理程序会解释该字符串常量。这有一些不良副作用:缺乏依赖跟踪。

PostgreSQL跟踪pg_depend和pg_shdepend目录表中数据库对象之间的依赖关系。这样,数据库就知道对象之间的关系:它要么阻止您删除其他对象所依赖的对象(如具有外键引用的表),要么自动删除依赖对象(如删除被删除表上的所有索引)。

由于函数体只是 PostgreSQL 无法解释的字符串常量,因此它不会跟踪函数和函数中使用的对象之间的依赖关系。过程语言可以提供一个验证器来检查函数体的语法正确性(如果check_function_bodies = on)。验证器还可以测试函数中引用的对象是否存在,但它不能阻止您以后删除函数使用的对象。

让我们用例子来证明:

DROP EXTENSION unaccent;
SELECT mangle('boom');
ERROR:  function unaccent(text) does not exist
LINE 1: SELECT lower(unaccent(t))
                     ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
QUERY:  SELECT lower(unaccent(t))
CONTEXT:  SQL function "mangle" during inlining
``

我们将通过再次创建扩展来解决问题。但是,最好在DROP EXTENSION不使用CASCADE选项的情况下运行时收到错误消息。

search_path作为安全问题

由于 PostgreSQL 在查询执行时解析函数体,它使用当前设置search_path来解析对不使用模式名称限定的数据库对象的所有引用。这不仅限于表和视图,还扩展到函数和运算符。我们可以使用示例函数来演示这个问题:

SET search_path = pg_catalog;
SELECT public.mangle('boom');
ERROR:  function unaccent(text) does not exist
LINE 1: SELECT lower(unaccent(t))
                     ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
QUERY:  SELECT lower(unaccent(t))
CONTEXT:  SQL function "mangle" during inlining
``

在我们的示例中,我们可以通过在函数调用中使用public.unaccent()来避免这种烦恼。但它可能比这更糟,特别是对于SECURITY DEFINER函数。由于对每个函数和运算符进行模式限定很麻烦,推荐的解决方案是search_path在函数上强制指定模式名:

1    ALTER FUNCTION mangle(text) SET search_path = public;
``

请注意,search_path上的模式应该只允许用户使用CREATE特权,因此这对于 v15 之前的版本不是一个好主意!设置search_path的一个令人不快的缺点是它会阻止 SQL 函数的内联。

PostgreSQL v14 中的新 SQL 函数语法

从 PostgreSQL v14 开始,SQL 函数和过程的主体不再是字符串常量。您现在可以对函数体使用以下形式之一:

CREATE FUNCTION function_name(...) RETURNS ...
RETURN expression;
 
CREATE FUNCTION function_name(...) RETURNS ...
BEGIN ATOMIC
   statement;
   ...
END;
``

第一种形式要求函数体是一个表达式。因此,如果要执行查询,则必须将其包装在括号中(将其转换为子查询,这是一个有效的表达式)。例如:

CREATE FUNCTION get_data(v_id bigint) RETURNS text
RETURN (SELECT value FROM data WHERE is = v_id);
``

第二种形式允许您编写具有多个 SQL 语句的函数。与过去使用多语句 SQL 函数一样,函数的结果将是最终 SQL 语句的结果。您可以使用新语法的第二种形式来创建 SQL 过程。第一种形式显然不适合过程,因为过程没有返回值。

我们可以轻松地重写示例函数以使用新语法:

CREATE OR REPLACE FUNCTION mangle(t text) RETURNS text
RETURN lower(unaccent(t));
``

请注意,这些新的 SQL 函数可以像旧的函数一样内联到 SQL 语句中!

新 SQL 函数语法的优点

主要区别在于:新式SQL函数和过程在函数定义时解析,并以解析后的形式存储在系统目录表pg_proc的prosqlbody列中。结果,上面提到的两个缺点就消失了:

1、使用新型 SQL 函数进行依赖跟踪

因为函数体以解析的形式提供,所以 PostgreSQL 可以跟踪依赖关系。让我们用重新定义的示例函数来试试:

DROP EXTENSION unaccent;
ERROR:  cannot drop extension unaccent because other objects depend on it
DETAIL:  function mangle(text) depends on function unaccent(text)
HINT:  Use DROP ... CASCADE to drop the dependent objects too.
``

2、用新型 SQL 函数修复search_path

search_path仅在解析 SQL 时才相关。由于现在在CREATE FUNCTION运行时会发生这种情况,因此我们不必担心函数执行时该参数的当前设置:

SET search_path = pg_catalog;
SELECT public.mangle('Schön besser');
    mangle   
══════════════
 schon besser
(1 row)
``

交互式客户端的问题

这不仅会混淆像HeidiSQL(从来没有学过美元引用)这样的常见问题,而且对于任何将分号识别为SQL语句之间分隔符的客户机来说都是一个问题。甚至旧版本的psql也有这样的语法问题:

您可能会注意到用于定义 SQL 函数的多语句包含用于终止 SQL 语句的分号。这不仅会混淆像HeidiSQL(从来没有学过美元引用)这样的常见问题,而且对于任何将分号识别为SQL语句之间分隔符的客户机来说都是一个问题。甚至旧版本的psql也有这样的语法问题:

psql (13.7, server 15beta2)
WARNING: psql major version 13, server major version 15.
         Some psql features might not work.
Type "help" for help.
 
test=> CREATE FUNCTION tryme() RETURNS integer
BEGIN ATOMIC
   SELECT 42;
END;
ERROR:  syntax error at end of input
LINE 3:    SELECT 42;
                     ^
WARNING:  there is no transaction in progress
COMMIT
``

psql认为“ SELECT 42”之后的分号终止CREATE FUNCTION语句。被截断的语句会导致错误。最后的END被视为它自己的语句,它是COMMIT同义词并导致警告。

在 v14 及更高版本中,psql正确处理此类语句。pgAdmin 4 学习了 6.3 版的新语法。但我确信有很多客户还没有收到消息。

结论

PostgreSQL v14 引入新的 SQL 函数语法在可用性和安全性方面具有很大的优势。获取支持新语法的客户端并开始将其用于您的SQL 函数。您应该考虑重写现有函数以利用这些优势。

原文链接:

Better SQL functions in PostgreSQL v14 - CYBERTECNew syntax for SQL functions was introduced in PostgreSQL v14, this article gives you vital tips for this important change & its advantages.https://www.cybertec-postgresql.com/en/better-sql-functions-in-postgresql-v14/

以上是关于PostgreSQL V14中更好的SQL函数的主要内容,如果未能解决你的问题,请参考以下文章

MySQL与PostgreSQL比较 哪个数据库更好

Postgresql函数返回记录数

PostgreSQL 和SQL Server哪个更好些?(作为平时学习之用)

postgreSQL使用sql归一化数据表的某列,以及出现“字段 ‘xxx’ 必须出现在 GROUP BY 子句中或者在聚合函数中”错误的可能原因之一

为大型 Postgresql 表优化嵌套连接窗口函数

PostgreSQL之SQL函数介绍及实践