Skip to content

Commit d0e40d3

Browse files
committed
refactor: split into header files
1 parent 9ce1e9f commit d0e40d3

File tree

6 files changed

+128
-97
lines changed

6 files changed

+128
-97
lines changed

src/aggs.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include "pg_prelude.h"
2+
3+
#include "aggs.h"
4+
#include "general.h"
5+
6+
static inline bool is_reserved(char c) {
7+
return c == DQUOTE || c == NEWLINE || c == CR;
8+
}
9+
10+
// Any comma, quote, CR, LF requires quoting as per RFC https://www.ietf.org/rfc/rfc4180.txt
11+
static inline bool needs_quote(const char *s, size_t n, char delim) {
12+
while (n--) {
13+
char c = *s++;
14+
if (c == delim || is_reserved(c)) return true;
15+
}
16+
return false;
17+
}
18+
19+
void parse_csv_options(HeapTupleHeader opts_hdr, CsvOptions *csv_opts) {
20+
// defaults
21+
csv_opts->delim = ',';
22+
csv_opts->with_bom = false;
23+
csv_opts->header = true;
24+
25+
if (opts_hdr == NULL) return;
26+
27+
TupleDesc desc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(opts_hdr),
28+
HeapTupleHeaderGetTypMod(opts_hdr));
29+
30+
Datum values[3];
31+
bool nulls[3];
32+
33+
heap_deform_tuple(
34+
&(HeapTupleData){.t_len = HeapTupleHeaderGetDatumLength(opts_hdr), .t_data = opts_hdr}, desc,
35+
values, nulls);
36+
37+
if (!nulls[0]) {
38+
csv_opts->delim = DatumGetChar(values[0]);
39+
if (is_reserved(csv_opts->delim))
40+
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
41+
errmsg("delimiter cannot be newline, carriage return or "
42+
"double quote")));
43+
}
44+
45+
if (!nulls[1]) {
46+
csv_opts->with_bom = DatumGetBool(values[1]);
47+
}
48+
49+
if (!nulls[2]) {
50+
csv_opts->header = DatumGetBool(values[2]);
51+
}
52+
53+
ReleaseTupleDesc(desc);
54+
}
55+
56+
void csv_append_field(StringInfo buf, const char *s, size_t n, char delim) {
57+
if (!needs_quote(s, n, delim)) {
58+
appendBinaryStringInfo(buf, s, n);
59+
} else {
60+
appendStringInfoChar(buf, DQUOTE);
61+
for (size_t j = 0; j < n; j++) {
62+
char c = s[j];
63+
if (c == DQUOTE) appendStringInfoChar(buf, DQUOTE);
64+
appendStringInfoChar(buf, c);
65+
}
66+
appendStringInfoChar(buf, DQUOTE);
67+
}
68+
}

src/aggs.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef AGGS_H
2+
#define AGGS_H
3+
4+
typedef struct {
5+
char delim;
6+
bool with_bom;
7+
bool header;
8+
} CsvOptions;
9+
10+
typedef struct {
11+
StringInfoData accum_buf;
12+
bool header_done;
13+
bool first_row;
14+
TupleDesc tupdesc;
15+
CsvOptions *options;
16+
} CsvAggState;
17+
18+
void parse_csv_options(HeapTupleHeader opts_hdr, CsvOptions *csv_opts);
19+
20+
void csv_append_field(StringInfo buf, const char *s, size_t n, char delim);
21+
22+
#endif

src/general.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include "general.h"
2+
3+
const char NEWLINE = '\n';
4+
const char DQUOTE = '"';
5+
const char CR = '\r';
6+
const char BOM[3] = "\xEF\xBB\xBF";

src/general.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifndef GENERAL_H
2+
#define GENERAL_H
3+
4+
extern const char NEWLINE;
5+
extern const char BOM[3];
6+
extern const char DQUOTE;
7+
extern const char CR;
8+
9+
#endif

src/pg_csv.c

Lines changed: 10 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,20 @@
1+
#define PG_PRELUDE_IMPL
12
#include "pg_prelude.h"
23

