Skip to content

Commit b38735a

Browse files
committed
WIP: Replace -> with a func that supports array unwrap for JSON
1 parent 90d0880 commit b38735a

File tree

9 files changed

+298
-36
lines changed

9 files changed

+298
-36
lines changed

src/backend/parser/parse_expr.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -490,11 +490,16 @@ transformIndirection(ParseState *pstate, A_Indirection *ind)
490490
result_basetypid = (result_typid == JSONOID || result_typid == JSONBOID) ?
491491
result_typid : getBaseType(result_typid);
492492

493-
if (result_basetypid == JSONOID || result_basetypid == JSONBOID)
494-
newresult = ParseJsonSimplifiedAccessorObjectField(pstate,
493+
if (result_basetypid == JSONBOID)
494+
newresult = ParseJsonbSimplifiedAccessorObjectField(pstate,
495495
strVal(n),
496496
result,
497497
location, result_basetypid);
498+
else if (result_basetypid == JSONOID)
499+
newresult = ParseJsonSimplifiedAccessorObjectField(pstate,
500+
strVal(n),
501+
result,
502+
location, result_basetypid);
498503
else
499504
newresult = ParseFuncOrColumn(pstate,
500505
list_make1(n),

src/backend/parser/parse_func.c

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1946,13 +1946,13 @@ ParseJsonSimplifiedAccessorArrayElement(ParseState *pstate, A_Indices *subscript
19461946
}
19471947

19481948
/*
1949-
* ParseJsonSimplifiedAccessorObjectField -
1949+
* ParseJsonbSimplifiedAccessorObjectField -
19501950
* handles function calls with a single argument that is of json type.
19511951
* If the function call is actually a column projection, return a suitably
19521952
* transformed expression tree. If not, return NULL.
19531953
*/
19541954
Node *
1955-
ParseJsonSimplifiedAccessorObjectField(ParseState *pstate, const char *funcname,
1955+
ParseJsonbSimplifiedAccessorObjectField(ParseState *pstate, const char *funcname,
19561956
Node *first_arg, int location, Oid basetypid)
19571957
{
19581958
OpExpr *result;
@@ -1985,6 +1985,40 @@ ParseJsonSimplifiedAccessorObjectField(ParseState *pstate, const char *funcname,
19851985
return (Node *) result;
19861986
}
19871987

1988+
/*
1989+
* ParseJsonSimplifiedAccessorObjectField -
1990+
* handles function calls with a single argument that is of json type.
1991+
* If the function call is actually a column projection, return a suitably
1992+
* transformed expression tree. If not, return NULL.
1993+
*/
1994+
Node *
1995+
ParseJsonSimplifiedAccessorObjectField(ParseState *pstate, const char *funcname,
1996+
Node *first_arg, int location, Oid basetypid)
1997+
{
1998+
FuncExpr *result;
1999+
Node *rexpr;
2000+
2001+
if (basetypid != JSONOID)
2002+
elog(ERROR, "unsupported type OID: %u", basetypid);
2003+
2004+
rexpr = (Node *) makeConst(
2005+
TEXTOID,
2006+
-1,
2007+
InvalidOid,
2008+
-1,
2009+
CStringGetTextDatum(funcname),
2010+
false,
2011+
false);
2012+
result = makeFuncExpr(4099,
2013+
JSONOID,
2014+
list_make2(first_arg, rexpr),
2015+
InvalidOid,
2016+
InvalidOid,
2017+
COERCE_EXPLICIT_CALL);
2018+
2019+
return (Node *) result;
2020+
}
2021+
19882022
/*
19892023
* ParseComplexProjection -
19902024
* handles function calls with a single argument that is of complex type.

src/backend/utils/adt/jsonfuncs.c

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ typedef struct GetState
9696
bool *pathok; /* is path matched to current depth? */
9797
int *array_cur_index; /* current element index at each path
9898
* level */
99+
List *tresult_list; /* for case of array unwrap */
99100
} GetState;
100101

101102
/* state for json_array_length */
@@ -358,7 +359,7 @@ static JsonParseErrorType get_scalar(void *state, char *token, JsonTokenType tok
358359
/* common worker function for json getter functions */
359360
static Datum get_path_all(FunctionCallInfo fcinfo, bool as_text);
360361
static text *get_worker(text *json, char **tpath, int *ipath, int npath,
361-
bool normalize_results);
362+
bool normalize_results, bool unwrap);
362363
static Datum get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text);
363364
static text *JsonbValueAsText(JsonbValue *v);
364365

@@ -849,7 +850,23 @@ json_object_field(PG_FUNCTION_ARGS)
849850
char *fnamestr = text_to_cstring(fname);
850851
text *result;
851852

852-
result = get_worker(json, &fnamestr, NULL, 1, false);
853+
result = get_worker(json, &fnamestr, NULL, 1, false, false);
854+
855+
if (result != NULL)
856+
PG_RETURN_TEXT_P(result);
857+
else
858+
PG_RETURN_NULL();
859+
}
860+
861+
Datum
862+
json_object_field_unwrap(PG_FUNCTION_ARGS)
863+
{
864+
text *json = PG_GETARG_TEXT_PP(0);
865+
text *fname = PG_GETARG_TEXT_PP(1);
866+
char *fnamestr = text_to_cstring(fname);
867+
text *result;
868+
869+
result = get_worker(json, &fnamestr, NULL, 1, false, true);
853870

854871
if (result != NULL)
855872
PG_RETURN_TEXT_P(result);
@@ -887,7 +904,7 @@ json_object_field_text(PG_FUNCTION_ARGS)
887904
char *fnamestr = text_to_cstring(fname);
888905
text *result;
889906

890-
result = get_worker(json, &fnamestr, NULL, 1, true);
907+
result = get_worker(json, &fnamestr, NULL, 1, true, false);
891908

892909
if (result != NULL)
893910
PG_RETURN_TEXT_P(result);
@@ -924,7 +941,7 @@ json_array_element(PG_FUNCTION_ARGS)
924941
int element = PG_GETARG_INT32(1);
925942
text *result;
926943

927-
result = get_worker(json, NULL, &element, 1, false);
944+
result = get_worker(json, NULL, &element, 1, false, false);
928945

929946
if (result != NULL)
930947
PG_RETURN_TEXT_P(result);
@@ -967,7 +984,7 @@ json_array_element_text(PG_FUNCTION_ARGS)
967984
int element = PG_GETARG_INT32(1);
968985
text *result;
969986

970-
result = get_worker(json, NULL, &element, 1, true);
987+
result = get_worker(json, NULL, &element, 1, true, false);
971988

972989
if (result != NULL)
973990
PG_RETURN_TEXT_P(result);
@@ -1073,7 +1090,7 @@ get_path_all(FunctionCallInfo fcinfo, bool as_text)
10731090
ipath[i] = INT_MIN;
10741091
}
10751092

1076-
result = get_worker(json, tpath, ipath, npath, as_text);
1093+
result = get_worker(json, tpath, ipath, npath, as_text, false);
10771094

10781095
if (result != NULL)
10791096
PG_RETURN_TEXT_P(result);
@@ -1103,14 +1120,16 @@ get_worker(text *json,
11031120
char **tpath,
11041121
int *ipath,
11051122
int npath,
1106-
bool normalize_results)
1123+
bool normalize_results,
1124+
bool unwrap)
11071125
{
11081126
JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
11091127
GetState *state = palloc0(sizeof(GetState));
11101128

11111129
Assert(npath >= 0);
11121130

11131131
state->lex = makeJsonLexContext(NULL, json, true);
1132+
state->lex->unwrap = unwrap;
11141133

11151134
/* is it "_as_text" variant? */
11161135
state->normalize_results = normalize_results;
@@ -1152,6 +1171,39 @@ get_worker(text *json,
11521171
pg_parse_json_or_ereport(state->lex, sem);
11531172
freeJsonLexContext(state->lex);
11541173

1174+
/*
1175+
* For dot notation:
1176+
* wrap the list of found values into a single text result.
1177+
*/
1178+
if (unwrap && state->tresult_list != NULL)
1179+
{
1180+
if (list_length(state->tresult_list) == 1)
1181+
return linitial(state->tresult_list);
1182+
else
1183+
{
1184+
StringInfo result = makeStringInfo();
1185+
ListCell *lc;
1186+
bool first = true;
1187+
1188+
appendStringInfoChar(result, '[');
1189+
foreach(lc, state->tresult_list)
1190+
{
1191+
text *tresult = (text *) lfirst(lc);
1192+
1193+
if (!first)
1194+
appendStringInfoString(result, ", ");
1195+
1196+
appendStringInfo(result, "%s", text_to_cstring(tresult));
1197+
first = false;
1198+
}
1199+
appendStringInfoChar(result, ']');
1200+
1201+
text *rst = cstring_to_text(result->data);
1202+
pfree(result->data);
1203+
return rst;
1204+
}
1205+
}
1206+
11551207
return state->tresult;
11561208
}
11571209

@@ -1279,8 +1331,12 @@ get_object_field_end(void *state, char *fname, bool isnull)
12791331
{
12801332
const char *start = _state->result_start;
12811333
int len = _state->lex->prev_token_terminator - start;
1334+
text *tresult = cstring_to_text_with_len(start, len);
12821335

1283-
_state->tresult = cstring_to_text_with_len(start, len);
1336+
if (_state->lex->unwrapped)
1337+
_state->tresult_list = lappend(_state->tresult_list, tresult);
1338+
else
1339+
_state->tresult = tresult;
12841340
}
12851341

12861342
/* this should be unnecessary but let's do it for cleanliness: */

src/common/jsonapi.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -713,16 +713,22 @@ pg_parse_json(JsonLexContext *lex, const JsonSemAction *sem)
713713

714714
tok = lex_peek(lex);
715715

716-
/* parse by recursive descent */
716+
/*
717+
* parse by recursive descent
718+
* array unwrap is only applicable if parsing an array and
719+
* is only applicable to the outermost array.
720+
*/
717721
switch (tok)
718722
{
719723
case JSON_TOKEN_OBJECT_START:
724+
lex->unwrap = false;
720725
result = parse_object(lex, sem);
721726
break;
722727
case JSON_TOKEN_ARRAY_START:
723728
result = parse_array(lex, sem);
724729
break;
725730
default:
731+
lex->unwrap = false;
726732
result = parse_scalar(lex, sem); /* json can be a bare scalar */
727733
}
728734

@@ -1428,6 +1434,7 @@ parse_array(JsonLexContext *lex, const JsonSemAction *sem)
14281434
json_struct_action astart = sem->array_start;
14291435
json_struct_action aend = sem->array_end;
14301436
JsonParseErrorType result;
1437+
bool unwrap_this = false;
14311438

14321439
#ifndef FRONTEND
14331440
check_stack_depth();
@@ -1446,7 +1453,14 @@ parse_array(JsonLexContext *lex, const JsonSemAction *sem)
14461453
* for the array start and restore it before we call the routine for the
14471454
* array end.
14481455
*/
1449-
lex->lex_level++;
1456+
if (!lex->unwrap || lex->unwrapped)
1457+
lex->lex_level++;
1458+
else if (lex->unwrap)
1459+
{
1460+
if (!lex->unwrapped)
1461+
unwrap_this = true;
1462+
lex->unwrapped = true;
1463+
}
14501464

14511465
result = lex_expect(JSON_PARSE_ARRAY_START, lex, JSON_TOKEN_ARRAY_START);
14521466
if (result == JSON_SUCCESS && lex_peek(lex) != JSON_TOKEN_ARRAY_END)
@@ -1468,7 +1482,8 @@ parse_array(JsonLexContext *lex, const JsonSemAction *sem)
14681482
if (result != JSON_SUCCESS)
14691483
return result;
14701484

1471-
lex->lex_level--;
1485+
if (!lex->unwrap || !unwrap_this)
1486+
lex->lex_level--;
14721487

14731488
if (aend != NULL)
14741489
{

src/include/catalog/pg_proc.dat

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9295,6 +9295,10 @@
92959295
{ oid => '3968', descr => 'get the type of a json value',
92969296
proname => 'json_typeof', prorettype => 'text', proargtypes => 'json',
92979297
prosrc => 'json_typeof' },
9298+
{ oid => '4099',
9299+
proname => 'json_object_field_unwrap', prorettype => 'json',
9300+
proargtypes => 'json text', proargnames => '{from_json, field_name}',
9301+
prosrc => 'json_object_field_unwrap' },
92989302

92999303
# uuid
93009304
{ oid => '2952', descr => 'I/O',

src/include/common/jsonapi.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,10 @@ typedef struct JsonLexContext
105105
const char *prev_token_terminator;
106106
bool incremental;
107107
JsonTokenType token_type;
108-
int lex_level;
108+
int lex_level; /* total level including both array and object */
109+
// int lex_array_level; /* level inside arrays only */
110+
bool unwrap;
111+
bool unwrapped;
109112
bits32 flags;
110113
int line_number; /* line number, starting from 1 */
111114
const char *line_start; /* where that line starts within input */

src/include/parser/parse_func.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ typedef enum
3434
extern Node *ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
3535
Node *last_srf, FuncCall *fn, bool proc_call,
3636
int location);
37-
extern Node *ParseJsonSimplifiedAccessorObjectField(ParseState *pstate, const char *funcname,
37+
extern Node *ParseJsonbSimplifiedAccessorObjectField(ParseState *pstate, const char *funcname,
3838
Node *first_arg, int location, Oid basetypid);
39+
extern Node *ParseJsonSimplifiedAccessorObjectField(ParseState *pstate, const char *funcname,
40+
Node *first_arg, int location, Oid basetypid);
3941
extern Node *ParseJsonSimplifiedAccessorArrayElement(ParseState *pstate, A_Indices *subscript,
4042
Node *first_arg, int location);
4143

0 commit comments

Comments
 (0)