Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1uw89j-006MRH-6S for pgsql-hackers@arkaria.postgresql.org; Tue, 09 Sep 2025 23:53:48 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1uw89i-00Fee8-BD for pgsql-hackers@arkaria.postgresql.org; Tue, 09 Sep 2025 23:53:46 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1uw89h-00Fedz-Vj for pgsql-hackers@lists.postgresql.org; Tue, 09 Sep 2025 23:53:46 +0000 Received: from mail-pl1-x644.google.com ([2607:f8b0:4864:20::644]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1uw89e-001bmJ-26 for pgsql-hackers@postgresql.org; Tue, 09 Sep 2025 23:53:46 +0000 Received: by mail-pl1-x644.google.com with SMTP id d9443c01a7336-24b1622788dso43391345ad.2 for ; Tue, 09 Sep 2025 16:53:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1757462022; x=1758066822; darn=postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=GcNJetn2S94CiUrlEL0pSdF8KS++BO6qmYTh6waT4GE=; b=EU20FRpIVu76lJDN7OD9WW4G4yQ+U1n6esu6e/5EEZSZ9qa/JA48TQv79mJwK/enKN I7RPJcudTykUHYwjtoG8b8Cw7EwP1UghXgsMvFGD3Oq1lo8RXInoVjj2kNqBePK+BpD4 3nEbqgRArbL4MINgoQfwDjeLQ8QDLJ3fCul52KmMru7bAcD5RHp+SQfPAEVlREClO792 zJO5vYrmpIR+KdsaBcyfhK78D/8SfdyE6M0KFeYR91b4II3OC1jcBGbsaSIRDMv2uHYI Yn7dlOi7ekxPAd8Y7A8wEu4pr3NZfoNmxZMXCb74w4t2PDOpYPcQ7Q02FDxUyKnkVYqU tkmg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1757462022; x=1758066822; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=GcNJetn2S94CiUrlEL0pSdF8KS++BO6qmYTh6waT4GE=; b=EWacYl/16V78de0aMwG7bRgPJwADUoTwkNlkiUCVUmpx6kPKKXtkj3AbpaFcBbVu3p bght2Ue8yjd99KgJXQC0UWpSAYI7WG+6xY3TwOB8az4Hzh9GJJ6npPXzIBptKhLBncA8 9Oc7xTTWTniwoF52jjZ7LP0oEZx1PKsCdMo+8oh2AjRqqg9l9GIYH4xM2ZEbmystgqya j2xZOJ+azQu66tqE/BN33jhzdwpMPhMCvwU/ByyP++MVujWm8QTbUfS1JQkTyZhMxS69 OBJT3W3BKAaAAylICbzIo2IABhsZasTibkXmvnsIQzjoKXB+aWbct+mKN+nl08YUq/uF JEZA== X-Forwarded-Encrypted: i=1; AJvYcCX2Ghxql0hOVVW5hISExN2N1XyxEdC7fdjwYByZ9KOy+nAHqaZTQL+bqtKbMWUYtfyAVzd/Rm95qGa+Wga9@postgresql.org X-Gm-Message-State: AOJu0YwmACOlKAKpxu+tS7FkClNvgnjhcEmeJnwyEaCv3V8+1jADp6BC IoWZvjWPCadN3EE+kBVYmvPSU7XGMXj2UeiFeEffgxFUV0Kf7olFofgpMW787MRQ7dxq0egP+YX 4cGYcFTC4vP/rQ/pA75+ZxBohMgEFIww= X-Gm-Gg: ASbGncvyaSb0A8sIp2fAXyKjiK8g57c+1OMxBvxn94TjraMx2yuvmNitUgRTO5H6KUR Cw0QE+iVV0ZecIr6vXDvtWhoXeOHoqCtYOF/aZdhbo0184lxdayPz1ImsW6pRbxRowCe3ONf+6w Luc5O49iLDAcWarUwt1l4i/TnjGaBPKCn/LhMcGxfzmNXRYFlZQJta7vRZ3oZZ+rdpIw94lMrkW WqthPsk X-Google-Smtp-Source: AGHT+IEhvoeQ5RZUzTOZE3JDxzww16iiR8/QEBkRjR76tJHP2NBq/CBgI9AQhptvkGSZKPOfu+xtqwT5WWRW4QAZKKw= X-Received: by 2002:a17:903:1104:b0:250:b622:c750 with SMTP id d9443c01a7336-2516e97ee86mr173710355ad.27.1757462021547; Tue, 09 Sep 2025 16:53:41 -0700 (PDT) MIME-Version: 1.0 References: <21fa36b4-6d56-4556-b524-9e3955b922c8@eisentraut.org> <290418A2-3A9B-4304-AAAD-4A40CFF1CB93@gmail.com> <98C6F445-773B-4C82-8198-7F5CD56D5252@gmail.com> <052E941B-C905-448F-AE3A-753C12AAC938@gmail.com> In-Reply-To: <052E941B-C905-448F-AE3A-753C12AAC938@gmail.com> From: Alexandra Wang Date: Tue, 9 Sep 2025 16:53:04 -0700 X-Gm-Features: Ac12FXyyZ4uQVV3XGvNMCTtrIjv8H8B52qH0Sg3kL1xQdLE4nOwdJbxEces8R_8 Message-ID: Subject: Re: SQL:2023 JSON simplified accessor support To: Chao Li Cc: Nikita Glukhov , jian he , Nikita Malakhov , Vik Fearing , Mark Dilger , Matheus Alcantara , Peter Eisentraut , Andrew Dunstan , PostgreSQL Hackers , "David E. Wheeler" Content-Type: multipart/alternative; boundary="0000000000008029b4063e6703e2" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --0000000000008029b4063e6703e2 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi Chao, On Tue, Sep 2, 2025 at 11:57=E2=80=AFPM Chao Li wr= ote: > On Sep 3, 2025, at 10:20, Alexandra Wang > wrote: > > This change would give an incorrect result for an accessor like > "[0].a" when the jsonb column data is a jsonb object instead of a > jsonb array. I've added two new test cases to cover this scenario: > > In jsonb.sql, the newly added tests are: > select (jb)[0].a from test_jsonb_dot_notation; -- returns same result as > (jb).a > select (jb)[1].a from test_jsonb_dot_notation; -- returns NULL > > > I think the latest patch is still wrong. Ultimately, dot notation =E2=80= =9C.a", > index =E2=80=9C[0] and slice "[1:4]=E2=80=9D rely on jsonpath, and subscr= ipt [=E2=80=9Ca=E2=80=9D] relies > on the rest logic in jsonb_subscript_transform() in =E2=80=9Cforeach=E2= =80=9D. > > Now =E2=80=9Cjsonb_check_jsonpath_needed()=E2=80=9D checks only dot natio= n and slice, but > not checking index case. So that reason why your case =E2=80=9Cselect (jb= )[0].a=E2=80=9D > works is because the second indirection is a dot nation. However, =E2=80= =9Cselect > (jb)[0][=E2=80=98a=E2=80=99]=E2=80=9D will not work. See my test: > > ``` > evantest=3D# select ('{"name": "Alice", "age": 30}'::jsonb)[0].name; > name > --------- > "Alice" > (1 row) > > evantest=3D# select ('{"name": "Alice", "age": 30}'::jsonb)[0]['name']; > jsonb > ------- > > (1 row) > ``` > > In my test, (jsonb)[0][=E2=80=99name=E2=80=99] should also return =E2=80= =9CAlice=E2=80=9D. > > So, end up, =E2=80=9Cjsonb_check_jsonpath_needed()=E2=80=9D only does inc= omplete and > inaccurate checks, only jsonb_subscript_make_jsonpath() can make an > accurate decision and return a null jsonpath upon subscript =E2=80=9C[=E2= =80=98a=E2=80=99]=E2=80=9D. > > As a result, =E2=80=9Cjson_check_jsonpath_needed()=E2=80=9D feels not nee= ded at all. In > jsonb_subscript_transform(), just go ahead to run > jsonb_subscript_make_jsonpath() first, if returned jsonpath is NULL, then > run rest of logic. > > With my dirty change of removing json_check_jsonpath_needed: > ``` > chaol@ChaodeMacBook-Air postgresql % git diff > diff --git a/src/backend/utils/adt/jsonbsubs.c > b/src/backend/utils/adt/jsonbsubs.c > index 374040b3b4e..d9faab5c799 100644 > --- a/src/backend/utils/adt/jsonbsubs.c > +++ b/src/backend/utils/adt/jsonbsubs.c > @@ -416,12 +416,12 @@ jsonb_subscript_transform(SubscriptingRef *sbsref, > sbsref->refrestype =3D JSONBOID; > sbsref->reftypmod =3D -1; > > - if (jsonb_check_jsonpath_needed(*indirection, isSlice)) > - { > + //if (jsonb_check_jsonpath_needed(*indirection, isSlice)) > + //{ > jsonb_subscript_make_jsonpath(pstate, indirection, sbsref= ); > if (sbsref->refjsonbpath) > return; > - } > + //} > > /* > * We reach here only in two cases: (a) the JSON simplified > accessor is > ``` > > You can see: > ``` > evantest=3D# select ('{"name": "Alice", "age": 30}'::jsonb)[0]['name']; > jsonb > --------- > "Alice" > (1 row) > > evantest=3D# select ('{"name": "Alice", "age": 30}'::jsonb)[0].name; > name > --------- > "Alice" > (1 row) > ``` > > Then I found =E2=80=9Cmake check=E2=80=9D failed. For example the first b= roken test case: > > ``` > @@ -4998,7 +4998,7 @@ > select ('123'::jsonb)[0]; > jsonb > ------- > - > + 123 > (1 row) > ``` > > The test expected an empty result, which implies =E2=80=9Cstrict=E2=80=9D= mode. > > But the problem is, which mode should be the default? JSON_QUERY() uses > =E2=80=9Clax=E2=80=9D as default mode. And from Peter Eisentraut=E2=80=99= s blog: > https://peter.eisentraut.org/blog/2023/04/04/sql-2023-is-finished-here-is= -whats-new > > ``` > The semantics of this are defined in terms of JSON_QUERY and JSON_VALUE c= onstructions > (which have been available since SQL:2016), so this really just syntactic > sugar. > ``` > > Also feels like =E2=80=9Clax=E2=80=9D should be the default mode. If that= is true, then my > dirty change of removing =E2=80=9Cjson_check_jsonpath_need()=E2=80=9D wor= ks properly. > > The current logic with this patch sounds strange. Because > =E2=80=9Cjson_check_jsonpath_need()=E2=80=9D iterate through unprocessed = indirections to > decide if jsonpath is needed (lax mode). With this logic: > > 1) if index [0] directly following dot notation, like (data).a[0], it=E2= =80=99s > lax mode > 2) if index [0] directly following subscript [=E2=80=98a=E2=80=99], like = (data)[=E2=80=98a=E2=80=99][0], > it=E2=80=99s strict mode > 3) if index [0] directly following the data column, then if there is a do= t > nation in indirection list, use lax mode, otherwise strict mode. For the > failed test case, as there is no more indirection following [0], so it > expected strict mode. > > I wonder where this behavior is defined? > > With my change, 1) and 2) are the same, for 3), if index [0] directly > following the data column, regardless what indirections are followed, it= =E2=80=99s > by default lax mode. > > So, I think this is a design decision. Maybe I missed something from your > previous design, but I don=E2=80=99t find anything about that from the co= mmit > comments. I feel this would be better aligned with 1) and 2). > Thanks for investigating this, and for the great questions! Here=E2=80=99s a bit of background. The pre-standard jsonb subscripting feature [1] was introduced in Postgres 14. Specifically, support for queries like (jb)[0] and (jb)[0]['a'] was added by this commit: commit 676887a3b0b8e3c0348ac3f82ab0d16e9a24bd43 (HEAD) Author: Alexander Korotkov Date: Sun Jan 31 23:50:40 2021 +0300 Implementation of subscripting for jsonb Without my patch, from version 14 through the current master branch, all supported versions of Postgres return the following results: test=3D# select ('123'::jsonb)[0]; jsonb ------- (1 row) test=3D# select ('{"name": "Alice", "age": 30}'::jsonb)[0]; jsonb ------- (1 row) test=3D# select ('{"name": "Alice", "age": 30}'::jsonb)[0]['age']; jsonb ------- (1 row) The simplified accessor syntax defined in the SQL standard includes dot-notation access as well as integer-based subscripts. It does not include text-based subscripts such as ['a']. Therefore, the only overlap between the pre-standard jsonb subscripting and the standard simplified accessor is non-slicing integer-based subscripts. (Since the pre-standard jsonb subscripting never supported slicing, we don=E2=80= =99t need to consider that case either when comparing the two syntaxes.) When we compare the two syntaxes for non-slicing integer-based subscripts, the only discrepancy is that for a jsonb object jb, (jb)[0] in the pre-standard syntax returns NULL, while (jb)[0] in the standard syntax returns (jb) itself. As long as the integer subscript is non-zero, both syntaxes return the same results. I think this (jb)[0] case is rather trivial. However, since it's been behaving the pre-standard way since PG14, I hesitate to change the existing behavior as part of my patches, and I feel it could be a bikeshedding kind of discussion in a separate thread. The main goal of my patch is to add dot-notation. For now, my intention is for cases like (jb)[0].a to work in the standard way, because if a user chooses dot-notation instead of text-based subscripting, they are likely expecting the standard behavior. Thoughts? [1] https://www.postgresql.org/docs/current/datatype-json.html#JSONB-SUBSCRIPTI= NG --0000000000008029b4063e6703e2 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi Chao,

On Tue, Sep 2, 2025 at = 11:57=E2=80=AFPM Chao Li <li.e= van.chao@gmail.com> wrote:
On Sep 3, 2025, at 1= 0:20, Alexandra Wang <alexandra.wang.oss@gmail.com> wrote:

<= div style=3D"font-family:Helvetica;font-size:14px;font-style:normal;font-va= riant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;te= xt-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-= decoration:none">This change would give an incorrect result for an accessor= like
"[0].a" when the jsonb column data is a jsonb object ins= tead of a
jsonb array.=C2=A0 I've added two new test cases to cover = this scenario:

In js= onb.sql, the newly added tests are:
select (jb)[0].a from test_jsonb_dot_notation; -- returns same r= esult as (jb).a
select (jb)[1].a from test_jsonb_dot_notation; -- returns NULL

I think the latest patch is= still wrong. Ultimately, dot notation =E2=80=9C.a", index =E2=80=9C[0= ] and slice "[1:4]=E2=80=9D rely on jsonpath, and subscript [=E2=80=9C= a=E2=80=9D] relies on the rest logic in jsonb_subscript_transform() in =E2= =80=9Cforeach=E2=80=9D.

Now =E2=80=9Cjsonb_check_j= sonpath_needed()=E2=80=9D checks only dot nation and slice, but not checkin= g index case. So that reason why your case =E2=80=9Cselect (jb)[0].a=E2=80= =9D works is because the second indirection is a dot nation. However, =E2= =80=9Cselect (jb)[0][=E2=80=98a=E2=80=99]=E2=80=9D will not work. See my te= st:

```
evantest=3D# select ('{= "name": "Alice", "age": 30}'::jsonb)[0].n= ame;
=C2=A0 name
---------
=C2=A0"Alice&= quot;
(1 row)

evantest=3D# select ('= {"name": "Alice", "age": 30}'::jsonb)[0][= 'name'];
=C2=A0jsonb
-------

(1 row)
```

In my test, (js= onb)[0][=E2=80=99name=E2=80=99] should also return =E2=80=9CAlice=E2=80=9D.=

So, end up, =E2=80=9Cjsonb_check_jsonpath_needed(= )=E2=80=9D only does incomplete and inaccurate checks, only jsonb_subscript= _make_jsonpath() can make an accurate decision and return a null jsonpath u= pon subscript =E2=80=9C[=E2=80=98a=E2=80=99]=E2=80=9D.

=
As a result, =E2=80=9Cjson_check_jsonpath_needed()=E2=80=9D feels not = needed at all. In jsonb_subscript_transform(), just go ahead to run jsonb_s= ubscript_make_jsonpath() first, if returned jsonpath is NULL, then run rest= of logic.

With my dirty change of removing json_c= heck_jsonpath_needed:
```
chaol@ChaodeMacBook-Air = postgresql % git diff
diff --git a/src/backend/utils/adt/jsonbsub= s.c b/src/backend/utils/adt/jsonbsubs.c
index 374040b3b4e..d9faab= 5c799 100644
--- a/src/backend/utils/adt/jsonbsubs.c
++= + b/src/backend/utils/adt/jsonbsubs.c
@@ -416,12 +416,12 @@ jsonb= _subscript_transform(SubscriptingRef *sbsref,
=C2=A0 =C2=A0 =C2= =A0 =C2=A0 sbsref->refrestype =3D JSONBOID;
=C2=A0 =C2=A0 =C2= =A0 =C2=A0 sbsref->reftypmod =3D -1;

- =C2=A0 = =C2=A0 =C2=A0 if (jsonb_check_jsonpath_needed(*indirection, isSlice))
=
- =C2=A0 =C2=A0 =C2=A0 {
+ =C2=A0 =C2=A0 =C2=A0 //if (jsonb_= check_jsonpath_needed(*indirection, isSlice))
+ =C2=A0 =C2=A0 =C2= =A0 //{
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 j= sonb_subscript_make_jsonpath(pstate, indirection, sbsref);
=C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (sbsref->refjsonbpa= th)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 return;
- =C2=A0 =C2=A0 =C2=A0 }
+ =C2=A0 =C2=A0 =C2=A0 //}

=C2=A0 =C2=A0 =C2=A0= =C2=A0 /*
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* We reach here only= in two cases: (a) the JSON simplified accessor is
```

You can see:
```
evantest=3D#= select ('{"name": "Alice", "age": 30}= 9;::jsonb)[0]['name'];
=C2=A0 jsonb
---------
=C2=A0"Alice"
(1 row)

evantest=3D# select ('{"name": "Alice", "age&= quot;: 30}'::jsonb)[0].name;
=C2=A0 name
---------<= /div>
=C2=A0"Alice"
(1 row)
```

Then I found =E2=80=9Cmake check=E2=80=9D failed. For= example the first broken test case:

```
@@ -4998,7 +4998,7 @@
=C2=A0select ('123'::jsonb)[0= ];
=C2=A0 jsonb
=C2=A0-------
-
+ 1= 23
=C2=A0(1 row)
```

The= test expected an empty result, which implies =E2=80=9Cstrict=E2=80=9D mode= .

But the problem is, which mode should be the def= ault? JSON_QUERY() uses =E2=80=9Clax=E2=80=9D as default mode. And from Pet= er Eisentraut=E2=80=99s blog:=C2=A0h= ttps://peter.eisentraut.org/blog/2023/04/04/sql-2023-is-finished-here-is-wh= ats-new

```
The semantics of this are defined in terms of=C2= =A0JSON_QUERY=C2=A0and=C2=A0JSON_VALUE=C2=A0constructions (= which have been available since SQL:2016), so this really just syntactic su= gar.
```

Also feels like =E2=80= =9Clax=E2=80=9D should be the default mode. If that is true, then my dirty = change of removing =E2=80=9Cjson_check_jsonpath_need()=E2=80=9D works prope= rly.

The current logic with this patch sounds stra= nge. Because =E2=80=9Cjson_check_jsonpath_need()=E2=80=9D iterate through u= nprocessed indirections to decide if jsonpath is needed (lax mode). With th= is logic:

1) if index [0] directly following dot n= otation, like (data).a[0], it=E2=80=99s lax mode
2) if index [0] = directly following subscript [=E2=80=98a=E2=80=99], like (data)[=E2=80=98a= =E2=80=99][0], it=E2=80=99s strict mode
3) if index [0] directly = following the data column, then if there is a dot nation in indirection lis= t, use lax mode, otherwise strict mode. For the failed test case, as there = is no more indirection following [0], so it expected strict mode.

