> On Nov 3, 2026, at 03:13, Ilia Evdokimov <[email protected]> 
> wrote:
> 
> Hi,
> 
> On an assertion-enabled build, `pg_restore_extended_stats()` triggers an 
> assertion failure in check_all_expr_argnames_valid() when an expression 
> statistics object contains an unknown JSON key with an array value
> 
> ```
> CREATE TABLE t (i INT);
> CREATE STATISTICS t_stats ON (i + 1) FROM t;
> SELECT pg_catalog.pg_restore_extended_stats(
>     'schemaname', 'public',
>     'relname', 't',
>     'statistics_schemaname', 'public',
>     'statistics_name', 't_stats',
>     'inherited', false,
>     'exprs', '[{"unknown": []}]'::jsonb
> );
> 
> server closed the connection unexpectedly
>     This probably means the server terminated abnormally
>     before or while processing the request.
> The connection to the server was lost. Attempting reset: Failed.
> The connection to the server was lost. Attempting reset: Failed.
> ```
> 
> Logs
> ```
> [506971] WARNING:  argument "exprs" has type text, expected type jsonb
> TRAP: failed Assert("jitok == WJB_VALUE"), File: "extended_stats_funcs.c", 
> Line: 950, PID: 506971
> postgres: postgres postgres [local] SELECT(ExceptionalCondition+0x6f) 
> [0x5bf2098a799f]
> postgres: postgres postgres [local] SELECT(pg_restore_extended_stats+0x2180) 
> [0x5bf2096d5390]
> postgres: postgres postgres [local] SELECT(+0x3a50d0) [0x5bf2095110d0]
> postgres: postgres postgres [local] SELECT(+0x3e581a) [0x5bf20955181a]
> postgres: postgres postgres [local] SELECT(standard_ExecutorRun+0x18a) 
> [0x5bf2095155ea]
> postgres: postgres postgres [local] SELECT(+0x5ca6f8) [0x5bf2097366f8]
> postgres: postgres postgres [local] SELECT(PortalRun+0x24e) [0x5bf20973812e]
> postgres: postgres postgres [local] SELECT(+0x5c7837) [0x5bf209733837]
> postgres: postgres postgres [local] SELECT(PostgresMain+0x17d3) 
> [0x5bf2097354a3]
> postgres: postgres postgres [local] SELECT(BackendMain+0x53) [0x5bf20972f143]
> postgres: postgres postgres [local] SELECT(postmaster_child_launch+0x103) 
> [0x5bf209669bb3]
> postgres: postgres postgres [local] SELECT(+0x501924) [0x5bf20966d924]
> postgres: postgres postgres [local] SELECT(PostmasterMain+0xd6b) 
> [0x5bf20966f45b]
> postgres: postgres postgres [local] SELECT(main+0x1d8) [0x5bf2092febb8]
> /usr/lib/x86_64-linux-gnu/libc.so.6(+0x2a601) [0x7adbd022a601]
> /usr/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x88) [0x7adbd022a718]
> postgres: postgres postgres [local] SELECT(_start+0x25) [0x5bf2092fef25]
> [506931] LOG:  client backend (PID 506971) was terminated by signal 6: Aborted
> [506931] DETAIL:  Failed process was running: SELECT 
> pg_catalog.pg_restore_extended_stats(
>        'schemaname', 'public',
>        'relname', 'test_expr_stats',
>        'statistics_schemaname', 'public',
>        'statistics_name', 'test_expr_stats_s',
>        'inherited', false,
>        'exprs', '[{"unknown": []}]'::jsonb
>    );
> ```
> 
> 
> The fix passes skipNested = true when reading a value. This function only 
> validates the keys of the outer object, so it should consume each value as a 
> single WJB_VALUE token rather than descend into nested containers.
> 
> Does anyone see any issues with this fix, or suggest a different approach?
> 
> --
> Best regards,
> Ilia Evdokimov,
> Tantor Labs LLC,
> https://tantorlabs.com/
> <v1-0001-Fix-iteration-over-nested-JSON-values-in-expressi.patch>

I doubt this bug is likely to be encountered in practice, but I agree that the 
server should not crash on invalid input anyway.

The fix looks correct to me.

Looking at import_pg_statistic(), we can see that the existing code already 
rejects nested values for recognized keys.
```
        /*
         * Loop through all keys that we need to look up.  If any value found is
         * neither a string nor a NULL, there is not much we can do, so just 
give
         * on the entire tuple for this expression.
         */
        for (int i = 0; i < NUM_ATTRIBUTE_STATS_ELEMS; i++)
        {
                const char *s = extexprargname[i];
                int                     len = strlen(s);

                if (getKeyJsonValueFromContainer(cont, s, len, &val[i]) == NULL)
                        continue;

                switch (val[i].type)
                {
                        case jbvString:
                                found[i] = true;
                                break;

                        case jbvNull:
                                break;

                        default:
                                ereport(WARNING,
                                                
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                                errmsg("could not parse \"%s\": 
invalid element in expression %d", argname, exprnum),
                                                errhint("Value of element 
\"%s\" must be a null or a string.", s));
                                goto pg_statistic_error;
                }
        }
```

By changing skipNested to true, JsonbIteratorNext() returns WJB_VALUE for a 
nested array or object instead of descending into it. This avoids the assertion 
failure and allows the iterator to continue with the next top-level key.

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/






Reply via email to