looksJSON reports whether a node is syntactically JSON: a cast to json/jsonb, a JSON-returning json* function call (including the json_extract calls this transform produces from -> / ->>), a JSON-producing conversion function (to_json/to_jsonb/row_to_json/array_to_json), or a `->` A_Expr (which this
(node *pg_query.Node)
| 654 | // `json_extract_string(d,'a') || 'x'` is plain text concat in Postgres and |
| 655 | // must stay that way (likewise `->>`, which yields text, does not count). |
| 656 | func looksJSON(node *pg_query.Node) bool { |
| 657 | if node == nil { |
| 658 | return false |
| 659 | } |
| 660 | if tc := node.GetTypeCast(); tc != nil && tc.TypeName != nil && len(tc.TypeName.Names) > 0 { |
| 661 | if last := tc.TypeName.Names[len(tc.TypeName.Names)-1].GetString_(); last != nil { |
| 662 | switch strings.ToLower(last.Sval) { |
| 663 | case "json", "jsonb": |
| 664 | return true |
| 665 | } |
| 666 | } |
| 667 | } |
| 668 | if fc := node.GetFuncCall(); fc != nil && len(fc.Funcname) > 0 { |
| 669 | if last := fc.Funcname[len(fc.Funcname)-1].GetString_(); last != nil { |
| 670 | name := strings.ToLower(last.Sval) |
| 671 | if strings.HasPrefix(name, "json") && !jsonFuncReturnsNonJSON(name) { |
| 672 | return true |
| 673 | } |
| 674 | // JSON-producing conversions whose names don't start with "json". |
| 675 | // FunctionTransform (which runs earlier) maps to_jsonb -> to_json, |
| 676 | // but cover the Postgres spellings too for direct/standalone use. |
| 677 | switch name { |
| 678 | case "to_json", "to_jsonb", "row_to_json", "array_to_json": |
| 679 | return true |
| 680 | } |
| 681 | } |
| 682 | } |
| 683 | // A bare `d -> 'a'` operand (no cast, not yet rewritten): the arrow always |
| 684 | // becomes json_extract, which returns JSON. Without this, `(d->'a') || |
| 685 | // (d->'b')` would fall through to DuckDB string concat of two JSON values. |
| 686 | if ae := node.GetAExpr(); ae != nil && ae.Kind == pg_query.A_Expr_Kind_AEXPR_OP && |
| 687 | ae.Lexpr != nil && ae.Rexpr != nil && len(ae.Name) == 1 { |
| 688 | if s := ae.Name[0].GetString_(); s != nil && s.Sval == "->" { |
| 689 | return true |
| 690 | } |
| 691 | } |
| 692 | return false |
| 693 | } |
| 694 | |
| 695 | // jsonFuncReturnsNonJSON reports whether a json*-named function returns a |
| 696 | // non-JSON value (VARCHAR, BIGINT, BOOLEAN, ...), so its result concatenated |
no test coverage detected