@@ -5,9 +5,11 @@ PG_MODULE_MAGIC;
55static const char NEWLINE = '\n' ;
66static const char DQUOTE = '"' ;
77static const char CR = '\r' ;
8+ static const char BOM [3 ] = "\xEF\xBB\xBF" ;
89
910typedef struct {
1011 char delim ;
12+ bool with_bom ;
1113} CsvOptions ;
1214
1315typedef struct {
@@ -55,15 +57,16 @@ static char *datum_to_cstring(Datum datum, Oid typeoid) {
5557
5658static void parse_csv_options (HeapTupleHeader opts_hdr , CsvOptions * csv_opts ) {
5759 // defaults
58- csv_opts -> delim = ',' ;
60+ csv_opts -> delim = ',' ;
61+ csv_opts -> with_bom = false;
5962
6063 if (opts_hdr == NULL ) return ;
6164
6265 TupleDesc desc = lookup_rowtype_tupdesc (HeapTupleHeaderGetTypeId (opts_hdr ),
6366 HeapTupleHeaderGetTypMod (opts_hdr ));
6467
65- Datum values [1 ];
66- bool nulls [1 ];
68+ Datum values [2 ];
69+ bool nulls [2 ];
6770
6871 heap_deform_tuple (
6972 & (HeapTupleData ){.t_len = HeapTupleHeaderGetDatumLength (opts_hdr ), .t_data = opts_hdr }, desc ,
@@ -77,6 +80,10 @@ static void parse_csv_options(HeapTupleHeader opts_hdr, CsvOptions *csv_opts) {
7780 "double quote" )));
7881 }
7982
83+ if (!nulls [1 ]) {
84+ csv_opts -> with_bom = DatumGetBool (values [1 ]);
85+ }
86+
8087 ReleaseTupleDesc (desc );
8188}
8289
@@ -118,6 +125,8 @@ Datum csv_agg_transfn(PG_FUNCTION_ARGS) {
118125 TupleDesc tdesc =
119126 lookup_rowtype_tupdesc (HeapTupleHeaderGetTypeId (next ), HeapTupleHeaderGetTypMod (next ));
120127
128+ if (state -> options -> with_bom ) appendBinaryStringInfo (& state -> accum_buf , BOM , sizeof (BOM ));
129+
121130 // build header row
122131 for (int i = 0 ; i < tdesc -> natts ; i ++ ) {
123132 Form_pg_attribute att = TupleDescAttr (tdesc , i );
0 commit comments