@@ -225,13 +225,13 @@ struct RecordIOData
225
225
ColumnIOData columns [FLEXIBLE_ARRAY_MEMBER ];
226
226
};
227
227
228
- /* per-query cache for populate_recordset */
229
- typedef struct PopulateRecordsetCache
228
+ /* per-query cache for populate_record_worker and populate_recordset_worker */
229
+ typedef struct PopulateRecordCache
230
230
{
231
231
Oid argtype ; /* declared type of the record argument */
232
232
ColumnIOData c ; /* metadata cache for populate_composite() */
233
233
MemoryContext fn_mcxt ; /* where this is stored */
234
- } PopulateRecordsetCache ;
234
+ } PopulateRecordCache ;
235
235
236
236
/* per-call state for populate_recordset */
237
237
typedef struct PopulateRecordsetState
@@ -244,16 +244,9 @@ typedef struct PopulateRecordsetState
244
244
JsonTokenType saved_token_type ;
245
245
Tuplestorestate * tuple_store ;
246
246
HeapTupleHeader rec ;
247
- PopulateRecordsetCache * cache ;
247
+ PopulateRecordCache * cache ;
248
248
} PopulateRecordsetState ;
249
249
250
- /* structure to cache metadata needed for populate_record_worker() */
251
- typedef struct PopulateRecordCache
252
- {
253
- Oid argtype ; /* declared type of the record argument */
254
- ColumnIOData c ; /* metadata cache for populate_composite() */
255
- } PopulateRecordCache ;
256
-
257
250
/* common data for populate_array_json() and populate_array_dim_jsonb() */
258
251
typedef struct PopulateArrayContext
259
252
{
@@ -429,6 +422,12 @@ static Datum populate_record_worker(FunctionCallInfo fcinfo, const char *funcnam
429
422
static HeapTupleHeader populate_record (TupleDesc tupdesc , RecordIOData * * record_p ,
430
423
HeapTupleHeader defaultval , MemoryContext mcxt ,
431
424
JsObject * obj );
425
+ static void get_record_type_from_argument (FunctionCallInfo fcinfo ,
426
+ const char * funcname ,
427
+ PopulateRecordCache * cache );
428
+ static void get_record_type_from_query (FunctionCallInfo fcinfo ,
429
+ const char * funcname ,
430
+ PopulateRecordCache * cache );
432
431
static void JsValueToJsObject (JsValue * jsv , JsObject * jso );
433
432
static Datum populate_composite (CompositeIOData * io , Oid typid ,
434
433
const char * colname , MemoryContext mcxt ,
@@ -3202,6 +3201,70 @@ populate_record(TupleDesc tupdesc,
3202
3201
return res -> t_data ;
3203
3202
}
3204
3203
3204
+ /*
3205
+ * Setup for json{b}_populate_record{set}: result type will be same as first
3206
+ * argument's type --- unless first argument is "null::record", which we can't
3207
+ * extract type info from; we handle that later.
3208
+ */
3209
+ static void
3210
+ get_record_type_from_argument (FunctionCallInfo fcinfo ,
3211
+ const char * funcname ,
3212
+ PopulateRecordCache * cache )
3213
+ {
3214
+ cache -> argtype = get_fn_expr_argtype (fcinfo -> flinfo , 0 );
3215
+ prepare_column_cache (& cache -> c ,
3216
+ cache -> argtype , -1 ,
3217
+ cache -> fn_mcxt , false);
3218
+ if (cache -> c .typcat != TYPECAT_COMPOSITE &&
3219
+ cache -> c .typcat != TYPECAT_COMPOSITE_DOMAIN )
3220
+ ereport (ERROR ,
3221
+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
3222
+ /* translator: %s is a function name, eg json_to_record */
3223
+ errmsg ("first argument of %s must be a row type" ,
3224
+ funcname )));
3225
+ }
3226
+
3227
+ /*
3228
+ * Setup for json{b}_to_record{set}: result type is specified by calling
3229
+ * query. We'll also use this code for json{b}_populate_record{set},
3230
+ * if we discover that the first argument is a null of type RECORD.
3231
+ *
3232
+ * Here it is syntactically impossible to specify the target type
3233
+ * as domain-over-composite.
3234
+ */
3235
+ static void
3236
+ get_record_type_from_query (FunctionCallInfo fcinfo ,
3237
+ const char * funcname ,
3238
+ PopulateRecordCache * cache )
3239
+ {
3240
+ TupleDesc tupdesc ;
3241
+ MemoryContext old_cxt ;
3242
+
3243
+ if (get_call_result_type (fcinfo , NULL , & tupdesc ) != TYPEFUNC_COMPOSITE )
3244
+ ereport (ERROR ,
3245
+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
3246
+ /* translator: %s is a function name, eg json_to_record */
3247
+ errmsg ("could not determine row type for result of %s" ,
3248
+ funcname ),
3249
+ errhint ("Provide a non-null record argument, "
3250
+ "or call the function in the FROM clause "
3251
+ "using a column definition list." )));
3252
+
3253
+ Assert (tupdesc );
3254
+ cache -> argtype = tupdesc -> tdtypeid ;
3255
+
3256
+ /* If we go through this more than once, avoid memory leak */
3257
+ if (cache -> c .io .composite .tupdesc )
3258
+ FreeTupleDesc (cache -> c .io .composite .tupdesc );
3259
+
3260
+ /* Save identified tupdesc */
3261
+ old_cxt = MemoryContextSwitchTo (cache -> fn_mcxt );
3262
+ cache -> c .io .composite .tupdesc = CreateTupleDescCopy (tupdesc );
3263
+ cache -> c .io .composite .base_typid = tupdesc -> tdtypeid ;
3264
+ cache -> c .io .composite .base_typmod = tupdesc -> tdtypmod ;
3265
+ MemoryContextSwitchTo (old_cxt );
3266
+ }
3267
+
3205
3268
/*
3206
3269
* common worker for json{b}_populate_record() and json{b}_to_record()
3207
3270
* is_json and have_record_arg identify the specific function
@@ -3227,63 +3290,24 @@ populate_record_worker(FunctionCallInfo fcinfo, const char *funcname,
3227
3290
{
3228
3291
fcinfo -> flinfo -> fn_extra = cache =
3229
3292
MemoryContextAllocZero (fnmcxt , sizeof (* cache ));
3293
+ cache -> fn_mcxt = fnmcxt ;
3230
3294
3231
3295
if (have_record_arg )
3232
- {
3233
- /*
3234
- * json{b}_populate_record case: result type will be same as first
3235
- * argument's.
3236
- */
3237
- cache -> argtype = get_fn_expr_argtype (fcinfo -> flinfo , 0 );
3238
- prepare_column_cache (& cache -> c ,
3239
- cache -> argtype , -1 ,
3240
- fnmcxt , false);
3241
- if (cache -> c .typcat != TYPECAT_COMPOSITE &&
3242
- cache -> c .typcat != TYPECAT_COMPOSITE_DOMAIN )
3243
- ereport (ERROR ,
3244
- (errcode (ERRCODE_DATATYPE_MISMATCH ),
3245
- errmsg ("first argument of %s must be a row type" ,
3246
- funcname )));
3247
- }
3296
+ get_record_type_from_argument (fcinfo , funcname , cache );
3248
3297
else
3249
- {
3250
- /*
3251
- * json{b}_to_record case: result type is specified by calling
3252
- * query. Here it is syntactically impossible to specify the
3253
- * target type as domain-over-composite.
3254
- */
3255
- TupleDesc tupdesc ;
3256
- MemoryContext old_cxt ;
3257
-
3258
- if (get_call_result_type (fcinfo , NULL , & tupdesc ) != TYPEFUNC_COMPOSITE )
3259
- ereport (ERROR ,
3260
- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
3261
- errmsg ("function returning record called in context "
3262
- "that cannot accept type record" ),
3263
- errhint ("Try calling the function in the FROM clause "
3264
- "using a column definition list." )));
3265
-
3266
- Assert (tupdesc );
3267
- cache -> argtype = tupdesc -> tdtypeid ;
3268
-
3269
- /* Save identified tupdesc */
3270
- old_cxt = MemoryContextSwitchTo (fnmcxt );
3271
- cache -> c .io .composite .tupdesc = CreateTupleDescCopy (tupdesc );
3272
- cache -> c .io .composite .base_typid = tupdesc -> tdtypeid ;
3273
- cache -> c .io .composite .base_typmod = tupdesc -> tdtypmod ;
3274
- MemoryContextSwitchTo (old_cxt );
3275
- }
3298
+ get_record_type_from_query (fcinfo , funcname , cache );
3276
3299
}
3277
3300
3278
3301
/* Collect record arg if we have one */
3279
- if (have_record_arg && !PG_ARGISNULL (0 ))
3302
+ if (!have_record_arg )
3303
+ rec = NULL ; /* it's json{b}_to_record() */
3304
+ else if (!PG_ARGISNULL (0 ))
3280
3305
{
3281
3306
rec = PG_GETARG_HEAPTUPLEHEADER (0 );
3282
3307
3283
3308
/*
3284
3309
* When declared arg type is RECORD, identify actual record type from
3285
- * the tuple itself. Note the lookup_rowtype_tupdesc call in
3286
- * update_cached_tupdesc will fail if we're unable to do this.
3310
+ * the tuple itself.
3287
3311
*/
3288
3312
if (cache -> argtype == RECORDOID )
3289
3313
{
@@ -3292,8 +3316,21 @@ populate_record_worker(FunctionCallInfo fcinfo, const char *funcname,
3292
3316
}
3293
3317
}
3294
3318
else
3319
+ {
3295
3320
rec = NULL ;
3296
3321
3322
+ /*
3323
+ * When declared arg type is RECORD, identify actual record type from
3324
+ * calling query, or fail if we can't.
3325
+ */
3326
+ if (cache -> argtype == RECORDOID )
3327
+ {
3328
+ get_record_type_from_query (fcinfo , funcname , cache );
3329
+ /* This can't change argtype, which is important for next time */
3330
+ Assert (cache -> argtype == RECORDOID );
3331
+ }
3332
+ }
3333
+
3297
3334
/* If no JSON argument, just return the record (if any) unchanged */
3298
3335
if (PG_ARGISNULL (json_arg_num ))
3299
3336
{
@@ -3517,7 +3554,7 @@ json_to_recordset(PG_FUNCTION_ARGS)
3517
3554
static void
3518
3555
populate_recordset_record (PopulateRecordsetState * state , JsObject * obj )
3519
3556
{
3520
- PopulateRecordsetCache * cache = state -> cache ;
3557
+ PopulateRecordCache * cache = state -> cache ;
3521
3558
HeapTupleHeader tuphead ;
3522
3559
HeapTupleData tuple ;
3523
3560
@@ -3559,7 +3596,7 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
3559
3596
ReturnSetInfo * rsi ;
3560
3597
MemoryContext old_cxt ;
3561
3598
HeapTupleHeader rec ;
3562
- PopulateRecordsetCache * cache = fcinfo -> flinfo -> fn_extra ;
3599
+ PopulateRecordCache * cache = fcinfo -> flinfo -> fn_extra ;
3563
3600
PopulateRecordsetState * state ;
3564
3601
3565
3602
rsi = (ReturnSetInfo * ) fcinfo -> resultinfo ;
@@ -3585,60 +3622,21 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
3585
3622
cache -> fn_mcxt = fcinfo -> flinfo -> fn_mcxt ;
3586
3623
3587
3624
if (have_record_arg )
3588
- {
3589
- /*
3590
- * json{b}_populate_recordset case: result type will be same as
3591
- * first argument's.
3592
- */
3593
- cache -> argtype = get_fn_expr_argtype (fcinfo -> flinfo , 0 );
3594
- prepare_column_cache (& cache -> c ,
3595
- cache -> argtype , -1 ,
3596
- cache -> fn_mcxt , false);
3597
- if (cache -> c .typcat != TYPECAT_COMPOSITE &&
3598
- cache -> c .typcat != TYPECAT_COMPOSITE_DOMAIN )
3599
- ereport (ERROR ,
3600
- (errcode (ERRCODE_DATATYPE_MISMATCH ),
3601
- errmsg ("first argument of %s must be a row type" ,
3602
- funcname )));
3603
- }
3625
+ get_record_type_from_argument (fcinfo , funcname , cache );
3604
3626
else
3605
- {
3606
- /*
3607
- * json{b}_to_recordset case: result type is specified by calling
3608
- * query. Here it is syntactically impossible to specify the
3609
- * target type as domain-over-composite.
3610
- */
3611
- TupleDesc tupdesc ;
3612
-
3613
- if (get_call_result_type (fcinfo , NULL , & tupdesc ) != TYPEFUNC_COMPOSITE )
3614
- ereport (ERROR ,
3615
- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
3616
- errmsg ("function returning record called in context "
3617
- "that cannot accept type record" ),
3618
- errhint ("Try calling the function in the FROM clause "
3619
- "using a column definition list." )));
3620
-
3621
- Assert (tupdesc );
3622
- cache -> argtype = tupdesc -> tdtypeid ;
3623
-
3624
- /* Save identified tupdesc */
3625
- old_cxt = MemoryContextSwitchTo (cache -> fn_mcxt );
3626
- cache -> c .io .composite .tupdesc = CreateTupleDescCopy (tupdesc );
3627
- cache -> c .io .composite .base_typid = tupdesc -> tdtypeid ;
3628
- cache -> c .io .composite .base_typmod = tupdesc -> tdtypmod ;
3629
- MemoryContextSwitchTo (old_cxt );
3630
- }
3627
+ get_record_type_from_query (fcinfo , funcname , cache );
3631
3628
}
3632
3629
3633
3630
/* Collect record arg if we have one */
3634
- if (have_record_arg && !PG_ARGISNULL (0 ))
3631
+ if (!have_record_arg )
3632
+ rec = NULL ; /* it's json{b}_to_recordset() */
3633
+ else if (!PG_ARGISNULL (0 ))
3635
3634
{
3636
3635
rec = PG_GETARG_HEAPTUPLEHEADER (0 );
3637
3636
3638
3637
/*
3639
3638
* When declared arg type is RECORD, identify actual record type from
3640
- * the tuple itself. Note the lookup_rowtype_tupdesc call in
3641
- * update_cached_tupdesc will fail if we're unable to do this.
3639
+ * the tuple itself.
3642
3640
*/
3643
3641
if (cache -> argtype == RECORDOID )
3644
3642
{
@@ -3647,8 +3645,21 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
3647
3645
}
3648
3646
}
3649
3647
else
3648
+ {
3650
3649
rec = NULL ;
3651
3650
3651
+ /*
3652
+ * When declared arg type is RECORD, identify actual record type from
3653
+ * calling query, or fail if we can't.
3654
+ */
3655
+ if (cache -> argtype == RECORDOID )
3656
+ {
3657
+ get_record_type_from_query (fcinfo , funcname , cache );
3658
+ /* This can't change argtype, which is important for next time */
3659
+ Assert (cache -> argtype == RECORDOID );
3660
+ }
3661
+ }
3662
+
3652
3663
/* if the json is null send back an empty set */
3653
3664
if (PG_ARGISNULL (json_arg_num ))
3654
3665
PG_RETURN_NULL ();
0 commit comments