@@ -396,6 +396,53 @@ byteasend(PG_FUNCTION_ARGS)
396396PG_RETURN_BYTEA_P (vlena );
397397}
398398
399+ Datum
400+ bytea_agg_transfn (PG_FUNCTION_ARGS )
401+ {
402+ StringInfo state ;
403+
404+ state = PG_ARGISNULL (0 ) ?NULL : (StringInfo )PG_GETARG_POINTER (0 );
405+
406+ /* Append the value unless null. */
407+ if (!PG_ARGISNULL (1 ))
408+ {
409+ bytea * value = PG_GETARG_BYTEA_PP (1 );
410+
411+ if (state == NULL )
412+ state = makeStringAggState (fcinfo );
413+
414+ appendBinaryStringInfo (state ,VARDATA_ANY (value ),VARSIZE_ANY_EXHDR (value ));
415+ }
416+
417+ /*
418+ * The transition type for bytea_agg() is declared to be "internal",
419+ * which is a pass-by-value type the same size as a pointer.
420+ */
421+ PG_RETURN_POINTER (state );
422+ }
423+
424+ Datum
425+ bytea_agg_finalfn (PG_FUNCTION_ARGS )
426+ {
427+ StringInfo state ;
428+
429+ /* cannot be called directly because of internal-type argument */
430+ Assert (AggCheckCallContext (fcinfo ,NULL ));
431+
432+ state = PG_ARGISNULL (0 ) ?NULL : (StringInfo )PG_GETARG_POINTER (0 );
433+
434+ if (state != NULL )
435+ {
436+ bytea * result ;
437+
438+ result = (bytea * )palloc (state -> len + VARHDRSZ );
439+ SET_VARSIZE (result ,state -> len + VARHDRSZ );
440+ memcpy (VARDATA (result ),state -> data ,state -> len );
441+ PG_RETURN_BYTEA_P (result );
442+ }
443+ else
444+ PG_RETURN_NULL ();
445+ }
399446
400447/*
401448 *textin- converts "..." to internal representation