|
35 | 35 | #include "parser/parse_agg.h"
|
36 | 36 | #include "utils/builtins.h"
|
37 | 37 | #include "utils/date.h"
|
| 38 | +#include "utils/fmgroids.h" |
38 | 39 | #include "utils/lsyscache.h"
|
39 | 40 | #include "utils/timestamp.h"
|
40 | 41 | #include "utils/xml.h"
|
@@ -121,6 +122,7 @@ static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
|
121 | 122 | static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
|
122 | 123 | static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
|
123 | 124 | static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
|
| 125 | +static Node *transformJsonObjectCtor(ParseState *pstate, JsonObjectCtor *ctor); |
124 | 126 | static Node *make_row_comparison_op(ParseState *pstate, List *opname,
|
125 | 127 | List *largs, List *rargs, int location);
|
126 | 128 | static Node *make_row_distinct_op(ParseState *pstate, List *opname,
|
@@ -369,6 +371,10 @@ transformExprRecurse(ParseState *pstate, Node *expr)
|
369 | 371 | break;
|
370 | 372 | }
|
371 | 373 |
|
| 374 | + case T_JsonObjectCtor: |
| 375 | + result = transformJsonObjectCtor(pstate, (JsonObjectCtor *) expr); |
| 376 | + break; |
| 377 | + |
372 | 378 | default:
|
373 | 379 | /* should not reach here */
|
374 | 380 | elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
@@ -3472,3 +3478,247 @@ ParseExprKindName(ParseExprKind exprKind)
|
3472 | 3478 | }
|
3473 | 3479 | return "unrecognized expression kind";
|
3474 | 3480 | }
|
| 3481 | + |
| 3482 | +static Const * |
| 3483 | +getJsonEncodingConst(JsonFormat *format) |
| 3484 | +{ |
| 3485 | + JsonEncoding encoding; |
| 3486 | + const char *enc; |
| 3487 | + Name encname = palloc(sizeof(NameData)); |
| 3488 | + |
| 3489 | + if (!format || |
| 3490 | + format->type == JS_FORMAT_DEFAULT || |
| 3491 | + format->encoding == JS_ENC_DEFAULT) |
| 3492 | + encoding = JS_ENC_UTF8; |
| 3493 | + else |
| 3494 | + encoding = format->encoding; |
| 3495 | + |
| 3496 | + switch (encoding) |
| 3497 | + { |
| 3498 | + case JS_ENC_UTF16: |
| 3499 | + enc = "UTF16"; |
| 3500 | + break; |
| 3501 | + case JS_ENC_UTF32: |
| 3502 | + enc = "UTF32"; |
| 3503 | + break; |
| 3504 | + case JS_ENC_UTF8: |
| 3505 | + default: |
| 3506 | + enc = "UTF8"; |
| 3507 | + break; |
| 3508 | + } |
| 3509 | + |
| 3510 | + namestrcpy(encname, enc); |
| 3511 | + |
| 3512 | + return makeConst(NAMEOID, -1, InvalidOid, NAMEDATALEN, |
| 3513 | + NameGetDatum(encname), false, false); |
| 3514 | +} |
| 3515 | + |
| 3516 | +static Node * |
| 3517 | +makeJsonByteaToTextConversion(Node *expr, JsonFormat *format, int location) |
| 3518 | +{ |
| 3519 | + Const *encoding = getJsonEncodingConst(format); |
| 3520 | + FuncExpr *fexpr = makeFuncExpr(F_PG_CONVERT_FROM, TEXTOID, |
| 3521 | + list_make2(expr, encoding), |
| 3522 | + InvalidOid, InvalidOid, |
| 3523 | + COERCE_EXPLICIT_CALL); |
| 3524 | + |
| 3525 | + fexpr->location = location; |
| 3526 | + |
| 3527 | + return (Node *) fexpr; |
| 3528 | +} |
| 3529 | + |
| 3530 | +static Node * |
| 3531 | +transformJsonValueExpr(ParseState *pstate, JsonValueExpr *ve, |
| 3532 | + JsonFormatType format) |
| 3533 | +{ |
| 3534 | + Node *expr = transformExprRecurse(pstate, (Node *) ve->expr); |
| 3535 | + |
| 3536 | + if (ve->format.type != JS_FORMAT_DEFAULT) |
| 3537 | + format = ve->format.type; |
| 3538 | + |
| 3539 | + if (format != JS_FORMAT_DEFAULT) |
| 3540 | + { |
| 3541 | + int location = exprLocation(expr); |
| 3542 | + Oid exprtype = exprType(expr); |
| 3543 | + Oid targettype = format == JS_FORMAT_JSONB ? JSONBOID : JSONOID; |
| 3544 | + Node *coerced; |
| 3545 | + |
| 3546 | + /* Convert encoded JSON text from bytea */ |
| 3547 | + if (format == JS_FORMAT_JSON && exprtype == BYTEAOID) |
| 3548 | + { |
| 3549 | + expr = makeJsonByteaToTextConversion(expr, &ve->format, location); |
| 3550 | + exprtype = TEXTOID; |
| 3551 | + } |
| 3552 | + |
| 3553 | + /* Try to coerce to target type */ |
| 3554 | + coerced = coerce_to_target_type(pstate, expr, exprtype, |
| 3555 | + targettype, -1, |
| 3556 | + COERCION_EXPLICIT, |
| 3557 | + COERCE_EXPLICIT_CAST, |
| 3558 | + location); |
| 3559 | + |
| 3560 | + if (coerced) |
| 3561 | + expr = coerced; |
| 3562 | + else |
| 3563 | + { |
| 3564 | + /* If coercion failed, use to_json()/to_jsonb() functions */ |
| 3565 | + Oid funcid = targettype == JSONOID ? F_TO_JSON : F_TO_JSONB; |
| 3566 | + FuncExpr *fexpr = makeFuncExpr(funcid, targettype, |
| 3567 | + list_make1(expr), |
| 3568 | + InvalidOid, InvalidOid, |
| 3569 | + COERCE_EXPLICIT_CALL); |
| 3570 | + |
| 3571 | + fexpr->location = location; |
| 3572 | + |
| 3573 | + expr = (Node *) fexpr; |
| 3574 | + } |
| 3575 | + } |
| 3576 | + |
| 3577 | + return expr; |
| 3578 | +} |
| 3579 | + |
| 3580 | +static void |
| 3581 | +checkJsonOutputFormat(ParseState *pstate, JsonFormat *format, Oid targettype) |
| 3582 | +{ |
| 3583 | + if (format->type == JS_FORMAT_JSON) |
| 3584 | + { |
| 3585 | + if (targettype != BYTEAOID && |
| 3586 | + format->encoding != JS_ENC_DEFAULT) |
| 3587 | + ereport(ERROR, |
| 3588 | + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 3589 | + parser_errposition(pstate, format->location), |
| 3590 | + errmsg("cannot set JSON encoding for non-bytea output types"))); |
| 3591 | +#if 0 |
| 3592 | + JsonEncoding enc = format->encoding != JS_ENC_DEFAULT ? |
| 3593 | + format->encoding : JS_ENC_UTF8; |
| 3594 | + |
| 3595 | + if (enc != JS_ENC_UTF8) |
| 3596 | + ereport(ERROR, |
| 3597 | + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 3598 | + parser_errposition(pstate, format->location), |
| 3599 | + errmsg("unsupported JSON encoding"), |
| 3600 | + errhint("only UTF8 JSON encoding is supported"))); |
| 3601 | +#endif |
| 3602 | + } |
| 3603 | +} |
| 3604 | + |
| 3605 | +static void |
| 3606 | +transformJsonOutput(ParseState *pstate, JsonOutput **poutput) |
| 3607 | +{ |
| 3608 | + JsonOutput *output = *poutput; |
| 3609 | + |
| 3610 | + if (!output) |
| 3611 | + { |
| 3612 | + output = makeNode(JsonOutput); |
| 3613 | + |
| 3614 | + output->format.type = JS_FORMAT_JSON; |
| 3615 | + output->format.encoding = JS_ENC_DEFAULT; |
| 3616 | + output->typename = NULL; |
| 3617 | + output->typid = InvalidOid; |
| 3618 | + output->typmod = -1; |
| 3619 | + |
| 3620 | + *poutput = output; |
| 3621 | + |
| 3622 | + return; |
| 3623 | + } |
| 3624 | + |
| 3625 | + typenameTypeIdAndMod(pstate, output->typename, |
| 3626 | + &output->typid, &output->typmod); |
| 3627 | + |
| 3628 | + if (output->format.type == JS_FORMAT_DEFAULT) |
| 3629 | + output->format.type = output->typid == JSONBOID |
| 3630 | + ? JS_FORMAT_JSONB : JS_FORMAT_JSON; |
| 3631 | + else |
| 3632 | + checkJsonOutputFormat(pstate, &output->format, output->typid); |
| 3633 | +} |
| 3634 | + |
| 3635 | +static Node * |
| 3636 | +coerceJsonFuncExpr(ParseState *pstate, Node *expr, JsonOutput *output) |
| 3637 | +{ |
| 3638 | + Node *res; |
| 3639 | + int location; |
| 3640 | + Oid exprtype = exprType(expr); |
| 3641 | + |
| 3642 | + if (!OidIsValid(output->typid) || output->typid == exprtype) |
| 3643 | + return expr; |
| 3644 | + |
| 3645 | + location = exprLocation(expr); |
| 3646 | + |
| 3647 | + if (location < 0) |
| 3648 | + location = output ? output->typename->location : -1; |
| 3649 | + |
| 3650 | + if (output->format.type == JS_FORMAT_JSON && output->typid == BYTEAOID) |
| 3651 | + { |
| 3652 | + /* encode json text into bytea */ |
| 3653 | + Const *enc = getJsonEncodingConst(&output->format); |
| 3654 | + FuncExpr *fexpr = makeFuncExpr(F_PG_CONVERT_TO, BYTEAOID, |
| 3655 | + list_make2(expr, enc), |
| 3656 | + InvalidOid, InvalidOid, |
| 3657 | + COERCE_EXPLICIT_CALL); |
| 3658 | + fexpr->location = location; |
| 3659 | + |
| 3660 | + return (Node *) fexpr; |
| 3661 | + } |
| 3662 | + |
| 3663 | + res = coerce_to_target_type(pstate, expr, exprtype, |
| 3664 | + output->typid, output->typmod, |
| 3665 | + COERCION_EXPLICIT, |
| 3666 | + COERCE_EXPLICIT_CAST, |
| 3667 | + location); |
| 3668 | + if (!res) |
| 3669 | + ereport(ERROR, |
| 3670 | + (errcode(ERRCODE_CANNOT_COERCE), |
| 3671 | + errmsg("cannot cast type %s to %s", |
| 3672 | + format_type_be(exprtype), |
| 3673 | + format_type_be(output->typid)), |
| 3674 | + parser_coercion_errposition(pstate, location, expr))); |
| 3675 | + |
| 3676 | + return res; |
| 3677 | +} |
| 3678 | + |
| 3679 | +static Node * |
| 3680 | +transformJsonObjectCtor(ParseState *pstate, JsonObjectCtor *ctor) |
| 3681 | +{ |
| 3682 | + FuncExpr *fexpr; |
| 3683 | + List *args = NIL; |
| 3684 | + Oid funcid; |
| 3685 | + Oid funcrettype; |
| 3686 | + |
| 3687 | + if (ctor->exprs) |
| 3688 | + { |
| 3689 | + ListCell *lc; |
| 3690 | + |
| 3691 | + args = lappend(args, makeBoolConst(ctor->absent_on_null, false)); |
| 3692 | + args = lappend(args, makeBoolConst(ctor->unique, false)); |
| 3693 | + |
| 3694 | + foreach(lc, ctor->exprs) |
| 3695 | + { |
| 3696 | + JsonKeyValue *kv = castNode(JsonKeyValue, lfirst(lc)); |
| 3697 | + Node *key = transformExprRecurse(pstate, (Node *) kv->key); |
| 3698 | + Node *val = transformJsonValueExpr(pstate, kv->value, |
| 3699 | + JS_FORMAT_DEFAULT); |
| 3700 | + |
| 3701 | + args = lappend(args, key); |
| 3702 | + args = lappend(args, val); |
| 3703 | + } |
| 3704 | + } |
| 3705 | + |
| 3706 | + transformJsonOutput(pstate, &ctor->output); |
| 3707 | + |
| 3708 | + if (ctor->output->format.type == JS_FORMAT_JSONB) |
| 3709 | + { |
| 3710 | + funcid = args ? F_JSONB_BUILD_OBJECT_EXT : F_JSONB_BUILD_OBJECT_NOARGS; |
| 3711 | + funcrettype = JSONBOID; |
| 3712 | + } |
| 3713 | + else |
| 3714 | + { |
| 3715 | + funcid = args ? F_JSON_BUILD_OBJECT_EXT : F_JSON_BUILD_OBJECT_NOARGS; |
| 3716 | + funcrettype = JSONOID; |
| 3717 | + } |
| 3718 | + |
| 3719 | + fexpr = makeFuncExpr(funcid, funcrettype, args, |
| 3720 | + InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL); |
| 3721 | + fexpr->location = ctor->location; |
| 3722 | + |
| 3723 | + return coerceJsonFuncExpr(pstate, (Node *) fexpr, ctor->output); |
| 3724 | +} |
0 commit comments