@@ -66,6 +66,9 @@ static void each_object_field_end(void *state, char *fname, bool isnull);
66
66
static void each_array_start (void * state );
67
67
static void each_scalar (void * state , char * token , JsonTokenType tokentype );
68
68
69
+ /* common worker for json_each* functions */
70
+ static inline Datum elements_worker (PG_FUNCTION_ARGS , bool as_text );
71
+
69
72
/* semantic action functions for json_array_elements */
70
73
static void elements_object_start (void * state );
71
74
static void elements_array_element_start (void * state , bool isnull );
@@ -165,6 +168,9 @@ typedef struct ElementsState
165
168
TupleDesc ret_tdesc ;
166
169
MemoryContext tmp_cxt ;
167
170
char * result_start ;
171
+ bool normalize_results ;
172
+ bool next_scalar ;
173
+ char * normalized_scalar ;
168
174
} ElementsState ;
169
175
170
176
/* state for get_json_object_as_hash */
@@ -1069,19 +1075,31 @@ each_scalar(void *state, char *token, JsonTokenType tokentype)
1069
1075
}
1070
1076
1071
1077
/*
1072
- * SQL function json_array_elements
1078
+ * SQL functions json_array_elements and json_array_elements_text
1073
1079
*
1074
1080
* get the elements from a json array
1075
1081
*
1076
1082
* a lot of this processing is similar to the json_each* functions
1077
1083
*/
1078
1084
Datum
1079
1085
json_array_elements (PG_FUNCTION_ARGS )
1086
+ {
1087
+ return elements_worker (fcinfo , false);
1088
+ }
1089
+
1090
+ Datum
1091
+ json_array_elements_text (PG_FUNCTION_ARGS )
1092
+ {
1093
+ return elements_worker (fcinfo , true);
1094
+ }
1095
+
1096
+ static inline Datum
1097
+ elements_worker (PG_FUNCTION_ARGS , bool as_text )
1080
1098
{
1081
1099
text * json = PG_GETARG_TEXT_P (0 );
1082
1100
1083
- /* elements doesn't need any escaped strings, so use false here */
1084
- JsonLexContext * lex = makeJsonLexContext (json , false );
1101
+ /* elements only needs escaped strings when as_text */
1102
+ JsonLexContext * lex = makeJsonLexContext (json , as_text );
1085
1103
JsonSemAction * sem ;
1086
1104
ReturnSetInfo * rsi ;
1087
1105
MemoryContext old_cxt ;
@@ -1124,6 +1142,9 @@ json_array_elements(PG_FUNCTION_ARGS)
1124
1142
sem -> array_element_start = elements_array_element_start ;
1125
1143
sem -> array_element_end = elements_array_element_end ;
1126
1144
1145
+ state -> normalize_results = as_text ;
1146
+ state -> next_scalar = false;
1147
+
1127
1148
state -> lex = lex ;
1128
1149
state -> tmp_cxt = AllocSetContextCreate (CurrentMemoryContext ,
1129
1150
"json_array_elements temporary cxt" ,
@@ -1146,7 +1167,17 @@ elements_array_element_start(void *state, bool isnull)
1146
1167
1147
1168
/* save a pointer to where the value starts */
1148
1169
if (_state -> lex -> lex_level == 1 )
1149
- _state -> result_start = _state -> lex -> token_start ;
1170
+ {
1171
+ /*
1172
+ * next_scalar will be reset in the array_element_end handler, and
1173
+ * since we know the value is a scalar there is no danger of it being
1174
+ * on while recursing down the tree.
1175
+ */
1176
+ if (_state -> normalize_results && _state -> lex -> token_type == JSON_TOKEN_STRING )
1177
+ _state -> next_scalar = true;
1178
+ else
1179
+ _state -> result_start = _state -> lex -> token_start ;
1180
+ }
1150
1181
}
1151
1182
1152
1183
static void
@@ -1158,7 +1189,7 @@ elements_array_element_end(void *state, bool isnull)
1158
1189
text * val ;
1159
1190
HeapTuple tuple ;
1160
1191
Datum values [1 ];
1161
- static bool nulls [1 ] = {false};
1192
+ bool nulls [1 ] = {false};
1162
1193
1163
1194
/* skip over nested objects */
1164
1195
if (_state -> lex -> lex_level != 1 )
@@ -1167,10 +1198,23 @@ elements_array_element_end(void *state, bool isnull)
1167
1198
/* use the tmp context so we can clean up after each tuple is done */
1168
1199
old_cxt = MemoryContextSwitchTo (_state -> tmp_cxt );
1169
1200
1170
- len = _state -> lex -> prev_token_terminator - _state -> result_start ;
1171
- val = cstring_to_text_with_len (_state -> result_start , len );
1201
+ if (isnull && _state -> normalize_results )
1202
+ {
1203
+ nulls [0 ] = true;
1204
+ values [0 ] = (Datum ) NULL ;
1205
+ }
1206
+ else if (_state -> next_scalar )
1207
+ {
1208
+ values [0 ] = CStringGetTextDatum (_state -> normalized_scalar );
1209
+ _state -> next_scalar = false;
1210
+ }
1211
+ else
1212
+ {
1213
+ len = _state -> lex -> prev_token_terminator - _state -> result_start ;
1214
+ val = cstring_to_text_with_len (_state -> result_start , len );
1215
+ values [0 ] = PointerGetDatum (val );
1216
+ }
1172
1217
1173
- values [0 ] = PointerGetDatum (val );
1174
1218
1175
1219
tuple = heap_form_tuple (_state -> ret_tdesc , values , nulls );
1176
1220
@@ -1204,10 +1248,9 @@ elements_scalar(void *state, char *token, JsonTokenType tokentype)
1204
1248
(errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
1205
1249
errmsg ("cannot call json_array_elements on a scalar" )));
1206
1250
1207
- /*
1208
- * json_array_elements always returns json, so there's no need to think
1209
- * about de-escaped values here.
1210
- */
1251
+ /* supply de-escaped value if required */
1252
+ if (_state -> next_scalar )
1253
+ _state -> normalized_scalar = token ;
1211
1254
}
1212
1255
1213
1256
/*
0 commit comments