Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

release-20.2: opt: fix FoldJSONAccessIntoValues corner case #60808

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions pkg/sql/opt/norm/project_funcs.go
Expand Up @@ -368,6 +368,11 @@ func (c *CustomFuncs) CanUnnestJSONFromValues(
return false
}
currJSON := expr.(*memo.ConstExpr).Value.(*tree.DJSON)
if currJSON.Type() != json.ObjectJSONType {
// This value is not an object. It is important to check, because a JSON
// array can pass the checks below (see #60522).
return false
}
iter, err := firstJSON.ObjectIter()
if err != nil {
return false
Expand Down
17 changes: 17 additions & 0 deletions pkg/sql/opt/norm/testdata/rules/project
Expand Up @@ -1478,3 +1478,20 @@ project
│ └── ('{"y": "three"}',)
└── projections
└── column1:1::JSONB->'x' [as=x:2, outer=(1), immutable]

# Verify that the rule doesn't fire because of an array that has the right key
# as an element (#60522).
norm expect-not=FoldJSONAccessIntoValues
SELECT j->'foo' FROM (VALUES ('{"foo": "bar"}'::JSONB), ('["foo", "baz"]'::JSONB)) AS v(j)
----
project
├── columns: "?column?":2
├── cardinality: [2 - 2]
├── immutable
├── values
│ ├── columns: column1:1!null
│ ├── cardinality: [2 - 2]
│ ├── ('{"foo": "bar"}',)
│ └── ('["foo", "baz"]',)
└── projections
└── column1:1->'foo' [as="?column?":2, outer=(1), immutable]
6 changes: 5 additions & 1 deletion pkg/util/json/json.go
Expand Up @@ -117,7 +117,11 @@ type JSON interface {
// AsText returns the JSON document as a string, with quotes around strings removed, and null as nil.
AsText() (*string, error)

// Exists implements the `?` operator.
// Exists implements the `?` operator: does the string exist as a top-level
// key within the JSON value?
//
// If the object is a JSON array, returns true when the key is a top-level
// element of the array.
Exists(string) (bool, error)

// StripNulls returns the JSON document with all object fields that have null values omitted
Expand Down