使用类型 ("TEXT") 的字符串表示形式将值转换为类型 (TEXT)

Cast value to type (TEXT) using string representation of type ("TEXT")

我想创建一个函数,尝试将一组值转换为用户指定的类型(默认为文本)。一个非常简单的函数如下所示:

CREATE OR REPLACE FUNCTION cast_to(variable jsonb, key text, target_type anyelement DEFAULT 'TEXT'::regtype) RETURNS anyelement as $$
begin
    RETURN CAST(variable->>key AS target_type);
end
$$
language plpgsql;

我尝试了以下方法:

  1. SELECT CAST('foo' AS 'text');: 给出语法错误
  2. SELECT CAST('foo' AS 'text'::regtype);: 与 1
  3. 相同的错误
  4. SELECT CAST('foo' AS pg_typeof(null::text)); 表示 type pg_typeof does not exist

最后一次尝试是想我可以传入一个具有目标类型的变量而不是文本表示。使用该函数将类似于 SELECT cast_to('text', NULL::text);.

如何实现这个或类似的功能?

编辑: 根据评论中的建议,我尝试使用动态 SQL。我运气不好。我创建了一个不使用任何变量的非常基本的案例:

CREATE OR REPLACE FUNCTION audit.cast_to() RETURNS text as $$
DECLARE 
_sql TEXT := 'SELECT CAST( AS )';
out TEXT;
begin
    EXECUTE _sql
    USING 'foo', 'TEXT'
    INTO out;
    return out;
end
$$
language plpgsql;

但是会抛出错误:

syntax error at or near ""

其实是可以的。即使没有动态 SQL。表面上也很简单。

CREATE FUNCTION cast_to(_js jsonb, INOUT _target_type anyelement = NULL::text) AS
$func$
BEGIN
   SELECT _js ->> pg_typeof(_target_type)::text
   INTO   _target_type;
END
$func$  LANGUAGE plpgsql;

db<>fiddle here

但是这个简约的函数包含了一些高级/棘手的细节:请参阅此处的最后一章了解基础知识:

  • Refactor a PL/pgSQL function to return the output of various SELECT queries

拼图的最后一块是将 text 值 return 由 ->> 运算符转换为 return 类型。一个简单的 SQL 函数对此很严格,并且不接受 text 作为 integer。 (对于由多态输入参数的实际输入定义的 integer 也不适用。对于试图简单地 RETURN 的 PL/pgSQL 函数也是如此。需要显式转换。

CAST (expression AS type) is not a normal function. Nor is the short syntax expression::type。这些是 结构 语法元素 。您可能已经注意到类型名称没有使用单引号,即:as identifier。 ( 你错过了那个细节,这是你报告的前 3 个语法错误的原因。)并且标识符不能在 SQL 中参数化。那将需要动态 SQL.

但是,我们可以赋值表达式的text结果到(必须键入的)变量或参数以毫不费力地实现相同的目标。 INTO 子句将实现这一点。为了方便,我直接赋值给INOUT参数_target_type。所以 _target_type 有几个用途:

  1. 定义多态 return 类型。

  2. 定义用于 JSON ->> 运算符的键名。该名称由 _target_typetype 携带,我用 pg_typeof() 提取它 - 实际上是 returns regtype,所以我们需要将 that 显式转换为文本。
    注意 总是会产生 Postgres 标准类型名称,例如:'integer',而不是 'int' 或 'int4'。如果您的键名称与默认的 Postgres 类型名称不同,您将必须像在原始设计中那样传递一个附加参数!

  3. 作为OUT参数(所以我们不需要DECLARE一个变量)可以赋值给.

  4. 附加了一个DEFAULT值:= NULL::text,所以第二个参数可以省略到return text.
    您在原始版本中尝试过,但是 DEFAULT 'TEXT'::regtype 偏离了目标。

您可能希望将其中一些用途拆分为多个参数/变量。

所以可以做到。
问题是:你为什么要做?通常,拐角处有一个更快、更简单的解决方案 - 即使更冗长。