| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
typedef struct fupg_tio fupg_tio; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
/* Send function, takes a Perl value and should write the binary encoded |
|
4
|
|
|
|
|
|
|
* format into the given fustr. */ |
|
5
|
|
|
|
|
|
|
typedef void (*fupg_send_fn)(pTHX_ const fupg_tio *, SV *, fustr *); |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
/* Receive function, takes a binary string and should return a Perl value. */ |
|
8
|
|
|
|
|
|
|
typedef SV *(*fupg_recv_fn)(pTHX_ const fupg_tio *, const char *, int); |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
typedef struct { |
|
11
|
|
|
|
|
|
|
char n[64]; |
|
12
|
|
|
|
|
|
|
} fupg_name; |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
/* Record/composite type definition */ |
|
15
|
|
|
|
|
|
|
typedef struct { |
|
16
|
|
|
|
|
|
|
int nattrs; |
|
17
|
|
|
|
|
|
|
struct { |
|
18
|
|
|
|
|
|
|
Oid oid; |
|
19
|
|
|
|
|
|
|
fupg_name name; |
|
20
|
|
|
|
|
|
|
} attrs[]; |
|
21
|
|
|
|
|
|
|
} fupg_record; |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
/* Type I/O context */ |
|
24
|
|
|
|
|
|
|
struct fupg_tio { |
|
25
|
|
|
|
|
|
|
Oid oid; |
|
26
|
|
|
|
|
|
|
const char *name; |
|
27
|
|
|
|
|
|
|
fupg_send_fn send; |
|
28
|
|
|
|
|
|
|
fupg_recv_fn recv; |
|
29
|
|
|
|
|
|
|
union { |
|
30
|
|
|
|
|
|
|
fupg_tio *arrayelem; |
|
31
|
|
|
|
|
|
|
struct { |
|
32
|
|
|
|
|
|
|
const fupg_record *info; |
|
33
|
|
|
|
|
|
|
fupg_tio *tio; |
|
34
|
|
|
|
|
|
|
} record; |
|
35
|
|
|
|
|
|
|
SV *cb; |
|
36
|
|
|
|
|
|
|
}; |
|
37
|
|
|
|
|
|
|
}; |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
typedef struct { |
|
40
|
|
|
|
|
|
|
Oid oid; |
|
41
|
|
|
|
|
|
|
Oid elemoid; /* For arrays & domain types; relid for records */ |
|
42
|
|
|
|
|
|
|
fupg_name name; |
|
43
|
|
|
|
|
|
|
fupg_send_fn send; |
|
44
|
|
|
|
|
|
|
fupg_recv_fn recv; |
|
45
|
|
|
|
|
|
|
} fupg_type; |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
#define RECVFN(name) static SV *fupg_recv_##name(pTHX_ const fupg_tio *ctx __attribute__((unused)), const char *buf, int len) |
|
50
|
|
|
|
|
|
|
#define SENDFN(name) static void fupg_send_##name(pTHX_ const fupg_tio *ctx __attribute__((unused)), SV *val, fustr *out) |
|
51
|
|
|
|
|
|
|
#define RERR(msg, ...) fu_confess("Error parsing value for type '%s' (oid %u): "msg, ctx->name, ctx->oid __VA_OPT__(,) __VA_ARGS__) |
|
52
|
|
|
|
|
|
|
#define SERR(msg, ...) fu_confess("Error converting Perl value '%s' to type '%s' (oid %u): "msg, SvPV_nolen(val), ctx->name, ctx->oid __VA_OPT__(,) __VA_ARGS__) |
|
53
|
|
|
|
|
|
|
#define RLEN(l) if (l != len) RERR("expected %d bytes but got %d", l, len) |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
/* Perl likes to play loose with SV-to-integer conversions, but that's not |
|
56
|
|
|
|
|
|
|
* very fun when trying to store values in a database. Text-based bind |
|
57
|
|
|
|
|
|
|
* parameters get stricter validation by Postgres, so let's emulate some of |
|
58
|
|
|
|
|
|
|
* that for binary parameters as well. */ |
|
59
|
|
|
|
|
|
|
#define SIV(min, max) IV iv;\ |
|
60
|
|
|
|
|
|
|
if (SvIOK(val)) iv = SvIV(val); \ |
|
61
|
|
|
|
|
|
|
else if (SvNOK(val)) { \ |
|
62
|
|
|
|
|
|
|
NV nv = SvNV(val); \ |
|
63
|
|
|
|
|
|
|
if (nv < IV_MIN || nv > IV_MAX || fabs((double)(nv - floor(nv))) > 0.0000000001) SERR("expected integer");\ |
|
64
|
|
|
|
|
|
|
iv = SvIV(val); \ |
|
65
|
|
|
|
|
|
|
} else if (SvPOK(val)) {\ |
|
66
|
|
|
|
|
|
|
STRLEN sl; \ |
|
67
|
|
|
|
|
|
|
UV uv; \ |
|
68
|
|
|
|
|
|
|
char *s = SvPV(val, sl); \ |
|
69
|
|
|
|
|
|
|
if (*s == '-' && grok_atoUV(s+1, &uv, NULL) && uv <= ((UV)IV_MAX)+1) iv = SvIV(val);\ |
|
70
|
|
|
|
|
|
|
else if (grok_atoUV(s, &uv, NULL) && uv <= IV_MAX) iv = SvIV(val);\ |
|
71
|
|
|
|
|
|
|
else SERR("expected integer");\ |
|
72
|
|
|
|
|
|
|
} else SERR("expected integer");\ |
|
73
|
|
|
|
|
|
|
if (iv < min || iv > max) SERR("integer out of range") |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
/* These are simply marker functions, not supposed to be called directly */ |
|
76
|
0
|
|
|
|
|
|
RECVFN(domain) { (void)buf; (void)len; RERR("domain type should not be handled by this function"); } |
|
77
|
0
|
|
|
|
|
|
SENDFN(domain) { (void)out; SERR("domain type should not be handled by this function"); } |
|
78
|
|
|
|
|
|
|
|
|
79
|
0
|
|
|
|
|
|
RECVFN(bool) { |
|
80
|
0
|
0
|
|
|
|
|
RLEN(1); |
|
81
|
0
|
0
|
|
|
|
|
return *buf ? newSV_true() : newSV_false(); |
|
82
|
|
|
|
|
|
|
} |
|
83
|
|
|
|
|
|
|
|
|
84
|
0
|
|
|
|
|
|
SENDFN(bool) { |
|
85
|
0
|
|
|
|
|
|
int r = fu_2bool(aTHX_ val); |
|
86
|
0
|
0
|
|
|
|
|
if (r < 0) { |
|
87
|
|
|
|
|
|
|
STRLEN l; |
|
88
|
0
|
|
|
|
|
|
const char *x = SvPV(val, l); |
|
89
|
0
|
0
|
|
|
|
|
if (l == 0 || (l == 1 && (*x == '0' || *x == 'f'))) r = 0; |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
90
|
0
|
0
|
|
|
|
|
else if (l == 1 && (*x == '1' || *x == 't')) r = 1; |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
91
|
0
|
|
|
|
|
|
else SERR("invalid boolean value: %s", x); |
|
92
|
|
|
|
|
|
|
} |
|
93
|
0
|
|
|
|
|
|
fustr_write_ch(out, r); |
|
94
|
0
|
|
|
|
|
|
} |
|
95
|
|
|
|
|
|
|
|
|
96
|
0
|
|
|
|
|
|
RECVFN(void) { |
|
97
|
0
|
0
|
|
|
|
|
RLEN(0); |
|
98
|
|
|
|
|
|
|
(void)buf; |
|
99
|
0
|
|
|
|
|
|
return newSV(0); |
|
100
|
|
|
|
|
|
|
} |
|
101
|
|
|
|
|
|
|
|
|
102
|
0
|
|
|
|
|
|
SENDFN(void) { |
|
103
|
|
|
|
|
|
|
(void)val; (void)out; |
|
104
|
0
|
|
|
|
|
|
} |
|
105
|
|
|
|
|
|
|
|
|
106
|
0
|
|
|
|
|
|
RECVFN(int2) { |
|
107
|
0
|
0
|
|
|
|
|
RLEN(2); |
|
108
|
0
|
|
|
|
|
|
return newSViv(fu_frombeI(16, buf)); |
|
109
|
|
|
|
|
|
|
} |
|
110
|
|
|
|
|
|
|
|
|
111
|
0
|
|
|
|
|
|
SENDFN(int2) { |
|
112
|
0
|
0
|
|
|
|
|
SIV(-32768, 32767); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
113
|
0
|
|
|
|
|
|
fustr_writebeI(16, out, iv); |
|
114
|
0
|
|
|
|
|
|
} |
|
115
|
|
|
|
|
|
|
|
|
116
|
0
|
|
|
|
|
|
RECVFN(int4) { |
|
117
|
0
|
0
|
|
|
|
|
RLEN(4); |
|
118
|
0
|
|
|
|
|
|
return newSViv(fu_frombeI(32, buf)); |
|
119
|
|
|
|
|
|
|
} |
|
120
|
|
|
|
|
|
|
|
|
121
|
0
|
|
|
|
|
|
SENDFN(int4) { |
|
122
|
0
|
0
|
|
|
|
|
SIV(-2147483648, 2147483647); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
123
|
0
|
|
|
|
|
|
fustr_writebeI(32, out, iv); |
|
124
|
0
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
|
|
126
|
0
|
|
|
|
|
|
RECVFN(int8) { |
|
127
|
0
|
0
|
|
|
|
|
RLEN(8); |
|
128
|
0
|
|
|
|
|
|
return newSViv(fu_frombeI(64, buf)); |
|
129
|
|
|
|
|
|
|
} |
|
130
|
|
|
|
|
|
|
|
|
131
|
0
|
|
|
|
|
|
SENDFN(int8) { |
|
132
|
0
|
0
|
|
|
|
|
SIV(IV_MIN, IV_MAX); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
133
|
0
|
|
|
|
|
|
fustr_writebeI(64, out, iv); |
|
134
|
0
|
|
|
|
|
|
} |
|
135
|
|
|
|
|
|
|
|
|
136
|
0
|
|
|
|
|
|
RECVFN(uint4) { |
|
137
|
0
|
0
|
|
|
|
|
RLEN(4); |
|
138
|
0
|
|
|
|
|
|
return newSViv(fu_frombeU(32, buf)); |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
|
|
141
|
0
|
|
|
|
|
|
SENDFN(uint4) { |
|
142
|
0
|
0
|
|
|
|
|
SIV(0, UINT32_MAX); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
143
|
0
|
|
|
|
|
|
fustr_writebeU(32, out, iv); |
|
144
|
0
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
|
|
146
|
0
|
|
|
|
|
|
RECVFN(uint8) { |
|
147
|
0
|
0
|
|
|
|
|
RLEN(8); |
|
148
|
0
|
|
|
|
|
|
return newSVuv(fu_frombeU(64, buf)); |
|
149
|
|
|
|
|
|
|
} |
|
150
|
|
|
|
|
|
|
|
|
151
|
0
|
|
|
|
|
|
SENDFN(uint8) { |
|
152
|
|
|
|
|
|
|
/* Doesn't have the nice input validation of 'SIV', but this type is pretty rare anyway */ |
|
153
|
0
|
|
|
|
|
|
fustr_writebeU(64, out, SvUV(val)); |
|
154
|
0
|
|
|
|
|
|
} |
|
155
|
|
|
|
|
|
|
|
|
156
|
0
|
|
|
|
|
|
RECVFN(bytea) { |
|
157
|
0
|
|
|
|
|
|
return newSVpvn(buf, len); |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
|
|
160
|
0
|
|
|
|
|
|
SENDFN(bytea) { |
|
161
|
|
|
|
|
|
|
STRLEN len; |
|
162
|
0
|
|
|
|
|
|
const char *buf = SvPVbyte(val, len); |
|
163
|
0
|
|
|
|
|
|
fustr_write(out, buf, len); |
|
164
|
0
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
|
|
166
|
0
|
|
|
|
|
|
RECVFN(hex) { |
|
167
|
0
|
0
|
|
|
|
|
SV *r = newSV(len ? len * 2 : 1); |
|
168
|
0
|
|
|
|
|
|
SvPOK_only(r); |
|
169
|
0
|
|
|
|
|
|
char *out = SvPVX(r); |
|
170
|
0
|
|
|
|
|
|
const unsigned char *in = (const unsigned char *)buf; |
|
171
|
|
|
|
|
|
|
int i; |
|
172
|
0
|
0
|
|
|
|
|
for (i=0; i
|
|
173
|
0
|
|
|
|
|
|
*out++ = PL_hexdigit[(in[i] >> 4) & 0x0f]; |
|
174
|
0
|
|
|
|
|
|
*out++ = PL_hexdigit[in[i] & 0x0f]; |
|
175
|
|
|
|
|
|
|
} |
|
176
|
0
|
|
|
|
|
|
*out = 0; |
|
177
|
0
|
|
|
|
|
|
SvCUR_set(r, len * 2); |
|
178
|
0
|
|
|
|
|
|
return r; |
|
179
|
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
|
|
181
|
0
|
|
|
|
|
|
SENDFN(hex) { |
|
182
|
|
|
|
|
|
|
STRLEN len; |
|
183
|
0
|
|
|
|
|
|
const char *in = SvPV(val, len); |
|
184
|
0
|
|
|
|
|
|
const char *end = in + len; |
|
185
|
0
|
0
|
|
|
|
|
if (len % 2) SERR("Invalid hex string"); |
|
186
|
0
|
0
|
|
|
|
|
while (in < end) { |
|
187
|
0
|
|
|
|
|
|
int v = (fu_hexdig(*in)<<4) + fu_hexdig(in[1]); |
|
188
|
0
|
0
|
|
|
|
|
if (v > 0xff) SERR("Invalid hex string"); |
|
189
|
0
|
|
|
|
|
|
fustr_write_ch(out, v); |
|
190
|
0
|
|
|
|
|
|
in += 2; |
|
191
|
|
|
|
|
|
|
} |
|
192
|
0
|
|
|
|
|
|
} |
|
193
|
|
|
|
|
|
|
|
|
194
|
0
|
|
|
|
|
|
RECVFN(char) { |
|
195
|
0
|
0
|
|
|
|
|
RLEN(1); |
|
196
|
0
|
|
|
|
|
|
return newSVpvn(buf, len); |
|
197
|
|
|
|
|
|
|
} |
|
198
|
|
|
|
|
|
|
|
|
199
|
0
|
|
|
|
|
|
SENDFN(char) { |
|
200
|
|
|
|
|
|
|
STRLEN len; |
|
201
|
0
|
|
|
|
|
|
const char *buf = SvPVbyte(val, len); |
|
202
|
0
|
0
|
|
|
|
|
if (len != 1) SERR("expected 1-byte string"); |
|
203
|
0
|
|
|
|
|
|
fustr_write(out, buf, len); |
|
204
|
0
|
|
|
|
|
|
} |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
/* Works for many text-based column types, including receiving any value in the text format */ |
|
207
|
0
|
|
|
|
|
|
RECVFN(text) { |
|
208
|
0
|
0
|
|
|
|
|
if (!is_c9strict_utf8_string((const U8*)buf, len)) RERR("invalid UTF-8"); |
|
209
|
0
|
|
|
|
|
|
return newSVpvn_utf8(buf, len, 1); |
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
|
|
212
|
0
|
|
|
|
|
|
SENDFN(text) { |
|
213
|
|
|
|
|
|
|
STRLEN len; |
|
214
|
0
|
|
|
|
|
|
const char *buf = SvPVutf8(val, len); |
|
215
|
0
|
|
|
|
|
|
fustr_write(out, buf, len); |
|
216
|
0
|
|
|
|
|
|
} |
|
217
|
|
|
|
|
|
|
|
|
218
|
0
|
|
|
|
|
|
RECVFN(float4) { |
|
219
|
0
|
0
|
|
|
|
|
RLEN(4); |
|
220
|
0
|
|
|
|
|
|
return newSVnv(fu_frombeT(float, 32, buf)); |
|
221
|
|
|
|
|
|
|
} |
|
222
|
|
|
|
|
|
|
|
|
223
|
0
|
|
|
|
|
|
SENDFN(float4) { |
|
224
|
0
|
0
|
|
|
|
|
if (!looks_like_number(val)) SERR("expected a number"); |
|
225
|
0
|
|
|
|
|
|
fustr_writebeT(float, 32, out, SvNV(val)); |
|
226
|
0
|
|
|
|
|
|
} |
|
227
|
|
|
|
|
|
|
|
|
228
|
0
|
|
|
|
|
|
RECVFN(float8) { |
|
229
|
0
|
0
|
|
|
|
|
RLEN(8); |
|
230
|
0
|
|
|
|
|
|
return newSVnv(fu_frombeT(double, 64, buf)); |
|
231
|
|
|
|
|
|
|
} |
|
232
|
|
|
|
|
|
|
|
|
233
|
0
|
|
|
|
|
|
SENDFN(float8) { |
|
234
|
0
|
0
|
|
|
|
|
if (!looks_like_number(val)) SERR("expected a number"); |
|
235
|
0
|
|
|
|
|
|
fustr_writebeT(double, 64, out, SvNV(val)); |
|
236
|
0
|
|
|
|
|
|
} |
|
237
|
|
|
|
|
|
|
|
|
238
|
0
|
|
|
|
|
|
RECVFN(json) { |
|
239
|
0
|
|
|
|
|
|
fujson_parse_ctx json = { |
|
240
|
|
|
|
|
|
|
.buf = (const unsigned char *)buf, |
|
241
|
0
|
|
|
|
|
|
.end = (const unsigned char *)buf + len, |
|
242
|
|
|
|
|
|
|
.depth = 512 |
|
243
|
|
|
|
|
|
|
}; |
|
244
|
0
|
|
|
|
|
|
SV *sv = fujson_parse(aTHX_ &json); |
|
245
|
0
|
0
|
|
|
|
|
if (sv == NULL) RERR("invalid JSON"); |
|
246
|
0
|
0
|
|
|
|
|
if (json.buf != json.end) RERR("trailing garbage"); |
|
247
|
0
|
|
|
|
|
|
return sv; |
|
248
|
|
|
|
|
|
|
} |
|
249
|
|
|
|
|
|
|
|
|
250
|
0
|
|
|
|
|
|
SENDFN(json) { |
|
251
|
0
|
|
|
|
|
|
fujson_fmt_ctx json = { .out = out, .depth = 512, .canon = 1, .pretty = 0 }; |
|
252
|
0
|
|
|
|
|
|
fujson_fmt(aTHX_ &json, val); |
|
253
|
0
|
|
|
|
|
|
} |
|
254
|
|
|
|
|
|
|
|
|
255
|
0
|
|
|
|
|
|
RECVFN(jsonb) { |
|
256
|
0
|
0
|
|
|
|
|
if (len <= 1 || *buf != 1) RERR("invalid JSONB"); |
|
|
|
0
|
|
|
|
|
|
|
257
|
0
|
|
|
|
|
|
return fupg_recv_json(aTHX_ ctx, buf+1, len-1); |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
|
|
260
|
0
|
|
|
|
|
|
SENDFN(jsonb) { |
|
261
|
0
|
|
|
|
|
|
fustr_write_ch(out, 1); |
|
262
|
0
|
|
|
|
|
|
fupg_send_json(aTHX_ ctx, val, out); |
|
263
|
0
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
|
|
265
|
0
|
|
|
|
|
|
RECVFN(jsonpath) { |
|
266
|
0
|
0
|
|
|
|
|
if (len <= 1 || *buf != 1) RERR("invalid jsonpath"); |
|
|
|
0
|
|
|
|
|
|
|
267
|
0
|
|
|
|
|
|
return fupg_recv_text(aTHX_ ctx, buf+1, len-1); |
|
268
|
|
|
|
|
|
|
} |
|
269
|
|
|
|
|
|
|
|
|
270
|
0
|
|
|
|
|
|
SENDFN(jsonpath) { |
|
271
|
0
|
|
|
|
|
|
fustr_write_ch(out, 1); |
|
272
|
0
|
|
|
|
|
|
fupg_send_text(aTHX_ ctx, val, out); |
|
273
|
0
|
|
|
|
|
|
} |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
#define ARRAY_MAXDIM 100 |
|
277
|
|
|
|
|
|
|
|
|
278
|
0
|
|
|
|
|
|
static SV *fupg_recv_array_elem(pTHX_ const fupg_tio *elem, const char *header, U32 dim, U32 ndim, const char **buf, const char *end) { |
|
279
|
|
|
|
|
|
|
SV *r; |
|
280
|
0
|
0
|
|
|
|
|
if (dim == ndim) { |
|
281
|
0
|
0
|
|
|
|
|
if (end - *buf < 4) fu_confess("Invalid array format"); |
|
282
|
0
|
|
|
|
|
|
I32 len = fu_frombeI(32, *buf); |
|
283
|
0
|
|
|
|
|
|
*buf += 4; |
|
284
|
|
|
|
|
|
|
|
|
285
|
0
|
0
|
|
|
|
|
if (end - *buf < len) fu_confess("Invalid array format"); |
|
286
|
0
|
0
|
|
|
|
|
if (len >= 0) { |
|
287
|
0
|
|
|
|
|
|
r = elem->recv(aTHX_ elem, *buf, len); |
|
288
|
0
|
|
|
|
|
|
*buf += len; |
|
289
|
|
|
|
|
|
|
} else { |
|
290
|
0
|
|
|
|
|
|
r = newSV(0); |
|
291
|
|
|
|
|
|
|
} |
|
292
|
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
} else { |
|
294
|
0
|
|
|
|
|
|
U32 n = fu_frombeU(32, header + dim*8); |
|
295
|
0
|
|
|
|
|
|
AV *av = newAV_alloc_x(n); |
|
296
|
0
|
|
|
|
|
|
r = sv_2mortal(newRV_noinc((SV *)av)); /* need to mortalize, we may croak */ |
|
297
|
|
|
|
|
|
|
U32 i; |
|
298
|
0
|
0
|
|
|
|
|
for (i=0; i
|
|
299
|
0
|
|
|
|
|
|
av_push_simple(av, fupg_recv_array_elem(aTHX_ elem, header, dim+1, ndim, buf, end)); |
|
300
|
0
|
|
|
|
|
|
SvREFCNT_inc(r); /* We're safe now, make sure it survives the mortal stack cleanup */ |
|
301
|
|
|
|
|
|
|
} |
|
302
|
0
|
|
|
|
|
|
return r; |
|
303
|
|
|
|
|
|
|
} |
|
304
|
|
|
|
|
|
|
|
|
305
|
0
|
|
|
|
|
|
RECVFN(array) { |
|
306
|
0
|
0
|
|
|
|
|
if (len < 12) RERR("input data too short"); |
|
307
|
0
|
|
|
|
|
|
U32 ndim = fu_frombeU(32, buf); |
|
308
|
|
|
|
|
|
|
// buf+4 is hasnull, can safely ignore |
|
309
|
0
|
|
|
|
|
|
Oid elemtype = fu_frombeU(32, buf+8); |
|
310
|
0
|
0
|
|
|
|
|
if (elemtype != ctx->arrayelem->oid) RERR("invalid element type, expected %u but got %u", ctx->arrayelem->oid, elemtype); |
|
311
|
|
|
|
|
|
|
|
|
312
|
0
|
0
|
|
|
|
|
if (ndim == 0) return newRV_noinc((SV *)newAV()); |
|
313
|
0
|
0
|
|
|
|
|
if (ndim > ARRAY_MAXDIM) RERR("too many dimensions"); |
|
314
|
0
|
0
|
|
|
|
|
if ((U32)len < 12 + ndim*8) RERR("input data too short"); |
|
315
|
|
|
|
|
|
|
|
|
316
|
0
|
|
|
|
|
|
const char *header = buf + 12; |
|
317
|
0
|
|
|
|
|
|
const char *data = header + ndim * 8; |
|
318
|
0
|
|
|
|
|
|
return fupg_recv_array_elem(aTHX_ ctx->arrayelem, header, 0, ndim, &data, buf+len); |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
|
|
321
|
0
|
|
|
|
|
|
void fupg_send_array_elem(pTHX_ const fupg_tio *elem, const U32 *dims, U32 dim, U32 ndim, SV *v, fustr *out, int *hasnull) { |
|
322
|
0
|
|
|
|
|
|
SvGETMAGIC(v); |
|
323
|
0
|
0
|
|
|
|
|
if (dim == ndim) { |
|
324
|
0
|
0
|
|
|
|
|
if (!SvOK(v)) { |
|
325
|
0
|
|
|
|
|
|
fustr_write(out, "\xff\xff\xff\xff", 4); |
|
326
|
0
|
|
|
|
|
|
*hasnull = 1; |
|
327
|
0
|
|
|
|
|
|
return; |
|
328
|
|
|
|
|
|
|
} |
|
329
|
0
|
0
|
|
|
|
|
size_t lenoff = fustr_len(out); |
|
330
|
0
|
|
|
|
|
|
fustr_write(out, "\0\0\0\0", 4); |
|
331
|
0
|
|
|
|
|
|
elem->send(aTHX_ elem, v, out); |
|
332
|
0
|
0
|
|
|
|
|
fu_tobeU(32, fustr_start(out) + lenoff, fustr_len(out) - lenoff - 4); |
|
|
|
0
|
|
|
|
|
|
|
333
|
0
|
|
|
|
|
|
return; |
|
334
|
|
|
|
|
|
|
} |
|
335
|
|
|
|
|
|
|
|
|
336
|
0
|
0
|
|
|
|
|
if (!SvROK(v)) fu_confess("Invalid array structure in bind parameter"); |
|
337
|
0
|
|
|
|
|
|
v = SvRV(v); |
|
338
|
0
|
|
|
|
|
|
SvGETMAGIC(v); |
|
339
|
0
|
0
|
|
|
|
|
if (SvTYPE(v) != SVt_PVAV) fu_confess("Invalid array structure in bind parameter"); |
|
340
|
0
|
|
|
|
|
|
AV *av = (AV*)v; |
|
341
|
0
|
0
|
|
|
|
|
if (av_count(av) != dims[dim]) fu_confess("Invalid array structure in bind parameter"); |
|
342
|
|
|
|
|
|
|
U32 i; |
|
343
|
0
|
0
|
|
|
|
|
for (i=0; i
|
|
344
|
0
|
|
|
|
|
|
SV **sv = av_fetch(av, i, 0); |
|
345
|
0
|
0
|
|
|
|
|
if (!sv || !*sv) fu_confess("Invalid array structure in bind parameter"); |
|
|
|
0
|
|
|
|
|
|
|
346
|
0
|
|
|
|
|
|
fupg_send_array_elem(aTHX_ elem, dims, dim+1, ndim, *sv, out, hasnull); |
|
347
|
|
|
|
|
|
|
} |
|
348
|
|
|
|
|
|
|
} |
|
349
|
|
|
|
|
|
|
|
|
350
|
0
|
|
|
|
|
|
SENDFN(array) { |
|
351
|
0
|
|
|
|
|
|
U32 ndim = 0; |
|
352
|
|
|
|
|
|
|
U32 dims[ARRAY_MAXDIM]; |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
/* First figure out ndim and length-per-dim. The has-null flag and |
|
355
|
|
|
|
|
|
|
* verification that each array-per-dimension has the same length is done |
|
356
|
|
|
|
|
|
|
* while writing the elements. |
|
357
|
|
|
|
|
|
|
* This is prone to errors if the elem type also accepts arrays as input, |
|
358
|
|
|
|
|
|
|
* not quite sure how to deal with that case. */ |
|
359
|
0
|
|
|
|
|
|
SV *v = val; |
|
360
|
0
|
|
|
|
|
|
while (true) { |
|
361
|
0
|
|
|
|
|
|
SvGETMAGIC(v); |
|
362
|
0
|
0
|
|
|
|
|
if (!SvROK(v)) break; |
|
363
|
0
|
|
|
|
|
|
v = SvRV(v); |
|
364
|
0
|
|
|
|
|
|
SvGETMAGIC(v); |
|
365
|
0
|
0
|
|
|
|
|
if (SvTYPE(v) != SVt_PVAV) break; |
|
366
|
0
|
0
|
|
|
|
|
if (ndim >= ARRAY_MAXDIM) SERR("too many dimensions"); |
|
367
|
0
|
|
|
|
|
|
dims[ndim] = av_count((AV*)v); |
|
368
|
0
|
0
|
|
|
|
|
if (ndim > 0 && dims[ndim] == 0) SERR("nested arrays may not be empty"); |
|
|
|
0
|
|
|
|
|
|
|
369
|
0
|
|
|
|
|
|
ndim++; |
|
370
|
0
|
|
|
|
|
|
SV **sv = av_fetch((AV*)v, 0, 0); |
|
371
|
0
|
0
|
|
|
|
|
if (!sv || !*sv) break; |
|
|
|
0
|
|
|
|
|
|
|
372
|
0
|
|
|
|
|
|
v = *sv; |
|
373
|
|
|
|
|
|
|
} |
|
374
|
0
|
0
|
|
|
|
|
if (ndim == 0) SERR("expected an array"); |
|
375
|
0
|
0
|
|
|
|
|
if (dims[0] == 0) ndim = 0; |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
/* Write header */ |
|
378
|
0
|
|
|
|
|
|
fustr_writebeU(32, out, ndim); |
|
379
|
0
|
|
|
|
|
|
fustr_write(out, "\0\0\0\0", 4); /* Placeholder for isnull */ |
|
380
|
0
|
0
|
|
|
|
|
size_t hasnull_off = fustr_len(out) - 1; |
|
381
|
0
|
|
|
|
|
|
fustr_writebeU(32, out, ctx->arrayelem->oid); |
|
382
|
|
|
|
|
|
|
U32 i; |
|
383
|
0
|
0
|
|
|
|
|
for (i=0; i
|
|
384
|
0
|
|
|
|
|
|
fustr_writebeU(32, out, dims[i]); |
|
385
|
|
|
|
|
|
|
/* int2vector and oidvector expect 0-based indexing, |
|
386
|
|
|
|
|
|
|
* everything else defaults to 1-based indexing. */ |
|
387
|
0
|
0
|
|
|
|
|
if (ctx->oid == 22 || ctx->oid == 30) fustr_write(out, "\0\0\0\0", 4); |
|
|
|
0
|
|
|
|
|
|
|
388
|
0
|
|
|
|
|
|
else fustr_write(out, "\0\0\0\1", 4); |
|
389
|
|
|
|
|
|
|
} |
|
390
|
0
|
0
|
|
|
|
|
if (ndim == 0) return; |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
/* write the elements */ |
|
393
|
0
|
|
|
|
|
|
int hasnull = 0; |
|
394
|
0
|
|
|
|
|
|
fupg_send_array_elem(aTHX_ ctx->arrayelem, dims, 0, ndim, val, out, &hasnull); |
|
395
|
0
|
0
|
|
|
|
|
if (hasnull) fustr_start(out)[hasnull_off] = 1; |
|
|
|
0
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
} |
|
397
|
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
#undef ARRAY_MAXDIM |
|
399
|
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
|
|
401
|
0
|
|
|
|
|
|
RECVFN(record) { |
|
402
|
0
|
0
|
|
|
|
|
if (len < 4) RERR("input data too short"); |
|
403
|
0
|
|
|
|
|
|
I32 nfields = fu_frombeI(32, buf); |
|
404
|
0
|
0
|
|
|
|
|
if (nfields != ctx->record.info->nattrs) RERR("expected %d fields but got %d", ctx->record.info->nattrs, nfields); |
|
405
|
0
|
|
|
|
|
|
buf += 4; len -= 4; |
|
406
|
0
|
|
|
|
|
|
HV *hv = newHV(); |
|
407
|
0
|
|
|
|
|
|
SV *sv = sv_2mortal(newRV_noinc((SV *)hv)); |
|
408
|
|
|
|
|
|
|
I32 i; |
|
409
|
0
|
0
|
|
|
|
|
for (i=0; i
|
|
410
|
0
|
0
|
|
|
|
|
if (len < 8) RERR("input data too short"); |
|
411
|
0
|
|
|
|
|
|
U32 oid = fu_frombeU(32, buf); |
|
412
|
0
|
0
|
|
|
|
|
if (oid != ctx->record.info->attrs[i].oid) |
|
413
|
0
|
|
|
|
|
|
RERR("expected field %d to be of type %u but got %u", i, ctx->record.info->attrs[i].oid, oid); |
|
414
|
0
|
|
|
|
|
|
I32 vlen = fu_frombeI(32, buf+4); |
|
415
|
|
|
|
|
|
|
SV *r; |
|
416
|
0
|
|
|
|
|
|
buf += 8; len -= 8; |
|
417
|
0
|
0
|
|
|
|
|
if (vlen > len) RERR("input data too short"); |
|
418
|
0
|
0
|
|
|
|
|
if (vlen >= 0) { |
|
419
|
0
|
|
|
|
|
|
r = ctx->record.tio[i].recv(aTHX_ ctx->record.tio+i, buf, vlen); |
|
420
|
0
|
|
|
|
|
|
buf += vlen; len -= vlen; |
|
421
|
|
|
|
|
|
|
} else { |
|
422
|
0
|
|
|
|
|
|
r = newSV(0); |
|
423
|
|
|
|
|
|
|
} |
|
424
|
0
|
|
|
|
|
|
hv_store(hv, ctx->record.info->attrs[i].name.n, -strlen(ctx->record.info->attrs[i].name.n), r, 0); |
|
425
|
|
|
|
|
|
|
} |
|
426
|
0
|
|
|
|
|
|
return SvREFCNT_inc(sv); |
|
427
|
|
|
|
|
|
|
} |
|
428
|
|
|
|
|
|
|
|
|
429
|
0
|
|
|
|
|
|
SENDFN(record) { |
|
430
|
0
|
0
|
|
|
|
|
if (!SvROK(val)) SERR("expected a hashref"); |
|
431
|
0
|
|
|
|
|
|
SV *sv = SvRV(val); |
|
432
|
0
|
|
|
|
|
|
SvGETMAGIC(sv); |
|
433
|
0
|
0
|
|
|
|
|
if (SvTYPE(sv) != SVt_PVHV) SERR("expected a hashref"); |
|
434
|
0
|
|
|
|
|
|
HV *hv = (HV *)sv; |
|
435
|
|
|
|
|
|
|
|
|
436
|
0
|
|
|
|
|
|
fustr_writebeU(32, out, ctx->record.info->nattrs); |
|
437
|
|
|
|
|
|
|
I32 i; |
|
438
|
0
|
0
|
|
|
|
|
for (i=0; irecord.info->nattrs; i++) { |
|
439
|
0
|
|
|
|
|
|
fustr_writebeI(32, out, ctx->record.info->attrs[i].oid); |
|
440
|
0
|
|
|
|
|
|
SV **rsv = hv_fetch(hv, ctx->record.info->attrs[i].name.n, -strlen(ctx->record.info->attrs[i].name.n), 0); |
|
441
|
0
|
0
|
|
|
|
|
if (!rsv || !*rsv) { |
|
|
|
0
|
|
|
|
|
|
|
442
|
0
|
|
|
|
|
|
fustr_writebeI(32, out, -1); |
|
443
|
0
|
|
|
|
|
|
continue; |
|
444
|
|
|
|
|
|
|
} |
|
445
|
0
|
|
|
|
|
|
sv = *rsv; |
|
446
|
0
|
|
|
|
|
|
SvGETMAGIC(sv); |
|
447
|
0
|
0
|
|
|
|
|
if (!SvOK(sv)) { |
|
448
|
0
|
|
|
|
|
|
fustr_writebeI(32, out, -1); |
|
449
|
0
|
|
|
|
|
|
continue; |
|
450
|
|
|
|
|
|
|
} |
|
451
|
0
|
0
|
|
|
|
|
size_t lenoff = fustr_len(out); |
|
452
|
0
|
|
|
|
|
|
fustr_write(out, "\0\0\0\0", 4); |
|
453
|
0
|
|
|
|
|
|
ctx->record.tio[i].send(aTHX_ ctx->record.tio+i, sv, out); |
|
454
|
0
|
0
|
|
|
|
|
fu_tobeU(32, fustr_start(out) + lenoff, fustr_len(out) - lenoff - 4); |
|
|
|
0
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
} |
|
456
|
0
|
|
|
|
|
|
} |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
|
|
459
|
0
|
|
|
|
|
|
RECVFN(perlcb) { |
|
460
|
0
|
|
|
|
|
|
dSP; |
|
461
|
|
|
|
|
|
|
|
|
462
|
0
|
|
|
|
|
|
ENTER; |
|
463
|
0
|
|
|
|
|
|
SAVETMPS; |
|
464
|
|
|
|
|
|
|
|
|
465
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
466
|
0
|
0
|
|
|
|
|
mXPUSHs(newSVpvn(buf, len)); |
|
467
|
0
|
|
|
|
|
|
PUTBACK; |
|
468
|
0
|
|
|
|
|
|
call_sv(ctx->cb, G_SCALAR); |
|
469
|
0
|
|
|
|
|
|
SPAGAIN; |
|
470
|
|
|
|
|
|
|
|
|
471
|
0
|
|
|
|
|
|
SV *ret = POPs; |
|
472
|
0
|
|
|
|
|
|
SvREFCNT_inc(ret); |
|
473
|
0
|
|
|
|
|
|
PUTBACK; |
|
474
|
|
|
|
|
|
|
|
|
475
|
0
|
0
|
|
|
|
|
FREETMPS; |
|
476
|
0
|
|
|
|
|
|
LEAVE; |
|
477
|
0
|
|
|
|
|
|
return ret; |
|
478
|
|
|
|
|
|
|
} |
|
479
|
|
|
|
|
|
|
|
|
480
|
0
|
|
|
|
|
|
SENDFN(perlcb) { |
|
481
|
0
|
|
|
|
|
|
dSP; |
|
482
|
|
|
|
|
|
|
|
|
483
|
0
|
|
|
|
|
|
ENTER; |
|
484
|
0
|
|
|
|
|
|
SAVETMPS; |
|
485
|
|
|
|
|
|
|
|
|
486
|
0
|
0
|
|
|
|
|
PUSHMARK(SP); |
|
487
|
0
|
0
|
|
|
|
|
XPUSHs(val); |
|
488
|
|
|
|
|
|
|
|
|
489
|
0
|
|
|
|
|
|
PUTBACK; |
|
490
|
0
|
|
|
|
|
|
call_sv(ctx->cb, G_SCALAR); |
|
491
|
0
|
|
|
|
|
|
SPAGAIN; |
|
492
|
|
|
|
|
|
|
|
|
493
|
0
|
|
|
|
|
|
SV *ret = POPs; |
|
494
|
0
|
|
|
|
|
|
PUTBACK; |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
STRLEN len; |
|
497
|
0
|
|
|
|
|
|
const char *buf = SvPV(ret, len); |
|
498
|
0
|
|
|
|
|
|
fustr_write(out, buf, len); |
|
499
|
|
|
|
|
|
|
|
|
500
|
0
|
0
|
|
|
|
|
FREETMPS; |
|
501
|
0
|
|
|
|
|
|
LEAVE; |
|
502
|
0
|
|
|
|
|
|
} |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
|
|
505
|
0
|
|
|
|
|
|
RECVFN(inet) { /* Also works for cidr */ |
|
506
|
|
|
|
|
|
|
char tmp[128]; |
|
507
|
0
|
0
|
|
|
|
|
if (len < 8) RERR("input data too short"); |
|
508
|
|
|
|
|
|
|
// 0: ip_family, 1: mask_bits, 2: is_cidr, 3: addrsize, 4: address |
|
509
|
|
|
|
|
|
|
|
|
510
|
0
|
0
|
|
|
|
|
if (buf[0] == 2) { /* INET */ |
|
511
|
0
|
0
|
|
|
|
|
RLEN(8); |
|
512
|
0
|
0
|
|
|
|
|
if (!inet_ntop(AF_INET, buf+4, tmp, sizeof(tmp)-1)) RERR("%s", strerror(errno)); |
|
513
|
0
|
0
|
|
|
|
|
} else if (buf[0] == 3) { /* INET6 */ |
|
514
|
0
|
0
|
|
|
|
|
RLEN(20); |
|
515
|
0
|
0
|
|
|
|
|
if (!inet_ntop(AF_INET6, buf+4, tmp, sizeof(tmp)-1)) RERR("%s", strerror(errno)); |
|
516
|
0
|
|
|
|
|
|
} else RERR("unknown address type"); |
|
517
|
|
|
|
|
|
|
|
|
518
|
0
|
0
|
|
|
|
|
if (buf[2] || buf[1] != (buf[0] == 2 ? 32 : (char)128)) |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
519
|
0
|
|
|
|
|
|
return newSVpvf("%s/%d", tmp, (unsigned char)buf[1]); |
|
520
|
0
|
|
|
|
|
|
return newSVpv(tmp, 0); |
|
521
|
|
|
|
|
|
|
} |
|
522
|
|
|
|
|
|
|
|
|
523
|
0
|
|
|
|
|
|
SENDFN(inet) { |
|
524
|
|
|
|
|
|
|
char tmp[128]; |
|
525
|
|
|
|
|
|
|
STRLEN len; |
|
526
|
0
|
|
|
|
|
|
const char *in = SvPV(val, len); |
|
527
|
0
|
0
|
|
|
|
|
if (len >= sizeof(tmp)) SERR("input too long"); |
|
528
|
0
|
0
|
|
|
|
|
char family = strchr(in, ':') ? 3 : 2; |
|
529
|
0
|
0
|
|
|
|
|
char *wr = fustr_write_buf(out, family == 2 ? 8 : 20); |
|
530
|
0
|
|
|
|
|
|
unsigned char *mask = (unsigned char*)wr+1; |
|
531
|
0
|
|
|
|
|
|
wr[0] = family; |
|
532
|
0
|
0
|
|
|
|
|
*mask = family == 2 ? 32 : 128; |
|
533
|
0
|
|
|
|
|
|
wr[2] = ctx->oid == 650; |
|
534
|
0
|
0
|
|
|
|
|
wr[3] = family == 2 ? 4 : 16; |
|
535
|
|
|
|
|
|
|
|
|
536
|
0
|
|
|
|
|
|
char *slash = strchr(in, '/'); |
|
537
|
0
|
0
|
|
|
|
|
if (slash && slash - in < 100) { |
|
|
|
0
|
|
|
|
|
|
|
538
|
0
|
|
|
|
|
|
memcpy(tmp, in, slash - in); |
|
539
|
0
|
|
|
|
|
|
tmp[slash - in] = 0; |
|
540
|
0
|
|
|
|
|
|
in = tmp; |
|
541
|
|
|
|
|
|
|
} |
|
542
|
0
|
0
|
|
|
|
|
if (inet_pton(family == 2 ? AF_INET : AF_INET6, in, wr+4) != 1) |
|
|
|
0
|
|
|
|
|
|
|
543
|
0
|
|
|
|
|
|
SERR("invalid address"); |
|
544
|
|
|
|
|
|
|
|
|
545
|
0
|
0
|
|
|
|
|
if (slash) { |
|
546
|
0
|
|
|
|
|
|
UV uv = 129; |
|
547
|
0
|
0
|
|
|
|
|
if (!grok_atoUV(slash+1, &uv, NULL) || uv > *mask) |
|
|
|
0
|
|
|
|
|
|
|
548
|
0
|
|
|
|
|
|
SERR("invalid mask"); |
|
549
|
0
|
|
|
|
|
|
*mask = uv; |
|
550
|
|
|
|
|
|
|
} |
|
551
|
0
|
|
|
|
|
|
} |
|
552
|
|
|
|
|
|
|
|
|
553
|
0
|
|
|
|
|
|
RECVFN(uuid) { |
|
554
|
0
|
0
|
|
|
|
|
RLEN(16); |
|
555
|
|
|
|
|
|
|
char tmp[64]; |
|
556
|
0
|
|
|
|
|
|
char *out = tmp; |
|
557
|
0
|
|
|
|
|
|
const unsigned char *in = (const unsigned char *)buf; |
|
558
|
|
|
|
|
|
|
int i; |
|
559
|
0
|
0
|
|
|
|
|
for (i=0; i<16; i++) { |
|
560
|
0
|
0
|
|
|
|
|
if (i == 4 || i == 6 || i == 8 || i == 10) *out++ = '-'; |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
561
|
0
|
|
|
|
|
|
*out++ = PL_hexdigit[(in[i] >> 4) & 0x0f]; |
|
562
|
0
|
|
|
|
|
|
*out++ = PL_hexdigit[in[i] & 0x0f]; |
|
563
|
|
|
|
|
|
|
} |
|
564
|
0
|
|
|
|
|
|
*out = '\0'; |
|
565
|
0
|
|
|
|
|
|
return newSVpv(tmp, 0); |
|
566
|
|
|
|
|
|
|
} |
|
567
|
|
|
|
|
|
|
|
|
568
|
0
|
|
|
|
|
|
SENDFN(uuid) { |
|
569
|
0
|
|
|
|
|
|
const char *in = SvPV_nolen(val); |
|
570
|
0
|
|
|
|
|
|
int bytes = 0; |
|
571
|
0
|
|
|
|
|
|
unsigned char dig = 0x10; |
|
572
|
0
|
0
|
|
|
|
|
if (*in == '{') in++; |
|
573
|
0
|
0
|
|
|
|
|
for (; *in; in++) { |
|
574
|
0
|
0
|
|
|
|
|
if (*in == '}') break; |
|
575
|
0
|
0
|
|
|
|
|
if (dig == 0x10 && *in == '-') continue; |
|
|
|
0
|
|
|
|
|
|
|
576
|
0
|
|
|
|
|
|
int x = fu_hexdig(*in); |
|
577
|
0
|
0
|
|
|
|
|
if (x > 0x10) SERR("invalid UUID"); |
|
578
|
0
|
0
|
|
|
|
|
if (bytes >= 16) SERR("invalid UUID"); |
|
579
|
0
|
0
|
|
|
|
|
if (dig == 0x10) dig = x; |
|
580
|
|
|
|
|
|
|
else { |
|
581
|
0
|
|
|
|
|
|
fustr_write_ch(out, (dig << 4) + x); |
|
582
|
0
|
|
|
|
|
|
bytes++; |
|
583
|
0
|
|
|
|
|
|
dig = 0x10; |
|
584
|
|
|
|
|
|
|
} |
|
585
|
|
|
|
|
|
|
} |
|
586
|
0
|
0
|
|
|
|
|
if (dig != 0x10 || bytes != 16) SERR("invalid UUID"); |
|
|
|
0
|
|
|
|
|
|
|
587
|
0
|
|
|
|
|
|
} |
|
588
|
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
/* Postgres uses 2000-01-01 as epoch, we stick with POSIX 1970-01-01 */ |
|
590
|
|
|
|
|
|
|
#define UNIX_PG_EPOCH (10957*86400) |
|
591
|
|
|
|
|
|
|
|
|
592
|
0
|
|
|
|
|
|
RECVFN(timestamp) { |
|
593
|
0
|
0
|
|
|
|
|
RLEN(8); |
|
594
|
0
|
|
|
|
|
|
return newSVnv(((NV)fu_frombeI(64, buf) / 1000000) + UNIX_PG_EPOCH); |
|
595
|
|
|
|
|
|
|
} |
|
596
|
|
|
|
|
|
|
|
|
597
|
0
|
|
|
|
|
|
SENDFN(timestamp) { |
|
598
|
0
|
0
|
|
|
|
|
if (!looks_like_number(val)) SERR("expected a number"); |
|
599
|
0
|
|
|
|
|
|
I64 ts = (SvNV(val) - UNIX_PG_EPOCH) * 1000000; |
|
600
|
0
|
|
|
|
|
|
fustr_writebeI(64, out, ts); |
|
601
|
0
|
|
|
|
|
|
} |
|
602
|
|
|
|
|
|
|
|
|
603
|
0
|
|
|
|
|
|
RECVFN(date) { |
|
604
|
0
|
0
|
|
|
|
|
RLEN(4); |
|
605
|
0
|
|
|
|
|
|
return newSVuv(((UV)fu_frombeI(32, buf)) * 86400 + UNIX_PG_EPOCH); |
|
606
|
|
|
|
|
|
|
} |
|
607
|
|
|
|
|
|
|
|
|
608
|
0
|
|
|
|
|
|
SENDFN(date) { |
|
609
|
0
|
0
|
|
|
|
|
if (!looks_like_number(val)) SERR("expected a number"); |
|
610
|
0
|
|
|
|
|
|
fustr_writebeI(32, out, (SvIV(val) - UNIX_PG_EPOCH) / 86400); |
|
611
|
0
|
|
|
|
|
|
} |
|
612
|
|
|
|
|
|
|
|
|
613
|
0
|
|
|
|
|
|
RECVFN(date_str) { |
|
614
|
0
|
0
|
|
|
|
|
RLEN(4); |
|
615
|
0
|
|
|
|
|
|
time_t ts = ((time_t)fu_frombeI(32, buf)) * 86400 + UNIX_PG_EPOCH; |
|
616
|
|
|
|
|
|
|
struct tm tm; |
|
617
|
0
|
|
|
|
|
|
gmtime_r(&ts, &tm); |
|
618
|
0
|
|
|
|
|
|
return newSVpvf("%04d-%02d-%02d", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday); |
|
619
|
|
|
|
|
|
|
} |
|
620
|
|
|
|
|
|
|
|
|
621
|
0
|
|
|
|
|
|
SENDFN(date_str) { |
|
622
|
|
|
|
|
|
|
int year, month, day; |
|
623
|
0
|
0
|
|
|
|
|
if (sscanf(SvPV_nolen(val), "%4d-%2d-%2d", &year, &month, &day) != 3) SERR("invalid date format"); |
|
624
|
|
|
|
|
|
|
/* Can't use mktime() hackery here because libc has no UTC variant. Code |
|
625
|
|
|
|
|
|
|
* below is adapted from PostgreSQL date2j() instead. */ |
|
626
|
0
|
0
|
|
|
|
|
if (month > 2) { |
|
627
|
0
|
|
|
|
|
|
month += 1; |
|
628
|
0
|
|
|
|
|
|
year += 4800; |
|
629
|
|
|
|
|
|
|
} else { |
|
630
|
0
|
|
|
|
|
|
month += 13; |
|
631
|
0
|
|
|
|
|
|
year += 4799; |
|
632
|
|
|
|
|
|
|
} |
|
633
|
0
|
|
|
|
|
|
int century = year / 100; |
|
634
|
0
|
|
|
|
|
|
int v = year * 365 - 32167; |
|
635
|
0
|
|
|
|
|
|
v += year / 4 - century + century / 4; |
|
636
|
0
|
|
|
|
|
|
v += 7834 * month / 256 + day; |
|
637
|
0
|
|
|
|
|
|
v -= 2451545; /* Julian -> Postgres */ |
|
638
|
0
|
|
|
|
|
|
fustr_writebeI(32, out, v); |
|
639
|
0
|
|
|
|
|
|
} |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
#undef UNIX_PG_EPOCH |
|
642
|
|
|
|
|
|
|
|
|
643
|
0
|
|
|
|
|
|
RECVFN(time) { |
|
644
|
0
|
0
|
|
|
|
|
RLEN(8); |
|
645
|
0
|
|
|
|
|
|
return newSVnv(((NV)fu_frombeI(64, buf)) / 1000000); |
|
646
|
|
|
|
|
|
|
} |
|
647
|
|
|
|
|
|
|
|
|
648
|
0
|
|
|
|
|
|
SENDFN(time) { |
|
649
|
0
|
0
|
|
|
|
|
if (!looks_like_number(val)) SERR("expected a number"); |
|
650
|
0
|
|
|
|
|
|
fustr_writebeI(64, out, SvNV(val) * 1000000); |
|
651
|
0
|
|
|
|
|
|
} |
|
652
|
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
/* VNDB types */ |
|
656
|
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
const char vndbtag_alpha[] = "\0""abcdefghijklmnopqrstuvwxyz?????"; |
|
658
|
|
|
|
|
|
|
|
|
659
|
0
|
|
|
|
|
|
static I16 vndbtag_parse(char **str) { |
|
660
|
0
|
|
|
|
|
|
I16 tag = 0; |
|
661
|
0
|
0
|
|
|
|
|
if (**str >= 'a' && **str <= 'z') { |
|
|
|
0
|
|
|
|
|
|
|
662
|
0
|
|
|
|
|
|
tag = (**str - 'a' + 1) << 10; |
|
663
|
0
|
|
|
|
|
|
(*str)++; |
|
664
|
0
|
0
|
|
|
|
|
if (**str >= 'a' && **str <= 'z') { |
|
|
|
0
|
|
|
|
|
|
|
665
|
0
|
|
|
|
|
|
tag |= (**str - 'a' + 1) << 5; |
|
666
|
0
|
|
|
|
|
|
(*str)++; |
|
667
|
0
|
0
|
|
|
|
|
if (**str >= 'a' && **str <= 'z') { |
|
|
|
0
|
|
|
|
|
|
|
668
|
0
|
|
|
|
|
|
tag |= **str - 'a' + 1; |
|
669
|
0
|
|
|
|
|
|
(*str)++; |
|
670
|
|
|
|
|
|
|
} |
|
671
|
|
|
|
|
|
|
} |
|
672
|
|
|
|
|
|
|
} |
|
673
|
0
|
|
|
|
|
|
return tag; |
|
674
|
|
|
|
|
|
|
} |
|
675
|
|
|
|
|
|
|
|
|
676
|
0
|
|
|
|
|
|
void vndbtag_fmt(I16 tag, char *out) { |
|
677
|
0
|
|
|
|
|
|
out[0] = vndbtag_alpha[(tag >> 10) & 31]; |
|
678
|
0
|
|
|
|
|
|
out[1] = vndbtag_alpha[(tag >> 5) & 31]; |
|
679
|
0
|
|
|
|
|
|
out[2] = vndbtag_alpha[(tag >> 0) & 31]; |
|
680
|
0
|
|
|
|
|
|
out[3] = 0; |
|
681
|
0
|
|
|
|
|
|
} |
|
682
|
|
|
|
|
|
|
|
|
683
|
0
|
|
|
|
|
|
RECVFN(vndbtag) { |
|
684
|
0
|
0
|
|
|
|
|
RLEN(2); |
|
685
|
0
|
|
|
|
|
|
SV *r = newSV(4); |
|
686
|
0
|
|
|
|
|
|
SvPOK_only(r); |
|
687
|
0
|
|
|
|
|
|
vndbtag_fmt(fu_frombeI(16, buf), SvPVX(r)); |
|
688
|
0
|
|
|
|
|
|
SvCUR_set(r, strlen(SvPVX(r))); |
|
689
|
0
|
|
|
|
|
|
return r; |
|
690
|
|
|
|
|
|
|
} |
|
691
|
|
|
|
|
|
|
|
|
692
|
0
|
|
|
|
|
|
SENDFN(vndbtag) { |
|
693
|
0
|
|
|
|
|
|
char *t = SvPV_nolen(val); |
|
694
|
0
|
|
|
|
|
|
I16 v = vndbtag_parse(&t); |
|
695
|
0
|
0
|
|
|
|
|
if (*t) SERR("Invalid vndbtag: '%s'", SvPV_nolen(val)); |
|
696
|
0
|
|
|
|
|
|
fustr_writebeI(16, out, v); |
|
697
|
0
|
|
|
|
|
|
} |
|
698
|
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
#define VNDBID2_MAXNUM (((I64)1<<48)-1) |
|
701
|
|
|
|
|
|
|
|
|
702
|
0
|
|
|
|
|
|
RECVFN(vndbid) { |
|
703
|
0
|
0
|
|
|
|
|
RLEN(8); |
|
704
|
0
|
|
|
|
|
|
I64 v = fu_frombeI(64, buf); |
|
705
|
|
|
|
|
|
|
char tbuf[4]; |
|
706
|
0
|
|
|
|
|
|
vndbtag_fmt(v >> 48, tbuf); |
|
707
|
0
|
|
|
|
|
|
return newSVpvf("%s%"UVuf, tbuf, (UV)(v & VNDBID2_MAXNUM)); |
|
708
|
|
|
|
|
|
|
} |
|
709
|
|
|
|
|
|
|
|
|
710
|
0
|
|
|
|
|
|
SENDFN(vndbid) { |
|
711
|
0
|
|
|
|
|
|
char *ostr = SvPV_nolen(val), *str = ostr; |
|
712
|
|
|
|
|
|
|
UV num; |
|
713
|
0
|
|
|
|
|
|
I16 tag = vndbtag_parse(&str); |
|
714
|
0
|
0
|
|
|
|
|
if (!grok_atoUV(str, &num, NULL) || num > VNDBID2_MAXNUM) SERR("invalid vndbid '%s'", ostr); |
|
|
|
0
|
|
|
|
|
|
|
715
|
0
|
|
|
|
|
|
fustr_writebeI(64, out, ((I64)tag)<<48 | num); |
|
716
|
0
|
|
|
|
|
|
} |
|
717
|
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
#undef SIV |
|
720
|
|
|
|
|
|
|
#undef RLEN |
|
721
|
|
|
|
|
|
|
#undef RECVFN |
|
722
|
|
|
|
|
|
|
#undef SENDFN |
|
723
|
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
/* List of types we handle directly in this module. |
|
728
|
|
|
|
|
|
|
Ideally, this includes everything returned by: |
|
729
|
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
SELECT oid, typname, typelem, typreceive |
|
731
|
|
|
|
|
|
|
FROM pg_type t |
|
732
|
|
|
|
|
|
|
WHERE typtype = 'b' |
|
733
|
|
|
|
|
|
|
AND typnamespace = 'pg_catalog'::regnamespace |
|
734
|
|
|
|
|
|
|
AND (typelem = 0 OR EXISTS(SELECT 1 FROM pg_type e WHERE e.oid = t.typelem AND e.typtype = 'b')) |
|
735
|
|
|
|
|
|
|
ORDER by oid |
|
736
|
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
(i.e. all base types and arrays of base types) |
|
738
|
|
|
|
|
|
|
Plus hopefully a bunch of common extension types. |
|
739
|
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
The "reg#" types are a bit funny: the Postgres devs obviously realized that |
|
741
|
|
|
|
|
|
|
writing JOINs is cumbersome, so they hacked together a numeric identifier |
|
742
|
|
|
|
|
|
|
type that automatically resolves to a string when formatted as text, or |
|
743
|
|
|
|
|
|
|
performs a lookup in the database when parsing text. In the text format, you |
|
744
|
|
|
|
|
|
|
don't get to see the numeric identifier, but sadly that conversion is not |
|
745
|
|
|
|
|
|
|
performed in the byte format so we're dealing with numbers instead. Oh well. |
|
746
|
|
|
|
|
|
|
Not worth writing custom lookup code for, users will have to adapt. |
|
747
|
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
Ordered by oid to support binary search. |
|
749
|
|
|
|
|
|
|
(name is only used when formatting error messages, for now) */ |
|
750
|
|
|
|
|
|
|
#define BUILTINS \ |
|
751
|
|
|
|
|
|
|
B( 16, "bool", bool )\ |
|
752
|
|
|
|
|
|
|
B( 17, "bytea", bytea )\ |
|
753
|
|
|
|
|
|
|
B( 18, "char", char )\ |
|
754
|
|
|
|
|
|
|
B( 19, "name", text )\ |
|
755
|
|
|
|
|
|
|
B( 20, "int8", int8 )\ |
|
756
|
|
|
|
|
|
|
B( 21, "int2", int2 )\ |
|
757
|
|
|
|
|
|
|
A( 22, "int2vector", 21 )\ |
|
758
|
|
|
|
|
|
|
B( 23, "int4", int4 )\ |
|
759
|
|
|
|
|
|
|
B( 24, "regproc", uint4 )\ |
|
760
|
|
|
|
|
|
|
B( 25, "text", text )\ |
|
761
|
|
|
|
|
|
|
B( 26, "oid", uint4 )\ |
|
762
|
|
|
|
|
|
|
/* 27 tid: u32 block, u16 offset; represent as hash? */ \ |
|
763
|
|
|
|
|
|
|
B( 28, "xid", uint4 )\ |
|
764
|
|
|
|
|
|
|
B( 29, "cid", uint4 )\ |
|
765
|
|
|
|
|
|
|
A( 30, "oidvector", 26 )\ |
|
766
|
|
|
|
|
|
|
B( 114, "json", json )\ |
|
767
|
|
|
|
|
|
|
B( 142, "xml", text )\ |
|
768
|
|
|
|
|
|
|
A( 143, "_xml", 142 )\ |
|
769
|
|
|
|
|
|
|
B( 194, "pg_node_tree", text ) /* can't be used as a bind param */\ |
|
770
|
|
|
|
|
|
|
A( 199, "_json", 114 )\ |
|
771
|
|
|
|
|
|
|
A( 271, "_xid8", 5069 )\ |
|
772
|
|
|
|
|
|
|
/* 600 point */\ |
|
773
|
|
|
|
|
|
|
/* 601 lseg */\ |
|
774
|
|
|
|
|
|
|
/* 602 path */\ |
|
775
|
|
|
|
|
|
|
/* 603 box */\ |
|
776
|
|
|
|
|
|
|
/* 604 polygon */\ |
|
777
|
|
|
|
|
|
|
/* 628 line */\ |
|
778
|
|
|
|
|
|
|
A( 629, "_line", 628 )\ |
|
779
|
|
|
|
|
|
|
B( 650, "cidr", inet )\ |
|
780
|
|
|
|
|
|
|
A( 651, "_cidr", 650 )\ |
|
781
|
|
|
|
|
|
|
B( 700, "float4", float4)\ |
|
782
|
|
|
|
|
|
|
B( 701, "float8", float8)\ |
|
783
|
|
|
|
|
|
|
/* 718 circle */\ |
|
784
|
|
|
|
|
|
|
A( 719, "_circle", 718 )\ |
|
785
|
|
|
|
|
|
|
/* 774 macaddr8 */\ |
|
786
|
|
|
|
|
|
|
A( 775, "_macaddr8", 774 )\ |
|
787
|
|
|
|
|
|
|
/* 790 money */\ |
|
788
|
|
|
|
|
|
|
A( 791, "_money", 790 )\ |
|
789
|
|
|
|
|
|
|
/* 829 macaddr */\ |
|
790
|
|
|
|
|
|
|
B( 869, "inet", inet )\ |
|
791
|
|
|
|
|
|
|
A( 1000, "_bool", 16 )\ |
|
792
|
|
|
|
|
|
|
A( 1001, "_bytea", 17 )\ |
|
793
|
|
|
|
|
|
|
A( 1002, "_char", 18 )\ |
|
794
|
|
|
|
|
|
|
A( 1003, "_name", 19 )\ |
|
795
|
|
|
|
|
|
|
A( 1005, "_int2", 21 )\ |
|
796
|
|
|
|
|
|
|
A( 1006, "_int2vector", 22 )\ |
|
797
|
|
|
|
|
|
|
A( 1007, "_int4", 23 )\ |
|
798
|
|
|
|
|
|
|
A( 1008, "_regproc", 24 )\ |
|
799
|
|
|
|
|
|
|
A( 1009, "_text", 25 )\ |
|
800
|
|
|
|
|
|
|
A( 1010, "_tid", 27 )\ |
|
801
|
|
|
|
|
|
|
A( 1011, "_xid", 28 )\ |
|
802
|
|
|
|
|
|
|
A( 1012, "_cid", 29 )\ |
|
803
|
|
|
|
|
|
|
A( 1013, "_oidvector", 30 )\ |
|
804
|
|
|
|
|
|
|
A( 1014, "_bpchar", 1042 )\ |
|
805
|
|
|
|
|
|
|
A( 1015, "_varchar", 1043 )\ |
|
806
|
|
|
|
|
|
|
A( 1016, "_int8", 20 )\ |
|
807
|
|
|
|
|
|
|
A( 1017, "_point", 600 )\ |
|
808
|
|
|
|
|
|
|
A( 1018, "_lseg", 601 )\ |
|
809
|
|
|
|
|
|
|
A( 1019, "_path", 602 )\ |
|
810
|
|
|
|
|
|
|
A( 1020, "_box", 603 )\ |
|
811
|
|
|
|
|
|
|
A( 1021, "_float4", 700 )\ |
|
812
|
|
|
|
|
|
|
A( 1022, "_float8", 701 )\ |
|
813
|
|
|
|
|
|
|
A( 1027, "_polygon", 604 )\ |
|
814
|
|
|
|
|
|
|
A( 1028, "_oid", 26 )\ |
|
815
|
|
|
|
|
|
|
/* 1033 aclitem, does not support send/recv */\ |
|
816
|
|
|
|
|
|
|
/* A( 1034, "_aclitem", 1033 ) */\ |
|
817
|
|
|
|
|
|
|
A( 1040, "_macaddr", 829 )\ |
|
818
|
|
|
|
|
|
|
A( 1041, "_inet", 869 )\ |
|
819
|
|
|
|
|
|
|
B( 1042, "bpchar", text )\ |
|
820
|
|
|
|
|
|
|
B( 1043, "varchar", text )\ |
|
821
|
|
|
|
|
|
|
B( 1082, "date", date )\ |
|
822
|
|
|
|
|
|
|
B( 1083, "time", time )\ |
|
823
|
|
|
|
|
|
|
B( 1114, "timestamp", timestamp)\ |
|
824
|
|
|
|
|
|
|
A( 1115, "_timestamp", 1114 )\ |
|
825
|
|
|
|
|
|
|
A( 1182, "_date", 1082 )\ |
|
826
|
|
|
|
|
|
|
A( 1183, "_time", 1083 )\ |
|
827
|
|
|
|
|
|
|
B( 1184, "timestamptz", timestamp)\ |
|
828
|
|
|
|
|
|
|
A( 1185, "_timestamptz", 1184 )\ |
|
829
|
|
|
|
|
|
|
/* 1186 interval */\ |
|
830
|
|
|
|
|
|
|
A( 1187, "_interval", 1186 )\ |
|
831
|
|
|
|
|
|
|
A( 1231, "_numeric", 1700 )\ |
|
832
|
|
|
|
|
|
|
/* 1266 timetz */\ |
|
833
|
|
|
|
|
|
|
A( 1270, "_timetz", 1266 )\ |
|
834
|
|
|
|
|
|
|
/* 1560 bit */\ |
|
835
|
|
|
|
|
|
|
A( 1561, "_bit", 1560 )\ |
|
836
|
|
|
|
|
|
|
/* 1562 varbit */\ |
|
837
|
|
|
|
|
|
|
A( 1563, "_varbit", 1562 )\ |
|
838
|
|
|
|
|
|
|
/* 1700 numeric */\ |
|
839
|
|
|
|
|
|
|
B( 1790, "refcursor", text )\ |
|
840
|
|
|
|
|
|
|
A( 2201, "_refcursor", 1790 )\ |
|
841
|
|
|
|
|
|
|
B( 2202, "regprocedure", uint4 )\ |
|
842
|
|
|
|
|
|
|
B( 2203, "regoper", uint4 )\ |
|
843
|
|
|
|
|
|
|
B( 2204, "regoperator", uint4 )\ |
|
844
|
|
|
|
|
|
|
B( 2205, "regclass", uint4 )\ |
|
845
|
|
|
|
|
|
|
B( 2206, "regtype", uint4 )\ |
|
846
|
|
|
|
|
|
|
A( 2207, "_regprocedure", 2202 )\ |
|
847
|
|
|
|
|
|
|
A( 2208, "_regoper", 2203 )\ |
|
848
|
|
|
|
|
|
|
A( 2209, "_regoperator", 2204 )\ |
|
849
|
|
|
|
|
|
|
A( 2210, "_regclass", 2205 )\ |
|
850
|
|
|
|
|
|
|
A( 2211, "_regtype", 2206 )\ |
|
851
|
|
|
|
|
|
|
B( 2278, "void", void )\ |
|
852
|
|
|
|
|
|
|
A( 2949, "_txid_snapshot", 2970 )\ |
|
853
|
|
|
|
|
|
|
B( 2950, "uuid", uuid )\ |
|
854
|
|
|
|
|
|
|
A( 2951, "_uuid", 2950 )\ |
|
855
|
|
|
|
|
|
|
/* 2970 txid_snapshot: same as pg_snapshot */\ |
|
856
|
|
|
|
|
|
|
/* 3220 pg_lsn: uint64 with custom formatting */\ |
|
857
|
|
|
|
|
|
|
A( 3221, "_pg_lsn", 3220 )\ |
|
858
|
|
|
|
|
|
|
/* 3361 pg_ndistinct */\ |
|
859
|
|
|
|
|
|
|
/* 3402 pg_dependencies */\ |
|
860
|
|
|
|
|
|
|
/* 3614 tsvector */\ |
|
861
|
|
|
|
|
|
|
/* 3615 tsquery */\ |
|
862
|
|
|
|
|
|
|
/* 3642 gtsvector, does not support send/recv */\ |
|
863
|
|
|
|
|
|
|
A( 3643, "_tsvector", 3614 )\ |
|
864
|
|
|
|
|
|
|
/*A( 3644, "_gtsvector", 3642 )*/\ |
|
865
|
|
|
|
|
|
|
A( 3645, "_tsquery", 3615 )\ |
|
866
|
|
|
|
|
|
|
B( 3734, "regconfig", uint4 )\ |
|
867
|
|
|
|
|
|
|
A( 3735, "_regconfig", 3734 )\ |
|
868
|
|
|
|
|
|
|
B( 3769, "regdictionary", uint4 )\ |
|
869
|
|
|
|
|
|
|
A( 3770, "_regdictionary", 3769 )\ |
|
870
|
|
|
|
|
|
|
B( 3802, "jsonb", jsonb )\ |
|
871
|
|
|
|
|
|
|
A( 3807, "_jsonb", 3802 )\ |
|
872
|
|
|
|
|
|
|
B( 4072, "jsonpath", jsonpath)\ |
|
873
|
|
|
|
|
|
|
A( 4073, "_jsonpath", 4072 )\ |
|
874
|
|
|
|
|
|
|
B( 4089, "regnamespace", uint4 )\ |
|
875
|
|
|
|
|
|
|
A( 4090, "_regnamespace", 4089 )\ |
|
876
|
|
|
|
|
|
|
B( 4096, "regrole", uint4 )\ |
|
877
|
|
|
|
|
|
|
A( 4097, "_regrole", 4096 )\ |
|
878
|
|
|
|
|
|
|
B( 4191, "regcollation", uint4 )\ |
|
879
|
|
|
|
|
|
|
A( 4192, "_regcollation", 4191 )\ |
|
880
|
|
|
|
|
|
|
/* 4600 pg_brin_bloom_summary */\ |
|
881
|
|
|
|
|
|
|
/* 4601 pg_brin_minmax_multi_summary */\ |
|
882
|
|
|
|
|
|
|
/* 5017 pg_mcv_list */\ |
|
883
|
|
|
|
|
|
|
/* 5038 pg_snapshot: int4 nxip, int8 xmin, int8 xmax, int8 xip */\ |
|
884
|
|
|
|
|
|
|
A( 5039, "_pg_snapshot", 5038 )\ |
|
885
|
|
|
|
|
|
|
B( 5069, "xid8", uint8 ) |
|
886
|
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
static const fupg_type fupg_builtin[] = { |
|
888
|
|
|
|
|
|
|
#define B(oid, name, fun) { oid, 0, {name"\0"}, fupg_send_##fun, fupg_recv_##fun }, |
|
889
|
|
|
|
|
|
|
#define A(oid, name, eoid) { oid, eoid, {name"\0"}, fupg_send_array, fupg_recv_array }, |
|
890
|
|
|
|
|
|
|
BUILTINS |
|
891
|
|
|
|
|
|
|
#undef B |
|
892
|
|
|
|
|
|
|
#undef A |
|
893
|
|
|
|
|
|
|
}; |
|
894
|
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
#undef BUILTINS |
|
896
|
|
|
|
|
|
|
#define FUPG_BUILTIN (sizeof(fupg_builtin) / sizeof(fupg_type)) |
|
897
|
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
/* List of types identified by name */ |
|
900
|
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
#define DYNOID\ |
|
902
|
|
|
|
|
|
|
T("vndbtag", vndbtag)\ |
|
903
|
|
|
|
|
|
|
T("vndbid", vndbid) |
|
904
|
|
|
|
|
|
|
|
|
905
|
|
|
|
|
|
|
static const fupg_type fupg_dynoid[] = { |
|
906
|
|
|
|
|
|
|
#define T(name, fun) { 0, 0, {name"\0"}, fupg_send_##fun, fupg_recv_##fun }, |
|
907
|
|
|
|
|
|
|
DYNOID |
|
908
|
|
|
|
|
|
|
#undef T |
|
909
|
|
|
|
|
|
|
}; |
|
910
|
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
#undef DYNOID |
|
912
|
|
|
|
|
|
|
#define FUPG_DYNOID (sizeof(fupg_dynoid) / sizeof(fupg_type)) |
|
913
|
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
/* List of special types for use with set_type() */ |
|
916
|
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
#define SPECIALS\ |
|
918
|
|
|
|
|
|
|
T("$date_str", date_str)\ |
|
919
|
|
|
|
|
|
|
T("$hex", hex ) |
|
920
|
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
static const fupg_type fupg_specials[] = { |
|
922
|
|
|
|
|
|
|
#define T(name, fun) { 0, 0, {name"\0"}, fupg_send_##fun, fupg_recv_##fun }, |
|
923
|
|
|
|
|
|
|
SPECIALS |
|
924
|
|
|
|
|
|
|
#undef T |
|
925
|
|
|
|
|
|
|
}; |
|
926
|
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
#undef SPECIALS |
|
928
|
|
|
|
|
|
|
#define FUPG_SPECIALS (sizeof(fupg_specials) / sizeof(fupg_type)) |
|
929
|
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
static const fupg_type fupg_type_perlcb = { 0, 0, {"$perl_cb"}, fupg_send_perlcb, fupg_recv_perlcb }; |
|
932
|
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
|
|
934
|
0
|
|
|
|
|
|
static const fupg_type *fupg_type_byoid(const fupg_type *list, int len, Oid oid) { |
|
935
|
0
|
|
|
|
|
|
int i, b = 0, e = len-1; |
|
936
|
0
|
0
|
|
|
|
|
while (b <= e) { |
|
937
|
0
|
|
|
|
|
|
i = b + (e - b)/2; |
|
938
|
0
|
0
|
|
|
|
|
if (list[i].oid == oid) return list+i; |
|
939
|
0
|
0
|
|
|
|
|
if (list[i].oid < oid) b = i+1; |
|
940
|
0
|
|
|
|
|
|
else e = i-1; |
|
941
|
|
|
|
|
|
|
} |
|
942
|
0
|
|
|
|
|
|
return NULL; |
|
943
|
|
|
|
|
|
|
} |
|
944
|
|
|
|
|
|
|
|
|
945
|
0
|
|
|
|
|
|
static const fupg_type *fupg_builtin_byoid(Oid oid) { |
|
946
|
0
|
|
|
|
|
|
return fupg_type_byoid(fupg_builtin, FUPG_BUILTIN, oid); |
|
947
|
|
|
|
|
|
|
} |
|
948
|
|
|
|
|
|
|
|
|
949
|
0
|
|
|
|
|
|
static const fupg_type *fupg_dynoid_byname(const char *name) { |
|
950
|
|
|
|
|
|
|
size_t i; |
|
951
|
0
|
0
|
|
|
|
|
for (i=0; i
|
|
952
|
0
|
0
|
|
|
|
|
if (strcmp(fupg_dynoid[i].name.n, name) == 0) |
|
953
|
0
|
|
|
|
|
|
return fupg_dynoid+i; |
|
954
|
0
|
|
|
|
|
|
return NULL; |
|
955
|
|
|
|
|
|
|
} |
|
956
|
|
|
|
|
|
|
|
|
957
|
0
|
|
|
|
|
|
static const fupg_type *fupg_builtin_byname(const char *name) { |
|
958
|
|
|
|
|
|
|
size_t i; |
|
959
|
0
|
|
|
|
|
|
const fupg_type *r = fupg_dynoid_byname(name); |
|
960
|
0
|
0
|
|
|
|
|
if (r) return r; |
|
961
|
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
/* XXX: Can use binary search here if the list of specials grows. |
|
963
|
|
|
|
|
|
|
* That list does not have to be ordered by oid. */ |
|
964
|
0
|
0
|
|
|
|
|
for (i=0; i
|
|
965
|
0
|
0
|
|
|
|
|
if (strcmp(fupg_specials[i].name.n, name) == 0) |
|
966
|
0
|
|
|
|
|
|
return fupg_specials+i; |
|
967
|
|
|
|
|
|
|
|
|
968
|
0
|
0
|
|
|
|
|
for (i=0; i
|
|
969
|
0
|
0
|
|
|
|
|
if (strcmp(fupg_builtin[i].name.n, name) == 0) |
|
970
|
0
|
|
|
|
|
|
return fupg_builtin+i; |
|
971
|
0
|
|
|
|
|
|
return NULL; |
|
972
|
|
|
|
|
|
|
} |