PostgreSQL 15.4 jsonb_to_recordset 和 jsonb_populate_recordset 的一个奇怪问题

问题描述 投票:0回答:2

我有一个 postgresql 函数,它接受多个参数,最后一个是 jsonb。 使用以下参数值调用它(由于 postgres 诊断输出而使用双引号):

SELECT vspm.rfd_add_f
(
  $1::bigint
  , $2::bigint
  , $3::bigint
  , $4::timestamptz
  , $5::timestamptz
  , $6::bigint
  , $7::bigint
  , $8::text
  , $9::text
  , $10::timestamptz
  , $11::text
  , $12::jsonb
)
","parameters: $1 = NULL, $2 = '1', $3 = '18014398509482060', $4 = NULL, $5 = NULL, $6 = '18014398509484942', $7 = '18014398509482082', $8 = NULL, $9 = NULL, $10 = NULL, $11 = NULL, $12 = '[{""id"":null,""modUserId"":null,""validFrom"":null,""validTo"":null,""clientId"":null,""client"":null,""rfdId"":null,""rfd"":null,""rfdNumber"":null,""rfdDate"":null,""managerId"":null,""manager"":null,""estimateId"":null,""estimate"":null,""productionNeedId"":""18014398509489518"",""quantity"":""111000000"",""sortOrder"":null,""wantedDeliveryDate"":""2023-12-11T18:00:00Z"",""objectId"":null,""object"":null,""section"":null,""nomenclatureId"":null,""nomenclature"":null,""muId"":null,""mu"":null,""remarks"":null},{""id"":null,""modUserId"":null,""validFrom"":null,""validTo"":null,""clientId"":null,""client"":null,""rfdId"":null,""rfd"":null,""rfdNumber"":null,""rfdDate"":null,""managerId"":null,""manager"":null,""estimateId"":null,""estimate"":null,""productionNeedId"":""18014398509489524"",""quantity"":""222000000"",""sortOrder"":null,""wantedDeliveryDate"":""2023-12-12T18:00:00Z"",""objectId"":null,""object"":null,""section"":null,""nomenclatureId"":null,""nomenclature"":null,""muId"":null,""mu"":null,""remarks"":null},{""id"":null,""modUserId"":null,""validFrom"":null,""validTo"":null,""clientId"":null,""client"":null,""rfdId"":null,""rfd"":null,""rfdNumber"":null,""rfdDate"":null,""managerId"":null,""manager"":null,""estimateId"":null,""estimate"":null,""productionNeedId"":""18014398509489530"",""quantity"":""333000000"",""sortOrder"":null,""wantedDeliveryDate"":""2023-12-13T18:00:00Z"",""objectId"":null,""object"":null,""section"":null,""nomenclatureId"":null,""nomenclature"":null,""muId"":null,""mu"":null,""remarks"":null}]'",,,,,,,,"PostgreSQL JDBC Driver","client backend",,0

在这种情况下,以下字段很重要:

[
    {
        "productionNeedId": "18014398509489518"
        , "quantity": "111000000"
        , "wantedDeliveryDate": "2023-12-11T21:00:00+03:00"
    }
    , {
        "productionNeedId": "18014398509489524"
        , "quantity": "222000000"
        , "wantedDeliveryDate": "2023-12-12T21:00:00+03:00"
    }
    , {
        "productionNeedId": "18014398509489530"
        , "quantity": "333000000"
        , "wantedDeliveryDate": "2023-12-13T21:00:00+03:00"
    }
]

在调试函数的开头有以下语句:

RAISE LOG '==> rfditem_add_many_f: json quantity = %, productionNeedId = %, wantedDeliveryDate = %'
  , (pitems::jsonb->0)::jsonb->>'quantity'
  , (pitems::jsonb->0)::jsonb->>'productionNeedId'
  , (pitems::jsonb->0)::jsonb->>'wantedDeliveryDate'
;
RAISE LOG '==> rfditem_add_many_f: json quantity = %, productionNeedId = %, wantedDeliveryDate = %'''
  , (pitems::jsonb->1)::jsonb->>'quantity'
  , (pitems::jsonb->1)::jsonb->>'productionNeedId'
  , (pitems::jsonb->1)::jsonb->>'wantedDeliveryDate'