I wonder where this behavior is defined?

With my change, 1) and 2) are the same, for 3), if index [0] directl= y following the data column, regardless what indirections are followed, it= =E2=80=99s by default lax mode.

So, I think this i= s a design decision. Maybe I missed something from your previous design, bu= t I don=E2=80=99t find anything about that from the commit comments. I feel= this would be better aligned with 1) and 2).
=
Thanks for investigating this, and for the great questions!<= br>

Here=E2=80=99s a bit of background. The pre-st= andard jsonb subscripting
feature [1] was introduced in Postgres 14. Spe= cifically, support for
queries like (jb)[0] and (jb)[0]['a'] was= added by this commit:

commit 676887a3b0b8e3c0348ac= 3f82ab0d16e9a24bd43 (HEAD)
Author: Alexander Korotkov <akorotkov@postgresql.org>
Date: =C2= =A0 Sun Jan 31 23:50:40 2021 +0300

=C2=A0 =C2=A0 Implementation of s= ubscripting for jsonb

Without my patc= h, from version 14 through the current master branch,
all supported vers= ions of Postgres return the following results:

test=3D# select ('123'::jsonb)[0];
=C2=A0jsonb
= -------

(1 row)

test=3D# select ('{"name": &quo= t;Alice", "age": 30}'::jsonb)[0];
=C2=A0jsonb
----= ---

