Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 02e07a5

Browse files
author
Nikita Glukhov
committed
Add JSON_OBJECT() transformation
1 parent 4c3c4f4 commit 02e07a5

File tree

10 files changed

+1005
-16
lines changed

10 files changed

+1005
-16
lines changed

src/backend/nodes/copyfuncs.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2121,6 +2121,37 @@ _copyOnConflictExpr(const OnConflictExpr *from)
21212121
return newnode;
21222122
}
21232123

2124+
/*
2125+
* _copyJsonKeyValue
2126+
*/
2127+
static JsonKeyValue *
2128+
_copyJsonKeyValue(const JsonKeyValue *from)
2129+
{
2130+
JsonKeyValue *newnode = makeNode(JsonKeyValue);
2131+
2132+
COPY_NODE_FIELD(key);
2133+
COPY_NODE_FIELD(value);
2134+
2135+
return newnode;
2136+
}
2137+
2138+
/*
2139+
* _copyJsonObjectCtor
2140+
*/
2141+
static JsonObjectCtor *
2142+
_copyJsonObjectCtor(const JsonObjectCtor *from)
2143+
{
2144+
JsonObjectCtor *newnode = makeNode(JsonObjectCtor);
2145+
2146+
COPY_NODE_FIELD(exprs);
2147+
COPY_NODE_FIELD(output);
2148+
COPY_SCALAR_FIELD(absent_on_null);
2149+
COPY_SCALAR_FIELD(unique);
2150+
COPY_LOCATION_FIELD(location);
2151+
2152+
return newnode;
2153+
}
2154+
21242155
/* ****************************************************************
21252156
* relation.h copy functions
21262157
*
@@ -4973,6 +5004,12 @@ copyObjectImpl(const void *from)
49735004
case T_OnConflictExpr:
49745005
retval = _copyOnConflictExpr(from);
49755006
break;
5007+
case T_JsonKeyValue:
5008+
retval = _copyJsonKeyValue(from);
5009+
break;
5010+
case T_JsonObjectCtor:
5011+
retval = _copyJsonObjectCtor(from);
5012+
break;
49765013

49775014
/*
49785015
* RELATION NODES

src/backend/parser/parse_expr.c

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "parser/parse_agg.h"
3636
#include "utils/builtins.h"
3737
#include "utils/date.h"
38+
#include "utils/fmgroids.h"
3839
#include "utils/lsyscache.h"
3940
#include "utils/timestamp.h"
4041
#include "utils/xml.h"
@@ -121,6 +122,7 @@ static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
121122
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
122123
static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
123124
static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
125+
static Node *transformJsonObjectCtor(ParseState *pstate, JsonObjectCtor *ctor);
124126
static Node *make_row_comparison_op(ParseState *pstate, List *opname,
125127
List *largs, List *rargs, int location);
126128
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@@ -369,6 +371,10 @@ transformExprRecurse(ParseState *pstate, Node *expr)
369371
break;
370372
}
371373

374+
case T_JsonObjectCtor:
375+
result = transformJsonObjectCtor(pstate, (JsonObjectCtor *) expr);
376+
break;
377+
372378
default:
373379
/* should not reach here */
374380
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
@@ -3472,3 +3478,247 @@ ParseExprKindName(ParseExprKind exprKind)
34723478
}
34733479
return "unrecognized expression kind";
34743480
}
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

Comments
 (0)