#36338: Grouping on JSONField subkey throws tuple index out of range error
-------------------------------------+-------------------------------------
     Reporter:  Marc DEBUREAUX       |                     Type:  Bug
       Status:  new                  |                Component:  Database
                                     |  layer (models, ORM)
      Version:  5.2                  |                 Severity:  Normal
     Keywords:  jsonfield            |             Triage Stage:
                                     |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
 Have this simple model:

 {{{
 class Link(models.Model):
   item = models.ForeignKey("Item", on_delete=models.CASCADE)
   extra = models.JSONField(blank=True, null=True)
 }}}

 And have this simple query:

 {{{
 Link.objects.filter(item_id=1).values("extra__key").annotate(count=Count("id"))
 }}}

 This throws the following stacktrace error since 5.2 release:


 {{{
 ---------------------------------------------------------------------------
 IndexError                                Traceback (most recent call
 last)
 File ~/python3.12/site-packages/IPython/core/formatters.py:770, in
 PlainTextFormatter.__call__(self, obj)
     763 stream = StringIO()
     764 printer = pretty.RepresentationPrinter(stream, self.verbose,
     765     self.max_width, self.newline,
     766     max_seq_length=self.max_seq_length,
     767     singleton_pprinters=self.singleton_printers,
     768     type_pprinters=self.type_printers,
     769     deferred_pprinters=self.deferred_printers)
 --> 770 printer.pretty(obj)
     771 printer.flush()
     772 return stream.getvalue()

 File ~/python3.12/site-packages/IPython/lib/pretty.py:411, in
 RepresentationPrinter.pretty(self, obj)
     400                         return meth(obj, self, cycle)
     401                 if (
     402                     cls is not object
     403                     # check if cls defines __repr__
    (...)    409                     and callable(_safe_getattr(cls,
 "__repr__", None))
     410                 ):
 --> 411                     return _repr_pprint(obj, self, cycle)
     413     return _default_pprint(obj, self, cycle)
     414 finally:

 File ~/python3.12/site-packages/IPython/lib/pretty.py:786, in
 _repr_pprint(obj, p, cycle)
     784 """A pprint that just redirects to the normal repr function."""
     785 # Find newlines and replace them with p.break_()
 --> 786 output = repr(obj)
     787 lines = output.splitlines()
     788 with p.group():

 File ~/python3.12/site-packages/django/db/models/query.py:360, in
 QuerySet.__repr__(self)
     359 def __repr__(self):
 --> 360     data = list(self[: REPR_OUTPUT_SIZE + 1])
     361     if len(data) > REPR_OUTPUT_SIZE:
     362         data[-1] = "...(remaining elements truncated)..."

 File ~/python3.12/site-packages/django/db/models/query.py:384, in
 QuerySet.__iter__(self)
     369 def __iter__(self):
     370     """
     371     The queryset iterator protocol uses three nested iterators in
 the
     372     default case:
    (...)    382            - Responsible for turning the rows into model
 objects.
     383     """
 --> 384     self._fetch_all()
     385     return iter(self._result_cache)

 File ~/python3.12/site-packages/django/db/models/query.py:1935, in
 QuerySet._fetch_all(self)
    1933 def _fetch_all(self):
    1934     if self._result_cache is None:
 -> 1935         self._result_cache = list(self._iterable_class(self))
    1936     if self._prefetch_related_lookups and not self._prefetch_done:
    1937         self._prefetch_related_objects()

 File ~/python3.12/site-packages/django/db/models/query.py:216, in
 ValuesIterable.__iter__(self)
     210     names = [
     211         *query.extra_select,
     212         *query.values_select,
     213         *query.annotation_select,
     214     ]
     215 indexes = range(len(names))
 --> 216 for row in compiler.results_iter(
     217     chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size
     218 ):
     219     yield {names[i]: row[i] for i in indexes}

 File ~/python3.12/site-packages/django/db/models/sql/compiler.py:1571, in
 SQLCompiler.results_iter(self, results, tuple_expected, chunked_fetch,
 chunk_size)
    1569 """Return an iterator over the results from executing this
 query."""
    1570 if results is None:
 -> 1571     results = self.execute_sql(
    1572         MULTI, chunked_fetch=chunked_fetch, chunk_size=chunk_size
    1573     )
    1574 fields = [s[0] for s in self.select[0 : self.col_count]]
    1575 converters = self.get_converters(fields)

 File ~/python3.12/site-packages/django/db/models/sql/compiler.py:1609, in
 SQLCompiler.execute_sql(self, result_type, chunked_fetch, chunk_size)
    1607 result_type = result_type or NO_RESULTS
    1608 try:
 -> 1609     sql, params = self.as_sql()
    1610     if not sql:
    1611         raise EmptyResultSet

 File ~/python3.12/site-packages/django/db/models/sql/compiler.py:765, in
 SQLCompiler.as_sql(self, with_limits, with_col_aliases)
     763 try:
     764     combinator = self.query.combinator
 --> 765     extra_select, order_by, group_by = self.pre_sql_setup(
     766         with_col_aliases=with_col_aliases or bool(combinator),
     767     )
     768     for_update_part = None
     769     # Is a LIMIT/OFFSET clause needed?

 File ~/python3.12/site-packages/django/db/models/sql/compiler.py:85, in
 SQLCompiler.pre_sql_setup(self, with_col_aliases)
      79 def pre_sql_setup(self, with_col_aliases=False):
      80     """
      81     Do any necessary class setup immediately prior to producing
 SQL. This
      82     is for things that can't necessarily be done in __init__
 because we
      83     might not have all the pieces in place at that time.
      84     """
 ---> 85     self.setup_query(with_col_aliases=with_col_aliases)
      86     order_by = self.get_order_by()
      87     self.where, self.having, self.qualify =
 self.query.where.split_having_qualify(
      88         must_group_by=self.query.group_by is not None
      89     )

 File ~/python3.12/site-packages/django/db/models/sql/compiler.py:74, in
 SQLCompiler.setup_query(self, with_col_aliases)
      72 if all(self.query.alias_refcount[a] == 0 for a in
 self.query.alias_map):
      73     self.query.get_initial_alias()
 ---> 74 self.select, self.klass_info, self.annotation_col_map =
 self.get_select(
      75     with_col_aliases=with_col_aliases,
      76 )
      77 self.col_count = len(self.select)

 File ~/python3.12/site-packages/django/db/models/sql/compiler.py:286, in
 SQLCompiler.get_select(self, with_col_aliases)
     284 # Reference to a column.
     285 elif isinstance(expression, int):
 --> 286     expression = cols[expression]
     287 # ColPairs cannot be aliased.
     288 if isinstance(expression, ColPairs):
 }}}
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36338>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/010701965020d855-2532bf00-e6e8-476c-bc70-fccc88d2eec3-000000%40eu-central-1.amazonses.com.

Reply via email to