Thread

  1. Re: BUG #19362: Extremely log processing of jsonb_path_exists_opr

    Андрей Рачицкий <therealgofman@mail.ru> — 2025-12-23T19:02:57Z

    More examples to reproduce the problem:
    ```
    –- Create a JSON array with nesting depth of 1000
    CREATE TEMP TABLE test_json AS 
    SELECT ('[' || repeat('[', 1000) || '0' || repeat(']', 1000) || ']')::jsonb AS data;
     
    –- Query with 4 `.**` operators executes quickly
    SELECT data @? '$.**.**.**.**' FROM test_json;
     ?column? 
    ----------
     t
    (1 row)
    Time: 0,550 ms
     
    –- Same query with added `.*` in LAX mode hangs > 23 minutes (not the limit)
    SELECT data @? '$.**.**.**.**.*' FROM test_json;
    ^CCancel request sent
    ERROR:  canceling statement due to user request
    Time: 1390914,431 ms (23:10,914)
    ```
     
    Code Analysis (src/backend/utils/adt/jsonpath_exec.c)
    Problem 1: Recursive .** Operator Execution
    The executeAnyItem() function calls itself recursively for each nested level:
    ```
    if (level < last && v.type == jbvBinary)
    {     // Recursive call for each nested level    
          res = executeAnyItem(cxt, jsp, v.val.binary.data, found, level + 1, first, last, ignoreStructuralErrors, unwrapNext);
    }
    ```
    Each additional .** operator forces this recursion to repeat for every already discovered level, creating a multiplicative effect:
     
    N levels × N levels × ... — repeated as many times as there are .** operators.
     
    Problem 2: .* in LAX Mode Triggers Re-entry
    When processing .* for arrays in LAX mode, the code calls executeItemUnwrapTargetArray(), which restarts the recursive traversal from the beginning (adding another full pass through all levels to the already explosive complexity):
    ```
    return executeAnyItem(cxt, jsp, jb->val.binary.data, found,
                          1, 1, 1, false, unwrapElements);  // re-entry! 
    ```
     
    Additional Issues:
    Strict Mode Problems
    ```
    -– Memory allocation error after ~5.5 seconds 
    postgres=# SELECT data @? 'strict $.**.**.**.**.**'::jsonpath from test_json;
    ERROR:  invalid memory alloc request size 1073741824
    Time: 5537,986 ms (00:05,538)
     
    -– Hangs > 32 minutes (not the limit)
    SELECT data @? 'strict $.**.**.**.**.**.*'::jsonpath FROM test_json;
    ^CCancel request sent
    ERROR:  canceling statement due to user request
    Time: 1927249,549 ms (32:07,250)
    ```
    Strict mode exhibits similar behavior to LAX mode, consuming significant CPU resources.
     
    Affected Functions: The jsonb_path_query function is also susceptible to this performance issue.
     
    -- 
    regards, Andrey Rachitskiy