;
RAISE LOG '==> rfditem_add_many_f: json quantity = %, productionNeedId = %, wantedDeliveryDate = %'''
  , (pitems::jsonb->2)::jsonb->>'quantity'  
  , (pitems::jsonb->2)::jsonb->>'productionNeedId'
  , (pitems::jsonb->2)::jsonb->>'wantedDeliveryDate'
;

我得到了正确的输出:

"==> rfditem_add_many_f: json quantity = 111000000, productionNeedId = 18014398509489518, wantedDeliveryDate = 2023-12-11T18:00:00Z",,,,,"PL/pgSQL function rfditem_add_many_f(bigint,bigint,timestamp with time zone,timestamp with time zone,bigint,timestamp with time zone,text,jsonb) line 20 at RAISE


"==> rfditem_add_many_f: json quantity = 222000000, productionNeedId = 18014398509489524, wantedDeliveryDate = 2023-12-12T18:00:00Z'",,,,,"PL/pgSQL function rfditem_add_many_f(bigint,bigint,timestamp with time zone,timestamp with time zone,bigint,timestamp with time zone,text,jsonb) line 25 at RAISE


"==> rfditem_add_many_f: json quantity = 333000000, productionNeedId = 18014398509489530, wantedDeliveryDate = 2023-12-13T18:00:00Z'",,,,,"PL/pgSQL function rfditem_add_many_f(bigint,bigint,timestamp with time zone,timestamp with time zone,bigint,timestamp with time zone,text,jsonb) line 30 at RAISE

但是当我进一步尝试使用 jsonb_to_recordset 函数处理这个 jsonb 时,找不到 ProductionNeedId 和 WantDeliveryDate 字段的值。

FOR lrecord IN
  SELECT
    id, moduserId, validFrom, validTo, clientId, rfdId, productionNeedId, quantity, sortOrder, wantedDeliveryDate, remarks
  FROM jsonb_to_recordset(pitems) AS (id bigint, moduserId bigint, validFrom timestamptz, validTo timestamptz, clientId bigint, rfdId bigint, productionNeedId bigint, quantity bigint, sortOrder int, wantedDeliveryDate timestamptz, remarks text)
LOOP
      RAISE LOG '==> rfditem_add_many_f read : validFrom = %, validTo = %, clientid = %, rfdId = %, productionNeedId = %, quantity = %, sortOrder = %, wantedDeliveryDate = %, remarks = %'
        , coalesce(lrecord.validFrom, '1900-01-01'::timestamptz)
        , coalesce(lrecord.validTo, '1900-01-01'::timestamptz)
        , coalesce(lrecord.clientId, -1)
        , coalesce(lrecord.rfdId, -1)
        , coalesce(lrecord.productionNeedId, -1)
        , coalesce(lrecord.quantity, -1)
        , coalesce(lrecord.sortOrder, -1)
        , coalesce(lrecord.wantedDeliveryDate, '1900-01-01'::timestamptz)
        , coalesce(lrecord.remarks, '')
      ;

"==> rfditem_add_many_f read : validFrom = 1900-01-01 00:00:00+02:30:17, validTo = 1900-01-01 00:00:00+02:30:17, clientid = -1, rfdId = -1, productionNeedId = -1, quantity = 111000000, sortOrder = -1, wantedDeliveryDate = 1900-01-01 00:00:00+02:30:17, remarks = "

json 数组的剩余两个元素不会被处理,因为稍后在函数中,当尝试向数据库表添加记录时,发生了完整性约束冲突。

我还尝试使用以下函数调用:

FOR lrecord IN
  SELECT
    validFrom, validTo, clientId, rfdId, productionNeedId, quantity, sortOrder, wantedDeliveryDate, remarks
  FROM jsonb_populate_recordset(null::rfditem_type, pitems)
LOOP

rfditem_type 声明为:

CREATE TYPE rfditem_type AS
(
  id                    bigint

  , moduserId           bigint
  , validFrom           timestamptz
  , validTo             timestamptz

  , clientId            bigint

  , rfdId               bigint
  , productionNeedId    bigint
  , quantity            bigint

  , sortOrder           integer

  , wantedDeliveryDate  timestamptz

  , remarks             text
);

