Thread

  1. Ambiguity in IS JSON description and logic

    PG Bug reporting form <noreply@postgresql.org> — 2025-03-19T12:14:33Z

    The following documentation comment has been logged on the website:
    
    Page: https://www.postgresql.org/docs/17/functions-json.html
    Description:
    
    On the manual page
    https://www.postgresql.org/docs/current/functions-json.html, in the Table
    9.48. "SQL/JSON Testing Functions" there is a description of IS JSON. It
    includes the next sentence: "If WITH UNIQUE KEYS is specified, then any
    object in the expression is also tested to see if it has duplicate keys."
    And such text is ambiguous, because the term "object" has certain meaning
    regarding json format. In reality the option WITH UNIQUE KEYS allows to
    check for duplicated keys any array element not object. For objects, both
    WITH UNIQUE KEYS and WITHOUT UNIQUE KEYS return false, and both IS JSON
    ARRAY WITH UNIQUE KEY and IS JSON ARRAY WITHOUT UNIQUE KEY return true (it
    is at the same time with and without unique values, how it is possible?),
    i.e. it works the same as just IS JSON ARRAY. The example code that confirms
    my reasoning:  
    SELECT
        js.vl AS                                      "tested str",             
                              
        js.vl IS JSON OBJECT WITH UNIQUE KEYS AS      ".. object w. UQ keys",   
     
        js.vl IS JSON OBJECT WITHOUT UNIQUE KEYS AS   ".. object w/o UQ keys",
        js.vl IS JSON ARRAY WITH UNIQUE KEYS AS       ".. array w. UQ keys",    
    
        js.vl IS JSON ARRAY WITHOUT UNIQUE KEYS AS    ".. array w/o UQ keys",
        js.vl IS JSON ARRAY ".. array"
    FROM (VALUES ('{{"a": "a1"}, {"a": "a2"}}'), ('[{"a": "a1"}, {"a": "a2"}]'),
    ('["a", "a"]')) AS js(vl);
    
    I'm not sure what should be the right logic for this option, for me it looks
    now the same as simple IS JSON ARRAY without any UNIQUE KEY option, but if
    we use an option it should be either true for WITH UNIQUE KEYS or WITHOUT
    UNIQUE KEYS but not for both at the same time. But anyway the sentence I
    showed above should contain "array" instead of "object" because for objects
    it returns false independently of applied option.  I tested it on
    "PostgreSQL 17.0 on x86_64-windows, compiled by msvc-19.41.34120, 64-bit".
    
  2. Re: Ambiguity in IS JSON description and logic

    Kirk Parker <khp@equatoria.us> — 2025-03-20T14:08:50Z

    On Thu, Mar 20, 2025 at 2:46 AM PG Doc comments form <noreply@postgresql.org>
    wrote:
    
    > The following documentation comment has been logged on the website:
    >
    > Page: https://www.postgresql.org/docs/17/functions-json.html
    > Description:
    >
    > On the manual page
    > https://www.postgresql.org/docs/current/functions-json.html, in the Table
    > 9.48. "SQL/JSON Testing Functions" there is a description of IS JSON. It
    > includes the next sentence: "If WITH UNIQUE KEYS is specified, then any
    > object in the expression is also tested to see if it has duplicate keys."
    > And such text is ambiguous, because the term "object" has certain meaning
    > regarding json format. In reality the option WITH UNIQUE KEYS allows to
    > check for duplicated keys any array element not object. For objects, both
    > WITH UNIQUE KEYS and WITHOUT UNIQUE KEYS return false, and both IS JSON
    > ARRAY WITH UNIQUE KEY and IS JSON ARRAY WITHOUT UNIQUE KEY return true (it
    > is at the same time with and without unique values, how it is possible?),
    > i.e. it works the same as just IS JSON ARRAY. The example code that
    > confirms
    > my reasoning:
    > SELECT
    >     js.vl AS                                      "tested str",
    >
    >
    >     js.vl IS JSON OBJECT WITH UNIQUE KEYS AS      ".. object w. UQ keys",
    >
    >
    >     js.vl IS JSON OBJECT WITHOUT UNIQUE KEYS AS   ".. object w/o UQ keys",
    >     js.vl IS JSON ARRAY WITH UNIQUE KEYS AS       ".. array w. UQ keys",
    >
    >
    >     js.vl IS JSON ARRAY WITHOUT UNIQUE KEYS AS    ".. array w/o UQ keys",
    >     js.vl IS JSON ARRAY ".. array"
    > FROM (VALUES ('{{"a": "a1"}, {"a": "a2"}}'), ('[{"a": "a1"}, {"a":
    > "a2"}]'),
    > ('["a", "a"]')) AS js(vl);
    >
    > I'm not sure what should be the right logic for this option, for me it
    > looks
    > now the same as simple IS JSON ARRAY without any UNIQUE KEY option, but if
    > we use an option it should be either true for WITH UNIQUE KEYS or WITHOUT
    > UNIQUE KEYS but not for both at the same time. But anyway the sentence I
    > showed above should contain "array" instead of "object" because for objects
    > it returns false independently of applied option.  I tested it on
    > "PostgreSQL 17.0 on x86_64-windows, compiled by msvc-19.41.34120, 64-bit".
    >
    
    First, WITHOUT UNIQUE KEYS does not mean "confirm that there are duplicate
    keys", it's just a way of stating the default explicitly. In other words it
    means "w/o testing for duplicate keys". Thus IS JSON OBJECT and IS JSON
    OBJECT WITHOUT UNIQUE KEYS will both always return identical results on the
    same JSON expression.
    
    Secondly, the UNIQUE test is recursive; for objects maybe the meaning is
    intuitive, but for JSON arrays -- which don't have any concept of keys;
    JSON arrays are just ordered lists -- it means "does this array contain any
    embedded objects with duplicate keys".
    
    See:
    
     SELECT js,
      js IS JSON "json?",
      js IS JSON OBJECT "object?",
      js IS JSON OBJECT WITH UNIQUE KEYS "object w. UK?",
      js IS JSON OBJECT WITHOUT UNIQUE KEYS "object w/o UK?",
      js IS JSON ARRAY "array?",
      js IS JSON ARRAY WITH UNIQUE KEYS "array w. UK?",
      js IS JSON ARRAY WITHOUT UNIQUE KEYS "array w/o UK?"
    FROM (VALUES
      ('[{"a":1},{"b":2,"b":3}]'),       -- expect t for array, array w/o UK
      ('[{"a":1},{"b":2,"c":3}]'),       -- expect t for ALL array tests
      ('{"b":2,"b":3}'),                 -- expect t for object, object w/o UK
      ('{"c":2,"d":3}'),                 -- expect t for ALL object tests
      ('{"c":2,"d":{ "e": 0, "e": 1}}'), -- WITH UNIQUE is recursive for nested
    objects
      ('{"c":2,"d":{ "e": 0, "f": {"g":1,"g":2}}}'), -- no matter how deep
      ('[{"a":1},{"b":2,"c":{"d":1, "d":2}}]') -- and also tests arrays
    recursively for embedded objecs
    ) foo(js);
    
    
    A couple of side notes:
    
    1. Your first data example is not JSON at all.  It's helpful for this kind
    of test to include a plain IS JSON column, since any of the IS JSON X tests
    can fail for two reasons: (a) it's not JSON, or (b) it is JSON but it's not
    an X.
    
    2. Curiously, the JSON spec itself is completely silent on the meaning of
    objects with duplicate keys.  PostgreSQL is more helpful in this
    regard--the docs explicitly state that the last value is the one that is
    retained by JSONB and used in processing functions.
    
  3. Re: Ambiguity in IS JSON description and logic

    Kirk Parker <khp@equatoria.us> — 2025-03-20T14:22:14Z

    On Thu, Mar 20, 2025 at 7:08 AM Kirk Parker <khp@equatoria.us> wrote:
    
    >
    > On Thu, Mar 20, 2025 at 2:46 AM PG Doc comments form <
    > noreply@postgresql.org> wrote:
    >
    >> The following documentation comment has been logged on the website:
    >>
    >> Page: https://www.postgresql.org/docs/17/functions-json.html
    >> Description:
    >>
    >> On the manual page
    >> https://www.postgresql.org/docs/current/functions-json.html, in the Table
    >> 9.48. "SQL/JSON Testing Functions" there is a description of IS JSON. It
    >> includes the next sentence: "If WITH UNIQUE KEYS is specified, then any
    >> object in the expression is also tested to see if it has duplicate keys."
    >> And such text is ambiguous, because the term "object" has certain meaning
    >> regarding json format. In reality the option WITH UNIQUE KEYS allows to
    >> check for duplicated keys any array element not object. For objects, both
    >> WITH UNIQUE KEYS and WITHOUT UNIQUE KEYS return false, and both IS JSON
    >> ARRAY WITH UNIQUE KEY and IS JSON ARRAY WITHOUT UNIQUE KEY return true (it
    >> is at the same time with and without unique values, how it is possible?),
    >> i.e. it works the same as just IS JSON ARRAY. The example code that
    >> confirms
    >> my reasoning:
    >> SELECT
    >>     js.vl AS                                      "tested str",
    >>
    >>
    >>     js.vl IS JSON OBJECT WITH UNIQUE KEYS AS      ".. object w. UQ
    >> keys",
    >>
    >>     js.vl IS JSON OBJECT WITHOUT UNIQUE KEYS AS   ".. object w/o UQ keys",
    >>     js.vl IS JSON ARRAY WITH UNIQUE KEYS AS       ".. array w. UQ keys",
    >>
    >>
    >>     js.vl IS JSON ARRAY WITHOUT UNIQUE KEYS AS    ".. array w/o UQ keys",
    >>     js.vl IS JSON ARRAY ".. array"
    >> FROM (VALUES ('{{"a": "a1"}, {"a": "a2"}}'), ('[{"a": "a1"}, {"a":
    >> "a2"}]'),
    >> ('["a", "a"]')) AS js(vl);
    >>
    >> I'm not sure what should be the right logic for this option, for me it
    >> looks
    >> now the same as simple IS JSON ARRAY without any UNIQUE KEY option, but if
    >> we use an option it should be either true for WITH UNIQUE KEYS or WITHOUT
    >> UNIQUE KEYS but not for both at the same time. But anyway the sentence I
    >> showed above should contain "array" instead of "object" because for
    >> objects
    >> it returns false independently of applied option.  I tested it on
    >> "PostgreSQL 17.0 on x86_64-windows, compiled by msvc-19.41.34120, 64-bit".
    >>
    >
    > First, WITHOUT UNIQUE KEYS does not mean "confirm that there are duplicate
    > keys", it's just a way of stating the default explicitly. In other words it
    > means "w/o testing for duplicate keys". Thus IS JSON OBJECT and IS JSON
    > OBJECT WITHOUT UNIQUE KEYS will both always return identical results on the
    > same JSON expression.
    >
    > Secondly, the UNIQUE test is recursive; for objects maybe the meaning is
    > intuitive, but for JSON arrays -- which don't have any concept of keys;
    > JSON arrays are just ordered lists -- it means "does this array contain any
    > embedded objects with duplicate keys".
    >
    > See:
    >
    >  SELECT js,
    >   js IS JSON "json?",
    >   js IS JSON OBJECT "object?",
    >   js IS JSON OBJECT WITH UNIQUE KEYS "object w. UK?",
    >   js IS JSON OBJECT WITHOUT UNIQUE KEYS "object w/o UK?",
    >   js IS JSON ARRAY "array?",
    >   js IS JSON ARRAY WITH UNIQUE KEYS "array w. UK?",
    >   js IS JSON ARRAY WITHOUT UNIQUE KEYS "array w/o UK?"
    > FROM (VALUES
    >   ('[{"a":1},{"b":2,"b":3}]'),       -- expect t for array, array w/o UK
    >   ('[{"a":1},{"b":2,"c":3}]'),       -- expect t for ALL array tests
    >   ('{"b":2,"b":3}'),                 -- expect t for object, object w/o UK
    >   ('{"c":2,"d":3}'),                 -- expect t for ALL object tests
    >   ('{"c":2,"d":{ "e": 0, "e": 1}}'), -- WITH UNIQUE is recursive for
    > nested objects
    >   ('{"c":2,"d":{ "e": 0, "f": {"g":1,"g":2}}}'), -- no matter how deep
    >   ('[{"a":1},{"b":2,"c":{"d":1, "d":2}}]') -- and also tests arrays
    > recursively for embedded objecs
    > ) foo(js);
    >
    >
    > A couple of side notes:
    >
    > 1. Your first data example is not JSON at all.  It's helpful for this kind
    > of test to include a plain IS JSON column, since any of the IS JSON X tests
    > can fail for two reasons: (a) it's not JSON, or (b) it is JSON but it's not
    > an X.
    >
    > 2. Curiously, the JSON spec itself is completely silent on the meaning of
    > objects with duplicate keys.  PostgreSQL is more helpful in this
    > regard--the docs explicitly state that the last value is the one that is
    > retained by JSONB and used in processing functions.
    >
    >
    To improve the documentation here, I would suggest simply adding the word
    "recursively" after "tested":
    
        If WITH UNIQUE KEYS is specified, then any object in the *expression* is
    also tested recursively to see if it has duplicate keys
    
    As for clarifying -- if we need to -- that WITHOUT UNIQUE KEYS means
    "without testing for uniqueness" rather than "confirming that non-unique
    keys are present", I will defer to anyone who has more familiarity with the
    PostgreSQL documentation style.  I probed around looking at other
    situations where there are phrases that make the default condition explicit
    (e.g. CREATE INDEX ... NULLS DISTINCT) that didn't help me much; all my
    attempts ended up too wordy for my liking.
    
  4. Re: Ambiguity in IS JSON description and logic

    noname stranger <vavankaru@gmail.com> — 2025-03-20T16:06:50Z

     Hello Kirk,
    
    Thank you for your answer and detailed explanation. You are absolutely
    right, I made a mistake in my reasoning. On the other hand, some ambiguity
    exists.
    It is a perfect idea to clarify what WITHOUT UNIQUE KEYS means. Such
    clarification could help with understanding the logic of IS JSON faster and
    avoiding confusion and ambiguity. I found the same option for a few JSON
    creation functions in table 9.47 at the same page, like json_object() and
    json(). Their descriptions also don't clarify this point.
    
    Best regards,
    Valery
    
    
    чт, 20 мар. 2025 г. в 15:22, Kirk Parker <khp@equatoria.us>:
    
    > On Thu, Mar 20, 2025 at 7:08 AM Kirk Parker <khp@equatoria.us> wrote:
    >
    >>
    >> On Thu, Mar 20, 2025 at 2:46 AM PG Doc comments form <
    >> noreply@postgresql.org> wrote:
    >>
    >>> The following documentation comment has been logged on the website:
    >>>
    >>> Page: https://www.postgresql.org/docs/17/functions-json.html
    >>> Description:
    >>>
    >>> On the manual page
    >>> https://www.postgresql.org/docs/current/functions-json.html, in the
    >>> Table
    >>> 9.48. "SQL/JSON Testing Functions" there is a description of IS JSON. It
    >>> includes the next sentence: "If WITH UNIQUE KEYS is specified, then any
    >>> object in the expression is also tested to see if it has duplicate keys."
    >>> And such text is ambiguous, because the term "object" has certain meaning
    >>> regarding json format. In reality the option WITH UNIQUE KEYS allows to
    >>> check for duplicated keys any array element not object. For objects, both
    >>> WITH UNIQUE KEYS and WITHOUT UNIQUE KEYS return false, and both IS JSON
    >>> ARRAY WITH UNIQUE KEY and IS JSON ARRAY WITHOUT UNIQUE KEY return true
    >>> (it
    >>> is at the same time with and without unique values, how it is possible?),
    >>> i.e. it works the same as just IS JSON ARRAY. The example code that
    >>> confirms
    >>> my reasoning:
    >>> SELECT
    >>>     js.vl AS                                      "tested str",
    >>>
    >>>
    >>>     js.vl IS JSON OBJECT WITH UNIQUE KEYS AS      ".. object w. UQ
    >>> keys",
    >>>
    >>>     js.vl IS JSON OBJECT WITHOUT UNIQUE KEYS AS   ".. object w/o UQ
    >>> keys",
    >>>     js.vl IS JSON ARRAY WITH UNIQUE KEYS AS       ".. array w. UQ
    >>> keys",
    >>>
    >>>     js.vl IS JSON ARRAY WITHOUT UNIQUE KEYS AS    ".. array w/o UQ keys",
    >>>     js.vl IS JSON ARRAY ".. array"
    >>> FROM (VALUES ('{{"a": "a1"}, {"a": "a2"}}'), ('[{"a": "a1"}, {"a":
    >>> "a2"}]'),
    >>> ('["a", "a"]')) AS js(vl);
    >>>
    >>> I'm not sure what should be the right logic for this option, for me it
    >>> looks
    >>> now the same as simple IS JSON ARRAY without any UNIQUE KEY option, but
    >>> if
    >>> we use an option it should be either true for WITH UNIQUE KEYS or WITHOUT
    >>> UNIQUE KEYS but not for both at the same time. But anyway the sentence I
    >>> showed above should contain "array" instead of "object" because for
    >>> objects
    >>> it returns false independently of applied option.  I tested it on
    >>> "PostgreSQL 17.0 on x86_64-windows, compiled by msvc-19.41.34120,
    >>> 64-bit".
    >>>
    >>
    >> First, WITHOUT UNIQUE KEYS does not mean "confirm that there are
    >> duplicate keys", it's just a way of stating the default explicitly. In
    >> other words it means "w/o testing for duplicate keys". Thus IS JSON OBJECT
    >> and IS JSON OBJECT WITHOUT UNIQUE KEYS will both always return identical
    >> results on the same JSON expression.
    >>
    >> Secondly, the UNIQUE test is recursive; for objects maybe the meaning is
    >> intuitive, but for JSON arrays -- which don't have any concept of keys;
    >> JSON arrays are just ordered lists -- it means "does this array contain any
    >> embedded objects with duplicate keys".
    >>
    >> See:
    >>
    >>  SELECT js,
    >>   js IS JSON "json?",
    >>   js IS JSON OBJECT "object?",
    >>   js IS JSON OBJECT WITH UNIQUE KEYS "object w. UK?",
    >>   js IS JSON OBJECT WITHOUT UNIQUE KEYS "object w/o UK?",
    >>   js IS JSON ARRAY "array?",
    >>   js IS JSON ARRAY WITH UNIQUE KEYS "array w. UK?",
    >>   js IS JSON ARRAY WITHOUT UNIQUE KEYS "array w/o UK?"
    >> FROM (VALUES
    >>   ('[{"a":1},{"b":2,"b":3}]'),       -- expect t for array, array w/o UK
    >>   ('[{"a":1},{"b":2,"c":3}]'),       -- expect t for ALL array tests
    >>   ('{"b":2,"b":3}'),                 -- expect t for object, object w/o UK
    >>   ('{"c":2,"d":3}'),                 -- expect t for ALL object tests
    >>   ('{"c":2,"d":{ "e": 0, "e": 1}}'), -- WITH UNIQUE is recursive for
    >> nested objects
    >>   ('{"c":2,"d":{ "e": 0, "f": {"g":1,"g":2}}}'), -- no matter how deep
    >>   ('[{"a":1},{"b":2,"c":{"d":1, "d":2}}]') -- and also tests arrays
    >> recursively for embedded objecs
    >> ) foo(js);
    >>
    >>
    >> A couple of side notes:
    >>
    >> 1. Your first data example is not JSON at all.  It's helpful for this
    >> kind of test to include a plain IS JSON column, since any of the IS JSON X
    >> tests can fail for two reasons: (a) it's not JSON, or (b) it is JSON but
    >> it's not an X.
    >>
    >> 2. Curiously, the JSON spec itself is completely silent on the meaning of
    >> objects with duplicate keys.  PostgreSQL is more helpful in this
    >> regard--the docs explicitly state that the last value is the one that is
    >> retained by JSONB and used in processing functions.
    >>
    >>
    > To improve the documentation here, I would suggest simply adding the word
    > "recursively" after "tested":
    >
    >     If WITH UNIQUE KEYS is specified, then any object in the *expression* is
    > also tested recursively to see if it has duplicate keys
    >
    > As for clarifying -- if we need to -- that WITHOUT UNIQUE KEYS means
    > "without testing for uniqueness" rather than "confirming that non-unique
    > keys are present", I will defer to anyone who has more familiarity with the
    > PostgreSQL documentation style.  I probed around looking at other
    > situations where there are phrases that make the default condition explicit
    > (e.g. CREATE INDEX ... NULLS DISTINCT) that didn't help me much; all my
    > attempts ended up too wordy for my liking.
    >
    
    
    -- 
    С уважением,
    Валерий
    
  5. Re: Ambiguity in IS JSON description and logic

    David G. Johnston <david.g.johnston@gmail.com> — 2025-03-21T02:04:18Z

    On Thu, Mar 20, 2025 at 7:22 AM Kirk Parker <khp@equatoria.us> wrote:
    
    > On Thu, Mar 20, 2025 at 7:08 AM Kirk Parker <khp@equatoria.us> wrote:
    >
    >>
    >> On Thu, Mar 20, 2025 at 2:46 AM PG Doc comments form <
    >> noreply@postgresql.org> wrote:
    >>
    >>> The following documentation comment has been logged on the website:
    >>>
    >>> Page: https://www.postgresql.org/docs/17/functions-json.html
    >>> Description:
    >>>
    >>> On the manual page
    >>> https://www.postgresql.org/docs/current/functions-json.html, in the
    >>> Table
    >>> 9.48. "SQL/JSON Testing Functions" there is a description of IS JSON. It
    >>> includes the next sentence: "If WITH UNIQUE KEYS is specified, then any
    >>> object in the expression is also tested to see if it has duplicate keys."
    >>> And such text is ambiguous, because the term "object" has certain meaning
    >>> regarding json format. In reality the option WITH UNIQUE KEYS allows to
    >>> check for duplicated keys any array element not object. For objects, both
    >>> WITH UNIQUE KEYS and WITHOUT UNIQUE KEYS return false, and both IS JSON
    >>> ARRAY WITH UNIQUE KEY and IS JSON ARRAY WITHOUT UNIQUE KEY return true
    >>> (it
    >>> is at the same time with and without unique values, how it is possible?),
    >>> i.e. it works the same as just IS JSON ARRAY. The example code that
    >>> confirms
    >>> my reasoning:
    >>> SELECT
    >>>     js.vl AS                                      "tested str",
    >>>
    >>>
    >>>     js.vl IS JSON OBJECT WITH UNIQUE KEYS AS      ".. object w. UQ
    >>> keys",
    >>>
    >>>     js.vl IS JSON OBJECT WITHOUT UNIQUE KEYS AS   ".. object w/o UQ
    >>> keys",
    >>>     js.vl IS JSON ARRAY WITH UNIQUE KEYS AS       ".. array w. UQ
    >>> keys",
    >>>
    >>>     js.vl IS JSON ARRAY WITHOUT UNIQUE KEYS AS    ".. array w/o UQ keys",
    >>>     js.vl IS JSON ARRAY ".. array"
    >>> FROM (VALUES ('{{"a": "a1"}, {"a": "a2"}}'), ('[{"a": "a1"}, {"a":
    >>> "a2"}]'),
    >>> ('["a", "a"]')) AS js(vl);
    >>>
    >>> I'm not sure what should be the right logic for this option, for me it
    >>> looks
    >>> now the same as simple IS JSON ARRAY without any UNIQUE KEY option, but
    >>> if
    >>> we use an option it should be either true for WITH UNIQUE KEYS or WITHOUT
    >>> UNIQUE KEYS but not for both at the same time. But anyway the sentence I
    >>> showed above should contain "array" instead of "object" because for
    >>> objects
    >>> it returns false independently of applied option.  I tested it on
    >>> "PostgreSQL 17.0 on x86_64-windows, compiled by msvc-19.41.34120,
    >>> 64-bit".
    >>>
    >>
    >> First, WITHOUT UNIQUE KEYS does not mean "confirm that there are
    >> duplicate keys", it's just a way of stating the default explicitly. In
    >> other words it means "w/o testing for duplicate keys". Thus IS JSON OBJECT
    >> and IS JSON OBJECT WITHOUT UNIQUE KEYS will both always return identical
    >> results on the same JSON expression.
    >>
    >> Secondly, the UNIQUE test is recursive; for objects maybe the meaning is
    >> intuitive, but for JSON arrays -- which don't have any concept of keys;
    >> JSON arrays are just ordered lists -- it means "does this array contain any
    >> embedded objects with duplicate keys".
    >>
    >> See:
    >>
    >>  SELECT js,
    >>   js IS JSON "json?",
    >>   js IS JSON OBJECT "object?",
    >>   js IS JSON OBJECT WITH UNIQUE KEYS "object w. UK?",
    >>   js IS JSON OBJECT WITHOUT UNIQUE KEYS "object w/o UK?",
    >>   js IS JSON ARRAY "array?",
    >>   js IS JSON ARRAY WITH UNIQUE KEYS "array w. UK?",
    >>   js IS JSON ARRAY WITHOUT UNIQUE KEYS "array w/o UK?"
    >> FROM (VALUES
    >>   ('[{"a":1},{"b":2,"b":3}]'),       -- expect t for array, array w/o UK
    >>   ('[{"a":1},{"b":2,"c":3}]'),       -- expect t for ALL array tests
    >>   ('{"b":2,"b":3}'),                 -- expect t for object, object w/o UK
    >>   ('{"c":2,"d":3}'),                 -- expect t for ALL object tests
    >>   ('{"c":2,"d":{ "e": 0, "e": 1}}'), -- WITH UNIQUE is recursive for
    >> nested objects
    >>   ('{"c":2,"d":{ "e": 0, "f": {"g":1,"g":2}}}'), -- no matter how deep
    >>   ('[{"a":1},{"b":2,"c":{"d":1, "d":2}}]') -- and also tests arrays
    >> recursively for embedded objecs
    >> ) foo(js);
    >>
    >>
    >> A couple of side notes:
    >>
    >> 1. Your first data example is not JSON at all.  It's helpful for this
    >> kind of test to include a plain IS JSON column, since any of the IS JSON X
    >> tests can fail for two reasons: (a) it's not JSON, or (b) it is JSON but
    >> it's not an X.
    >>
    >> 2. Curiously, the JSON spec itself is completely silent on the meaning of
    >> objects with duplicate keys.  PostgreSQL is more helpful in this
    >> regard--the docs explicitly state that the last value is the one that is
    >> retained by JSONB and used in processing functions.
    >>
    >>
    > To improve the documentation here, I would suggest simply adding the word
    > "recursively" after "tested":
    >
    >     If WITH UNIQUE KEYS is specified, then any object in the *expression* is
    > also tested recursively to see if it has duplicate keys
    >
    >
    I think the existing word "any" sufficiently implies "recursively".  It
    also doesn't really address the complaint here.  I'm thinking something
    more like:
    
    (this is changed intentionally, see below)
    
    expression IS [ NOT ] JSON [ { SCALAR | ARRAY | OBJECT } ] [ WITH UNIQUE ]
    -> boolean
    
    This predicate tests whether expression can be parsed as JSON.  Two
    additional properties can be tested at the same time: the type of the JSON
    value, and whether it passes the unique object keys constraint.  Enable the
    first test by specifying one of SCALAR, ARRAY, or OBJECT.  Enable the
    second test by specifying WITH UNIQUE: This test is applied to all objects
    contained within the JSON value.  The return value is true only if
    expression can be parsed as JSON and all enabled tests pass.  The return
    value is inverted if NOT is specified.
    
    The test label "array w/o UK?" has to go.  Coupling with the "additional
    tests" idea introduced above, and the recommended syntax, we should do
    something like:
    
    SELECT js,
      js IS JSON "parses ok, no tests",
      js IS JSON OBJECT "object test only",
      js IS JSON ARRAY "array test only",
      js IS JSON WITH UNIQUE "unique test only",
      js IS JSON ARRAY WITH UNIQUE "array and unique tests"
    
    
    Then, to keep the technical reference thorough, re-add the full syntax at
    the end.
    
    
    This is the full syntax accepted for this predicate.  Both VALUE and
    WITHOUT, the default explicit keywords to disable the two additional tests,
    as well as KEYS, are omitted above for clarity.
    expression IS [ NOT ] JSON [ { VALUE | SCALAR | ARRAY | OBJECT } ] [ { WITH
    | WITHOUT } UNIQUE [ KEYS ] ]
    
    David J.
    
  6. Re: Ambiguity in IS JSON description and logic

    Ilmar Yunusov <tanswis42@gmail.com> — 2025-11-18T11:55:00Z

    Hi,
    
    Based on this discussion, I’ve prepared a small documentation patch that
    tries to clarify the behavior of the IS JSON ... UNIQUE KEYS clause.
    
    The patch explains that:
    - the WITH/WITHOUT UNIQUE KEYS clause controls an additional test on
      duplicate object keys,
    - WITH UNIQUE KEYS requires that no object contained in the expression
      (recursively) have duplicate keys, and
    - WITHOUT UNIQUE KEYS, which is also the default, just disables this
      additional test and does not require duplicates to be present.
    
    Patch is attached.
    
    Regards,
    Ilmar Yunusov
    
    вт, 18 нояб. 2025 г. в 16:50, David G. Johnston <david.g.johnston@gmail.com
    >:
    
    > On Thu, Mar 20, 2025 at 7:22 AM Kirk Parker <khp@equatoria.us> wrote:
    >
    >> On Thu, Mar 20, 2025 at 7:08 AM Kirk Parker <khp@equatoria.us> wrote:
    >>
    >>>
    >>> On Thu, Mar 20, 2025 at 2:46 AM PG Doc comments form <
    >>> noreply@postgresql.org> wrote:
    >>>
    >>>> The following documentation comment has been logged on the website:
    >>>>
    >>>> Page: https://www.postgresql.org/docs/17/functions-json.html
    >>>> Description:
    >>>>
    >>>> On the manual page
    >>>> https://www.postgresql.org/docs/current/functions-json.html, in the
    >>>> Table
    >>>> 9.48. "SQL/JSON Testing Functions" there is a description of IS JSON. It
    >>>> includes the next sentence: "If WITH UNIQUE KEYS is specified, then any
    >>>> object in the expression is also tested to see if it has duplicate
    >>>> keys."
    >>>> And such text is ambiguous, because the term "object" has certain
    >>>> meaning
    >>>> regarding json format. In reality the option WITH UNIQUE KEYS allows to
    >>>> check for duplicated keys any array element not object. For objects,
    >>>> both
    >>>> WITH UNIQUE KEYS and WITHOUT UNIQUE KEYS return false, and both IS JSON
    >>>> ARRAY WITH UNIQUE KEY and IS JSON ARRAY WITHOUT UNIQUE KEY return true
    >>>> (it
    >>>> is at the same time with and without unique values, how it is
    >>>> possible?),
    >>>> i.e. it works the same as just IS JSON ARRAY. The example code that
    >>>> confirms
    >>>> my reasoning:
    >>>> SELECT
    >>>>     js.vl AS                                      "tested str",
    >>>>
    >>>>
    >>>>     js.vl IS JSON OBJECT WITH UNIQUE KEYS AS      ".. object w. UQ
    >>>> keys",
    >>>>
    >>>>     js.vl IS JSON OBJECT WITHOUT UNIQUE KEYS AS   ".. object w/o UQ
    >>>> keys",
    >>>>     js.vl IS JSON ARRAY WITH UNIQUE KEYS AS       ".. array w. UQ
    >>>> keys",
    >>>>
    >>>>     js.vl IS JSON ARRAY WITHOUT UNIQUE KEYS AS    ".. array w/o UQ
    >>>> keys",
    >>>>     js.vl IS JSON ARRAY ".. array"
    >>>> FROM (VALUES ('{{"a": "a1"}, {"a": "a2"}}'), ('[{"a": "a1"}, {"a":
    >>>> "a2"}]'),
    >>>> ('["a", "a"]')) AS js(vl);
    >>>>
    >>>> I'm not sure what should be the right logic for this option, for me it
    >>>> looks
    >>>> now the same as simple IS JSON ARRAY without any UNIQUE KEY option, but
    >>>> if
    >>>> we use an option it should be either true for WITH UNIQUE KEYS or
    >>>> WITHOUT
    >>>> UNIQUE KEYS but not for both at the same time. But anyway the sentence I
    >>>> showed above should contain "array" instead of "object" because for
    >>>> objects
    >>>> it returns false independently of applied option.  I tested it on
    >>>> "PostgreSQL 17.0 on x86_64-windows, compiled by msvc-19.41.34120,
    >>>> 64-bit".
    >>>>
    >>>
    >>> First, WITHOUT UNIQUE KEYS does not mean "confirm that there are
    >>> duplicate keys", it's just a way of stating the default explicitly. In
    >>> other words it means "w/o testing for duplicate keys". Thus IS JSON OBJECT
    >>> and IS JSON OBJECT WITHOUT UNIQUE KEYS will both always return identical
    >>> results on the same JSON expression.
    >>>
    >>> Secondly, the UNIQUE test is recursive; for objects maybe the meaning is
    >>> intuitive, but for JSON arrays -- which don't have any concept of keys;
    >>> JSON arrays are just ordered lists -- it means "does this array contain any
    >>> embedded objects with duplicate keys".
    >>>
    >>> See:
    >>>
    >>>  SELECT js,
    >>>   js IS JSON "json?",
    >>>   js IS JSON OBJECT "object?",
    >>>   js IS JSON OBJECT WITH UNIQUE KEYS "object w. UK?",
    >>>   js IS JSON OBJECT WITHOUT UNIQUE KEYS "object w/o UK?",
    >>>   js IS JSON ARRAY "array?",
    >>>   js IS JSON ARRAY WITH UNIQUE KEYS "array w. UK?",
    >>>   js IS JSON ARRAY WITHOUT UNIQUE KEYS "array w/o UK?"
    >>> FROM (VALUES
    >>>   ('[{"a":1},{"b":2,"b":3}]'),       -- expect t for array, array w/o UK
    >>>   ('[{"a":1},{"b":2,"c":3}]'),       -- expect t for ALL array tests
    >>>   ('{"b":2,"b":3}'),                 -- expect t for object, object w/o
    >>> UK
    >>>   ('{"c":2,"d":3}'),                 -- expect t for ALL object tests
    >>>   ('{"c":2,"d":{ "e": 0, "e": 1}}'), -- WITH UNIQUE is recursive for
    >>> nested objects
    >>>   ('{"c":2,"d":{ "e": 0, "f": {"g":1,"g":2}}}'), -- no matter how deep
    >>>   ('[{"a":1},{"b":2,"c":{"d":1, "d":2}}]') -- and also tests arrays
    >>> recursively for embedded objecs
    >>> ) foo(js);
    >>>
    >>>
    >>> A couple of side notes:
    >>>
    >>> 1. Your first data example is not JSON at all.  It's helpful for this
    >>> kind of test to include a plain IS JSON column, since any of the IS JSON X
    >>> tests can fail for two reasons: (a) it's not JSON, or (b) it is JSON but
    >>> it's not an X.
    >>>
    >>> 2. Curiously, the JSON spec itself is completely silent on the meaning
    >>> of objects with duplicate keys.  PostgreSQL is more helpful in this
    >>> regard--the docs explicitly state that the last value is the one that is
    >>> retained by JSONB and used in processing functions.
    >>>
    >>>
    >> To improve the documentation here, I would suggest simply adding the word
    >> "recursively" after "tested":
    >>
    >>     If WITH UNIQUE KEYS is specified, then any object in the *expression* is
    >> also tested recursively to see if it has duplicate keys
    >>
    >>
    > I think the existing word "any" sufficiently implies "recursively".  It
    > also doesn't really address the complaint here.  I'm thinking something
    > more like:
    >
    > (this is changed intentionally, see below)
    >
    > expression IS [ NOT ] JSON [ { SCALAR | ARRAY | OBJECT } ] [ WITH UNIQUE ]
    > -> boolean
    >
    > This predicate tests whether expression can be parsed as JSON.  Two
    > additional properties can be tested at the same time: the type of the JSON
    > value, and whether it passes the unique object keys constraint.  Enable the
    > first test by specifying one of SCALAR, ARRAY, or OBJECT.  Enable the
    > second test by specifying WITH UNIQUE: This test is applied to all objects
    > contained within the JSON value.  The return value is true only if
    > expression can be parsed as JSON and all enabled tests pass.  The return
    > value is inverted if NOT is specified.
    >
    > The test label "array w/o UK?" has to go.  Coupling with the "additional
    > tests" idea introduced above, and the recommended syntax, we should do
    > something like:
    >
    > SELECT js,
    >   js IS JSON "parses ok, no tests",
    >   js IS JSON OBJECT "object test only",
    >   js IS JSON ARRAY "array test only",
    >   js IS JSON WITH UNIQUE "unique test only",
    >   js IS JSON ARRAY WITH UNIQUE "array and unique tests"
    >
    >
    > Then, to keep the technical reference thorough, re-add the full syntax at
    > the end.
    >
    >
    > This is the full syntax accepted for this predicate.  Both VALUE and
    > WITHOUT, the default explicit keywords to disable the two additional tests,
    > as well as KEYS, are omitted above for clarity.
    > expression IS [ NOT ] JSON [ { VALUE | SCALAR | ARRAY | OBJECT } ] [ {
    > WITH | WITHOUT } UNIQUE [ KEYS ] ]
    >
    > David J.
    >
    >
    
  7. Re: Ambiguity in IS JSON description and logic

    Ilmar Yunusov <tanswis42@gmail.com> — 2025-11-18T11:56:50Z

    I’ve sent a docs patch to pgsql-docs in the
    “Ambiguity in IS JSON description and logic” thread.
    
    The patch clarifies that the WITH/WITHOUT UNIQUE KEYS clause for IS JSON
    controls an additional test for duplicate object keys, that WITH UNIQUE KEYS
    enforces this constraint recursively, and that WITHOUT UNIQUE KEYS (the default)
    disables this additional test rather than requiring duplicates to be present.
  8. Re: Ambiguity in IS JSON description and logic

    Laurenz Albe <laurenz.albe@cybertec.at> — 2025-11-18T16:37:21Z

    On Tue, 2025-11-18 at 16:55 +0500, r314tive wrote:
    > Based on this discussion, I’ve prepared a small documentation patch that
    > tries to clarify the behavior of the IS JSON ... UNIQUE KEYS clause.
    > 
    > The patch explains that:
    > - the WITH/WITHOUT UNIQUE KEYS clause controls an additional test on
    >   duplicate object keys,
    > - WITH UNIQUE KEYS requires that no object contained in the expression
    >   (recursively) have duplicate keys, and
    > - WITHOUT UNIQUE KEYS, which is also the default, just disables this
    >   additional test and does not require duplicates to be present.
    > 
    > Patch is attached.
    
    I don't think that the patch is really an improvement.  To me, the original
    wording contains all the important information.  The patch it somewhat
    repetetive and contains unnecessary detail.  For example, I can see from
    the syntax diagram that I can use WITHOUT UNIQUE KEYS, and I have no
    trouble guessing what that means.
    
    Perhaps it would be enough to add something like "*recursively* checks for
    duplicate keys" or "checks for duplicate keys *on any nesting level*".
    
    Yours,
    Laurenz Albe