首页 > 解决方案 > postgresql:可能的临时json变量?

问题描述

如何创建一个可以引用的临时/非常短暂的 json 对象(用于在 RLS 策略中构建检查)?稍后将使用“真实”json 对象作为会话变量调用这些策略,我只是在运行时之前无法访问这些值,并且我无法控制这一点。

我在考虑 SET LOCAL,但看起来你不能使用 json 类型?我可以伪造一个 json 类型吗?例如我试过:

SET LOCAL myjson '{"foo":123, "color":red}';
SELECT myjson ->> 'foo';

但我得到一个语法错误:

ERROR:  syntax error at or near "'{"foo":123, "color":red}'"
LINE 1: SET LOCAL myjson '{"foo":123, "color":red}';
                         ^
SQL state: 42601
Character: 18

标签: postgresql

解决方案


根据您是否需要确切的SELECT myjson ->> 'foo';语法,有几个选项。我只介绍几个:

如果需要SELECT myjson ->> 'foo';,我能想到的最接近的是创建一个返回 json 的函数:

edb=# CREATE OR REPLACE FUNCTION myjson() RETURNS json AS $$ BEGIN RETURN '{"foo":"123", "color":"red"}'::json; END; $$ LANGUAGE plpgsql;
CREATE FUNCTION

edb=# SELECT myjson ->> 'foo';
 ?column? 
----------
 123
(1 row)

如果您负担得起使用 CTE,您可以这样做:

edb=# WITH x AS (SELECT '{"foo":"123", "color":"red"}'::json AS myjson)
SELECT myjson ->> 'foo' FROM x;
 ?column? 
----------
 123
(1 row)

你甚至可以放入myjson一个临时表:

edb=# SELECT '{"foo":"123", "color":"red"}'::json AS myjson INTO TEMPORARY TABLE temptable;
SELECT 1
edb=# SELECT myjson->>'foo' FROM temptable;
 ?column? 
----------
 123
(1 row)

如果你想使用SET LOCAL,你会遇到一些问题:

  1. 需要在一个事务块中(即BEGIN/COMMIT/ROLLBACK
  2. 需要以各种命名空间为前缀(任何名称都可以)
  3. 您不能将SELECT其视为会话中浮动的变量,您需要使用current_setting()
  4. 您需要在检索它后将其转换为current_setting()(换句话说,您必须将其存储为文本,而不是 json)

一个工作示例是这样的:

edb=# SET LOCAL myjson = '{"foo":"123", "color":"red"}';
WARNING:  SET LOCAL can only be used in transaction blocks
ERROR:  unrecognized configuration parameter "myjson"
edb=# SET LOCAL myparam.myjson = '{"foo":"123", "color":"red"}';
WARNING:  SET LOCAL can only be used in transaction blocks
SET
edb=# BEGIN;
BEGIN
edb=# SET LOCAL myparam.myjson = '{"foo":"123", "color":"red"}';
SET
edb=# SELECT current_setting('myparam.myjson');
       current_setting        
------------------------------
 {"foo":"123", "color":"red"}
(1 row)

edb=# SELECT current_setting('myparam.myjson')::json ->> 'foo';
 ?column? 
----------
 123
(1 row)

如果您想使用SET但不想使用BEGIN/COMMIT块,请尝试使用SET SESSION(或仅使用 plain SET)。您仍然需要将其放入命名空间(即foo.myjson)并用于current_setting(foo.myjson)提取它。


推荐阅读