3-
PG_MODULE_MAGIC;
4-
5-
static const char NEWLINE = '\n';
6-
static const char DQUOTE = '"';
7-
static const char CR = '\r';
8-
static const char BOM[3] = "\xEF\xBB\xBF";
9-
10-
typedef struct {
11-
char delim;
12-
bool with_bom;
13-
bool header;
14-
} CsvOptions;
15-
16-
typedef struct {
17-
StringInfoData accum_buf;
18-
bool header_done;
19-
bool first_row;
20-
TupleDesc tupdesc;
21-
CsvOptions *options;
22-
} CsvAggState;
23-
24-
static inline bool is_reserved(char c) {
25-
return c == DQUOTE || c == NEWLINE || c == CR;
26-
}
27-
28-
// Any comma, quote, CR, LF requires quoting as per RFC https://www.ietf.org/rfc/rfc4180.txt
29-
static inline bool needs_quote(const char *s, size_t n, char delim) {
30-
while (n--) {
31-
char c = *s++;
32-
if (c == delim || is_reserved(c)) return true;
33-
}
34-
return false;
35-
}
36-
37-
static inline void csv_append_field(StringInfo buf, const char *s, size_t n, char delim) {
38-
if (!needs_quote(s, n, delim)) {
39-
appendBinaryStringInfo(buf, s, n);
40-
} else {
41-
appendStringInfoChar(buf, DQUOTE);
42-
for (size_t j = 0; j < n; j++) {
43-
char c = s[j];
44-
if (c == DQUOTE) appendStringInfoChar(buf, DQUOTE);
45-
appendStringInfoChar(buf, c);
46-
}
47-
appendStringInfoChar(buf, DQUOTE);
48-
}
49-
}
4+
#include "aggs.h"
5+
#include "general.h"
506

51-
static char *datum_to_cstring(Datum datum, Oid typeoid) {
52-
Oid out_func;
53-
bool is_varlena;
54-
getTypeOutputInfo(typeoid, &out_func, &is_varlena);
55-
56-
return OidOutputFunctionCall(out_func, datum);
57-
}
58-
59-
static void parse_csv_options(HeapTupleHeader opts_hdr, CsvOptions *csv_opts) {
60-
// defaults
61-
csv_opts->delim = ',';
62-
csv_opts->with_bom = false;
63-
csv_opts->header = true;
64-
65-
if (opts_hdr == NULL) return;
66-
67-
TupleDesc desc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(opts_hdr),
68-
HeapTupleHeaderGetTypMod(opts_hdr));
69-
70-
Datum values[3];
71-
bool nulls[3];
7+
PG_MODULE_MAGIC;
728

73-
heap_deform_tuple(
74-
&(HeapTupleData){.t_len = HeapTupleHeaderGetDatumLength(opts_hdr), .t_data = opts_hdr}, desc,
75-
values, nulls);
76-
77-
if (!nulls[0]) {
78-
csv_opts->delim = DatumGetChar(values[0]);
79-
if (is_reserved(csv_opts->delim))
80-
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
81-
errmsg("delimiter cannot be newline, carriage return or "
82-
"double quote")));
83-
}
9+
PG_FUNCTION_INFO_V1(csv_agg_finalfn);
10+
Datum csv_agg_finalfn(PG_FUNCTION_ARGS) {
11+
if (PG_ARGISNULL(0)) PG_RETURN_NULL();
8412

85-
if (!nulls[1]) {
86-
csv_opts->with_bom = DatumGetBool(values[1]);
87-
}
13+
CsvAggState *state = (CsvAggState *)PG_GETARG_POINTER(0);
8814

89-
if (!nulls[2]) {
90-
csv_opts->header = DatumGetBool(values[2]);
91-
}
15+
if (state->tupdesc != NULL) ReleaseTupleDesc(state->tupdesc);
9216

93-
ReleaseTupleDesc(desc);
17+
PG_RETURN_TEXT_P(cstring_to_text_with_len(state->accum_buf.data, state->accum_buf.len));
9418
}
9519

9620
PG_FUNCTION_INFO_V1(csv_agg_transfn);
@@ -188,14 +112,3 @@ Datum csv_agg_transfn(PG_FUNCTION_ARGS) {
188112

189113
PG_RETURN_POINTER(state);
190114
}
191-
192-
PG_FUNCTION_INFO_V1(csv_agg_finalfn);
193-
Datum csv_agg_finalfn(PG_FUNCTION_ARGS) {
194-
if (PG_ARGISNULL(0)) PG_RETURN_NULL();
195-
196-
CsvAggState *state = (CsvAggState *)PG_GETARG_POINTER(0);
197-
198-
if (state->tupdesc != NULL) ReleaseTupleDesc(state->tupdesc);
199-
200-
PG_RETURN_TEXT_P(cstring_to_text_with_len(state->accum_buf.data, state->accum_buf.len));
201-
}

src/pg_prelude.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,17 @@
4949

5050
#pragma GCC diagnostic pop
5151

52+
char *datum_to_cstring(Datum datum, Oid typeoid);
5253
#endif /* PG_PRELUDE_H */
54+
55+
#ifdef PG_PRELUDE_IMPL
56+
57+
char *datum_to_cstring(Datum datum, Oid typeoid) {
58+
Oid out_func;
59+
bool is_varlena;
60+
getTypeOutputInfo(typeoid, &out_func, &is_varlena);
61+
62+
return OidOutputFunctionCall(out_func, datum);
63+
}
64+
65+
#endif /* PG_PRELUDE_IMPL */

0 commit comments

Comments
 (0)