是否有一些JSONB参数的“直接强制转换”?

时间:2016-03-14 13:43:46

标签: postgresql jsonb

这是在JSONB上下文中使用的典型wrap function(重载非json foo()函数),

-- suppose FUNCTION foo(text[], numeric, boolean) returns int.

CREATE FUNCTION foo(JSONB) RETURNS int AS $f$
    SELECT foo(
        (SELECT array_agg(x) FROM jsonb_array_elements_text($1->'list') t(x) ),
        ($1->>'lim')::numeric, -- why cast JSONB number to numeric?
        ($1->>'osort')::boolean  -- idem for boolean
    );
$f$ LANGUAGE SQL IMMUTABLE;

是否有一种优雅的方式将JSONB 字符串数组转换为SQL text数组?如果内部表示显示其数据类型,为什么我们需要这个丑陋的numericboolean强制转换?

在好的@IgorRomanchenko讨论后编辑:

哲学笔记

我的preocupation是

  

为什么PostreSQL 9.5 丢失了JSONB 内部数据类型信息和内部(数字和布尔)表示?

现在,运行时引擎使用CPU将 JSONB编号转换为文本和文本到 SQL numeric ,或者使用boolean将文本文本转换为布尔...理想(优化) )是“旁路”JSONB内部表示,或只是“二进制到二进制”(不解析为文本并将文本转换为二进制)。

在内部使用JSONB数据类型(jsonb_typeof)时,PostgreSQL将更加优化和友好!

PS1:SELECT array_agg(x) FROM jsonb_array_elements_text()示例仅放大并暴露相同的问题。在这种情况下,我们还需要一个直接的jsonb_array_to_array(x,type)函数来绕过数组bynary表示。

PS2:CREATE CAST不是这个问题的解决方案,因为它会破坏原始的JSONB表示。

......怎么做?

Hum ...也许创建像::numericByPass这样的替代强制类型来“绕过JSONB二进制数到SQL数”和::booleanByPass“绕过JSONB boolean到SQL boolean”,所以,保留< / strong>内部表示,将其解析为文本值。当然,如果运行时检查看到字符串而不是期望的数据类型,则会触发错误。

查看示例,我们看到上下文有义务使用number和boolean,用户不需要将它说给编译器。

SQL解析器可以使用这种隐式的“上下文义务”来自行完成,在编译时,转换为::numericByPass::booleanByPass,保留JSONB内部表示。如果用户真的害怕字符串输入,则只有在这种情况下,用户才会添加明确的::numeric::boolean元素。

2 个答案:

答案 0 :(得分:1)

您需要运行时强制转换和检查,因为输入JSON没有定义任何结构。您无法检查此输入JSON是否包含带有文本数组的字段"list"或带有整数的字段"lim"

以JSON:

为例
{
"list":42,
"lim": [3,5],
"osort": "maybe"
}

当前函数将抛出类型转换异常,因为它具有运行时强制转换和检查。没有任何运行时检查,这个函数会做什么?

BTW如果你真的希望postgres为你做这件事 - 你可以启用从varcharintboolean的隐式演员,如下文所示:Postgresql. CREATE CAST 'character varying' to 'integer'

这不是最好的主意,但它会奏效。

答案 1 :(得分:0)

您可以创建类似于 - &gt;&gt;的不同运算符产生不同的输出类型。例如, - &gt;#可以给你一个数字和 - &gt; |布尔值。

CREATE FUNCTION json_numeric(
    j json
    , e text
) RETURNS NUMERIC IMMUTABLE STRICT LANGUAGE SQL AS $$
    SELECT (j->>e)::numeric
$$;

CREATE OPERATOR -># ( PROCEDURE = json_numeric, LEFTARG = json, RIGHTARG = text );

SELECT ('{"a": 42, "b": "1"}')::json -># 'a';
 ?column? 
----------
       42
(1 row)

SELECT ('{"a": 42, "b": "1"}')::json -># 'b';
 ?column? 
----------
        1
(1 row)

如果使用plpgsql,如果json_typeof()不正确,则可能会抛出错误,但请注意,这将比SQL版本慢得多(尽管在大多数用法中都不重要)。