但结果更糟糕(调试输出是一样的):

"==> rfditem_add_many_f read : validFrom = 1900-01-01 00:00:00+02:30:17, validTo = 1900-01-01 00:00:00+02:30:17, clientid = -1, rfdId = 111000000, productionNeedId = -1, quantity = -1, sortOrder = -1, wantedDeliveryDate = 1900-01-01 00:00:00+02:30:17, remarks = "

...
"==> rfditem_add_many_f read : validFrom = 1900-01-01 00:00:00+02:30:17, validTo = 1900-01-01 00:00:00+02:30:17, clientid = -1, rfdId = 222000000, productionNeedId = -1, quantity = -1, sortOrder = -1, wantedDeliveryDate = 1900-01-01 00:00:00+02:30:17, remarks = "
...
"==> rfditem_add_many_f read : validFrom = 1900-01-01 00:00:00+02:30:17, validTo = 1900-01-01 00:00:00+02:30:17, clientid = -1, rfdId = 333000000, productionNeedId = -1, quantity = -1, sortOrder = -1, wantedDeliveryDate = 1900-01-01 00:00:00+02:30:17, remarks = "

数量的值已分配给 rfdId 字段,其他字段未分配

FOR lrecord IN
  SELECT * from jsonb_populate_recordset(null::rfditem_type, pitems)
LOOP

在这种情况下,结果略有不同:

"==> rfditem_add_many_f read : validFrom = 1900-01-01 00:00:00+02:30:17, validTo = 1900-01-01 00:00:00+02:30:17, clientid = -1, rfdId = -1, productionNeedId = -1, quantity = 111000000, sortOrder = -1, wantedDeliveryDate = 1900-01-01 00:00:00+02:30:17, remarks = "

也就是说,数量字段的值已正确分配,但未找到 ProductionNeedId 和 WantedDeliveryDate 字段

我有一个想法,这可能是由于字段名称中字母的大小写不同造成的,但是为什么诊断输出中一切都正常工作呢? (我想在编辑后端之前确定是这种情况)。

json postgresql jsonb
2个回答
0
投票

是的,这几乎肯定是由于外壳不同造成的。当您不引用标识符时,Postgres 会将它们视为小写字母,这将导致在 JSON 对象中找不到它们。使用

->>
并将属性名称作为字符串文字不会出现此问题。

所以使用

FOR lrecord IN
  SELECT *
  FROM jsonb_to_recordset(pitems) AS ("id" bigint, "moduserId" bigint, "validFrom" timestamptz, "validTo" timestamptz, "clientId" bigint, "rfdId" bigint, "productionNeedId" bigint, "quantity" bigint, "sortOrder" int, "wantedDeliveryDate" timestamptz, "remarks" text)
LOOP
  RAISE LOG '==> rfditem_add_many_f read : validFrom = %, validTo = %, clientid = %, rfdId = %, productionNeedId = %, quantity = %, sortOrder = %, wantedDeliveryDate = %, remarks = %'
    , coalesce(lrecord."validFrom", '1900-01-01'::timestamptz)
    , coalesce(lrecord."validTo", '1900-01-01'::timestamptz)
    , coalesce(lrecord."clientId", -1)
    , coalesce(lrecord."rfdId", -1)
    , coalesce(lrecord."productionNeedId", -1)
    , coalesce(lrecord."quantity", -1)
    , coalesce(lrecord."sortOrder", -1)
    , coalesce(lrecord."wantedDeliveryDate", '1900-01-01'::timestamptz)
    , coalesce(lrecord."remarks", '')
  ;

0
投票

是的,问题肯定出在 json 属性名称的大小写上。重新设计背景并开始仅以小写形式传输这些名称后,一切正常。

我希望 Postgresql 团队在处理 json 的函数和运算符上有更大的一致性。如果第一个诊断输出中的属性

((pitems::jsonb->0)::jsonb->>'productionNeedId')
与随后在函数 jsonb_to_recordsetjsonb_populate_recordset 中的定位方式不同,问题会更早地被识别和解决

© www.soinside.com 2019 - 2024. All rights reserved.