(1 row)

test=3D# select ('{"name": "Al= ice", "age": 30}'::jsonb)[0]['age'];
=C2=A0js= onb
-------

(1 row)

The sim= plified accessor syntax defined in the SQL standard includes
dot-notatio= n access as well as integer-based subscripts. It does not
include text-b= ased subscripts such as ['a']. Therefore, the only
overlap betwe= en the pre-standard jsonb subscripting and the standard
simplified acces= sor is non-slicing integer-based subscripts. (Since
the pre-standard jso= nb subscripting never supported slicing, we don=E2=80=99t
need to consid= er that case either when comparing the two syntaxes.)

When we compare the two syntaxes for non-slicing int= eger-based
subscripts, the only discrepancy is that for a jsonb object j= b,
(jb)[0] in the pre-standard syntax returns NULL, while (jb)[0] in the=
standard syntax returns (jb) itself. As long as the integer subscriptis non-zero, both syntaxes return the same results.

I think this (jb)[0] case is rather trivial. However= , since it's been
behaving the pre-standard way since PG14, I hesita= te to change the
existing behavior as part of my patches, and I feel it = could be a
bikeshedding kind of discussion in a separate thread.=C2=A0

The main goal of my patch is to ad= d dot-notation. For now, my
intention is for cases like (jb)[0].a to wor= k in the standard way,
because if a user chooses dot-notation instead of= text-based
subscripting, they are likely expecting the standard behavio= r.

Thoughts?
--0000000000008029b4063e6703e2--