Coding & Refactoringlow risk

jsonpath-and-jsonb

PostgreSQL's SQL/JSON support — jsonpath (SQL:2016 path language) + jsonb (binary-encoded JSON) + the SQL/JSON operators like `->`, `->>`, `@?`, `@@`, `jsonb_path_query`. Covers `src/backend/utils/adt/jsonb*.c` (jsonb storage + operators + GIN opclass + subscripting) and `jsonpath*.c` (path language parser + executor). Loads when the user asks about jsonpath semantics, path variables ($, @, current), lax vs strict mode, predicate expressions, `jsonb_path_query` / `jsonb_path_exists`, JSON_TABLE (SQL:2023 / PG 17+), jsonb_gin operator class, memory management in jsonpath_exec (Tom Lane's 5a2043bf713 rewrite), or "why is my JSON search slow" (usually GIN or path complexity). Skip when the ask is about the older `json` type (`json.c` is separate — text representation) or about JSON output formats (that's `format_type` territory).

matejformanek/postgres-claude·.claude/skills/jsonpath-and-jsonb/SKILL.md
85/ 100质量分

导入这个 Skill

选择你的 coding agent,复制项目级或个人级安装命令。

固定到平台收录的 commit
导入当前项目.agents/skills/jsonpath-and-jsonb
npx skills add https://github.com/matejformanek/postgres-claude/tree/eb838af606a4c3c5a2efc4302e49d85a39c2fd44/.claude/skills/jsonpath-and-jsonb -a codex -y
导入个人环境~/.agents/skills/jsonpath-and-jsonb
npx skills add https://github.com/matejformanek/postgres-claude/tree/eb838af606a4c3c5a2efc4302e49d85a39c2fd44/.claude/skills/jsonpath-and-jsonb -a codex -g -y
手动放置目录.agents/skills/jsonpath-and-jsonbOfficial docs ↗
导入当前项目.claude/skills/jsonpath-and-jsonb
npx skills add https://github.com/matejformanek/postgres-claude/tree/eb838af606a4c3c5a2efc4302e49d85a39c2fd44/.claude/skills/jsonpath-and-jsonb -a claude-code -y
导入个人环境~/.claude/skills/jsonpath-and-jsonb
npx skills add https://github.com/matejformanek/postgres-claude/tree/eb838af606a4c3c5a2efc4302e49d85a39c2fd44/.claude/skills/jsonpath-and-jsonb -a claude-code -g -y
手动放置目录.claude/skills/jsonpath-and-jsonbOfficial docs ↗
导入当前项目.agents/skills/jsonpath-and-jsonb
npx skills add https://github.com/matejformanek/postgres-claude/tree/eb838af606a4c3c5a2efc4302e49d85a39c2fd44/.claude/skills/jsonpath-and-jsonb -a github-copilot -y
导入个人环境~/.copilot/skills/jsonpath-and-jsonb
npx skills add https://github.com/matejformanek/postgres-claude/tree/eb838af606a4c3c5a2efc4302e49d85a39c2fd44/.claude/skills/jsonpath-and-jsonb -a github-copilot -g -y
手动放置目录.agents/skills/jsonpath-and-jsonbOfficial docs ↗
导入当前项目.agents/skills/jsonpath-and-jsonb
npx skills add https://github.com/matejformanek/postgres-claude/tree/eb838af606a4c3c5a2efc4302e49d85a39c2fd44/.claude/skills/jsonpath-and-jsonb -a cursor -y
导入个人环境~/.cursor/skills/jsonpath-and-jsonb
npx skills add https://github.com/matejformanek/postgres-claude/tree/eb838af606a4c3c5a2efc4302e49d85a39c2fd44/.claude/skills/jsonpath-and-jsonb -a cursor -g -y
手动放置目录.agents/skills/jsonpath-and-jsonbOfficial docs ↗
导入当前项目.agents/skills/jsonpath-and-jsonb
npx skills add https://github.com/matejformanek/postgres-claude/tree/eb838af606a4c3c5a2efc4302e49d85a39c2fd44/.claude/skills/jsonpath-and-jsonb -a gemini-cli -y
导入个人环境~/.gemini/skills/jsonpath-and-jsonb
npx skills add https://github.com/matejformanek/postgres-claude/tree/eb838af606a4c3c5a2efc4302e49d85a39c2fd44/.claude/skills/jsonpath-and-jsonb -a gemini-cli -g -y
Native Gemini CLIgemini skills install https://github.com/matejformanek/postgres-claude.git --scope workspace --path .claude/skills/jsonpath-and-jsonb
手动放置目录.agents/skills/jsonpath-and-jsonbOfficial docs ↗
⚠ 安装命令使用开源 skills CLI。执行前请检查来源、脚本和权限。
# jsonpath-and-jsonb — SQL/JSON path language and binary JSON

PG has TWO JSON types:

- **`json`** — text storage. Preserves formatting/whitespace/key-order. Slow to query (parsed every access).
- **`jsonb`** — binary storage. Deduped/sorted keys, faster to query, indexable via GIN. Almost always what you want.

Plus **`jsonpath`** — a query language type (SQL:2016). Compiled path expressions used by `jsonb_path_query`, `jsonb_path_exists`, `@@` operator, `JSON_TABLE`.

## The file map

| File | KB | Role |
|---|---:|---|
| `utils/adt/jsonb.c` | 47 | Public `jsonb` operators + I/O — `jsonb_in`, `jsonb_out`, `jsonb_object`, `->`, `->>`, key existence `?`, containment `@>`. |
| `utils/adt/jsonb_util.c` | 58 | Internal serialization + traversal. `JsonbIterator`, `findJsonbValueFromContainer`, packing helpers. Contains ordering/deduplication rules. |
| `utils/adt/jsonb_gin.c` | 35 | GIN opclass for jsonb — extract-value / extract-query / consistent functions. Multiple index shapes: default, path_ops. |
| `utils/adt/jsonb_op.c` | 7 | Small operator wrappers. |
| `utils/adt/jsonbsubs.c` | 12 | Array/object subscripting — `jsonb['key']` (PG 14+). |
| `utils/adt/jsonpath.c` | 38 | jsonpath type support — parser (via `jsonpath_scan.l` + `jsonpath_gram.y`), I/O, tree walks. |
| `utils/adt/jsonpath_exec.c` | **127** | jsonpath EXECUTOR. `executeItem`, `executePredicate`, `executeBinaryArithmExpr` — traverses the jsonpath tree against a jsonb value. **This is where the leak fix landed** — see `planning/jsonpath_leak/`. |
| `include/utils/jsonb.h`, `jsonapi.h`, `jsonpath.h` | — | Public APIs. |

## jsonb storage layout

A jsonb is a varlena with:

- **Header** — 4 bytes VARSIZE_ANY + 4 bytes JEntry-array-count for the root container.
- **JEntry array** — one per top-level element, encoding type + length/offset.
- **Serialized values** — the raw bytes for scalars, or nested containers with their own JEntry arrays.
- **Recursive** — a jsonb OBJECT's value is a nested container repeating the pattern.

Keys are sorted lexically (bytewise UTF-8) and deduped (last-wins semantics). This is why:
- `'{"a": 1, "a": 2}'::jsonb` returns `{"a": 2}`.
- `->` on a large object is O(log n) via binary search on sorted keys.

## jsonpath language basics

Path expressions like `$.employees[*] ? (@.name == "Alice")` mean:

- `$` — the root (the jsonb value passed in).
- `.name` — field access.
- `[*]` — all array elements.
- `? (predicate)` — filter — keep only elements where predicate is true.
- `@` — the current element in a filter context.

Two modes:

- **lax** (default) — missing paths return "no result" instead of erroring. Auto-unwraps arrays. Auto-wraps scalars. Forgiving.
- **strict** — errors on missing paths / type mismatches. Strict RFC compliance.

Set at query time: `SELECT jsonb_path_query(v, 'strict $.a')`.

## The executor's key structures

From `jsonpath_exec.c`:

- **`JsonPathExecContext`** — walks state: current position, mode, variables map.
- **`JsonValueList`** — accumulates result values. **This is the struct Tom Lane's `5a2043bf713` redesigned inline** to eliminate the executePredicate memory leak. See `planning/jsonpath_leak/comparison.md` for the pattern comparison.
- **`JsonPathItem`** — one node in the compiled path tree.
- **`JsonbValue`** — the value being examined.

The executor does DFS over the jsonpath tree, executing predicates, applying arithmetic, accumulating results into a `JsonValueList`.

## GIN index on jsonb

Two operator classes:

- **`jsonb_ops`** (default) — index each unique key/value combo. Supports `@>`, `?`, `?&`, `?|`. Bigger index; more query flexibility.
- **`jsonb_path_ops`** — hash each path-to-value. Only supports `@>`. Smaller; faster; strict subset.

Choose `path_ops` for pure containment queries (JSON-Blob search). Choose default for key-existence + containment.

## Common patch shapes

### Add a new jsonpath function

- Extend `jsonpath_gram.y` for the syntax.
- Add case in `executeItem` (jsonpath_exec.c) — implement the semantics.
- If the function returns multiple values, decide lax vs strict behavior.
- Regress in `src/test/regress/sql/jsonpath.sql`.

### Add a new jsonb operator

- Add C function in `jsonb.c` or `jsonb_op.c`.
- pg_operator.dat entry (with commutator / negator / restrict / join fns).
- Consider GIN opclass support — a new operator without index support is a footgun.

### Optimize a jsonb query

- EXPLAIN — is the GIN index being used?
- If not: `path_ops` may not support your operator; switch to default. Or your predicate has too much post-index work.
- If yes but slow: consider a **partial index** on the specific path shape.
- Container-emit vs path-emit — the executor may be materializing large intermediate results. Consider strict mode + explicit path.

### Fix a jsonpath memory leak

Been done. See `planning/jsonpath_leak/`:
- The blind-trilogy calibration reproduced Tom Lane's leak fix (5a2043bf713) with a different mechanism but identical RSS envelope (32 MB vs 32 MB).
- The bug pattern: `JsonValueList` accumulated results without per-call context cleanup. Fixes: struct redesign (Tom's) OR per-call AllocSetContext wrap (calibration's approach). Both work.
- Lesson: for iterator-style accumulator structs, ownership of the elements matters as much as ownership of the containers.

## Pitfalls

- **`json` vs `jsonb` type — pick jsonb** — unless you have a specific reason (preserving formatting, key order), always jsonb. json is a "text with syntax check", not a queryable structure.
- **Key deduplication in jsonb** — `'{"a":1,"a":2}'::jsonb` silently keeps the last. Applications that generate jsonb should not rely on duplicate keys.
- **jsonpath variables need explicit passing** — `jsonb_path_query(v, '$var', '{"var": 5}'::jsonb)`. Beginners often try to embed variables inline.
- **`->` returns jsonb; `->>` returns text** — using `->` when you want a text scalar leaves you with a jsonb-encoded scalar (e.g. `"hello"` with quotes).
- **`@?` vs `@@`** — `@?` returns bool (path exists); `@@` returns bool (path returns truthy). Common mixup.
- **`jsonb_path_ops` is more restrictive than you think** — no `?` operator, no `->` (that's the wrong tool anyway).
- **Deep jsonpath is quadratic** — a path like `$.a.b.c...z` on a deeply nested jsonb is O(depth × object-size). Consider caching sub-values.
- **jsonpath_exec.c is a memory hotspot** — see `planning/jsonpath_leak/` for a real bug fixed at scale (5.7 GB → 32 MB RSS on the reproducer). New iterator-style code paths should apply the per-call context pattern.
- **JSON_TABLE (PG 17+) is jsonpath at heart** — its columns are jsonpath expressions. Same performance considerations.
- **JIT doesn't help jsonb operations** — jsonpath executor is interpreter-only. JIT compiles ExprState machinery, not the jsonpath tree.

## Related corpus

- **Idioms**: `error-handling` (jsonpath errors in strict mode), `heap-tuple-decompression-pattern` (jsonb is toasted → detoast + traverse).
- **Subsystems**: `utils-mmgr` (jsonpath_exec is a memory hotspot — hence the leak), `parser-and-rewrite` (jsonpath grammar is separate but sits in the same layer).
- **Scenarios**: `add-new-builtin-function` (for adding jsonb / jsonpath fns), `fix-memory-leak` (jsonpath_leak is the canonical calibration).
- **Planning**: `planning/jsonpath_leak/` — 3-phase blind calibration reproducing Tom Lane's fix. Read `comparison.md` for the mechanism comparison (inline storage vs per-call context).

## Corpus-chain shortcut

```
python3 scripts/corpus-chain.py --file src/backend/utils/adt/jsonpath_exec.c
python3 scripts/corpus-chain.py --file src/backend/utils/adt/jsonb.c
python3 scripts/corpus-chain.py --idiom heap-tuple-decompression-pattern
```

## Boundary

**Use this skill** for jsonb + jsonpath + related operators + JSON_TABLE.

**Don't use** for:
- **`json` type (text-based)** — separate `json.c`; use only for compatibility.
- **`hstore`** — contrib module, different data model.
- **`hstore_plpython` / `hstore_plperl`** — different transformers.
- **XML type** — `xml.c` etc., separate subsystem.
- **JSON parsing at the SQL boundary** — that's `jsonapi.c`'s pull-based parser, used by many callers; often you don't need to touch it.