FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

JSON path equality against a non-string value: silently empty on MySQL, raises on PostgreSQL · Issue #1564 · datajoint/datajoint-python · GitHub

Repository navigation

JSON path equality against a non-string value: silently empty on MySQL, raises on PostgreSQL #1564

Open

Description

Table & {"json_attr.field": value} only behaves correctly when value is a string. A Python bool returns the wrong answer on MySQL and raises on PostgreSQL; a Python int raises on PostgreSQL.

Sibling of #1563 — same root (JSON extraction yields text), different side: that one is the :type annotation on projection, this one is the Python value on restriction.

Repro

@schema
class E(dj.Manual):
    definition = """
    name : varchar(32)
    ---
    s : json
    """
E.insert([
    {"name": "a", "s": {"vendor": "Acme",  "ch": 64, "cal": True}},
    {"name": "b", "s": {"vendor": "Other", "ch": 16, "cal": False}},
])

Each row should select ['a']:

restriction MySQL 8.0 postgres:15
& {"s.vendor": "Acme"} ['a'] ['a']
& {"s.ch": 64} ['a'] UndefinedFunction: operator does not exist: text = integer
& {"s.ch": "64"} ['a'] ['a']
& {"s.cal": True} [] UndefinedFunction: operator does not exist: text = boolean
& {"s.cal": "true"} ['a'] ['a']

The MySQL boolean row is the serious one: no error, no rows, and the natural reading of an empty result is "nothing is calibrated."

Cause

adapter.json_path_expr yields json_value(...) / jsonb_extract_path_text(...), both of which return text. prep_value (condition.py:336) then renders the Python value by its own type, so the comparison becomes text = <non-text>:

  • PostgreSQL refuses the comparison outright.
  • MySQL coerces for numerics, which is why 64 happens to work, but compares the extracted true against 1 for a bool and matches nothing.

The asymmetry is invisible to a user: the same expression is correct, wrong, or an error depending on the value's Python type and the backend.

Suggested fix

On the JSON-path branch of prep_value, render the comparison value as text — or cast the extraction to the value's type — so that True, 64 and "Acme" all behave the same way on both backends.

Whichever way, a bool must not silently match nothing. If a given comparison cannot be made portable, raising is acceptable; returning the wrong rows is not.

Documentation

This is almost certainly why tutorials/advanced/json-type.ipynb teaches "Filtering on JSON Content — fetch then filter in Python" and lists "Filter in Python" as an inherent property of JSON in its Design Guidelines. Server-side filtering does work, with the value passed as a string; the tutorial's advice reads as a limitation of the type rather than of this behavior. Worth revisiting together.

Activity

  1. added
    bugIndicates an unexpected problem or unintended behavior
    on Oct 1, 2026
  2. added this to the v2.4 milestone on Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIndicates an unexpected problem or unintended behavior

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL