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 函数。您应该考虑重写现有函数以利用这些优势。
原文链接:
以上是关于PostgreSQL V14中更好的SQL函数的主要内容,如果未能解决你的问题,请参考以下文章
PostgreSQL 和SQL Server哪个更好些?(作为平时学习之用)
postgreSQL使用sql归一化数据表的某列,以及出现“字段 ‘xxx’ 必须出现在 GROUP BY 子句中或者在聚合函数中”错误的可能原因之一