Start background writer during archive recovery. Background writer now performs
[PostgreSQL.git] / contrib / hstore / hstore_io.c
blobfaf24d97c62d2d413c20a4d3dc2f8d916c438038
1 /*
2 * $PostgreSQL$
3 */
4 #include "postgres.h"
6 #include <ctype.h>
8 #include "hstore.h"
10 PG_MODULE_MAGIC;
12 typedef struct
14 char *begin;
15 char *ptr;
16 char *cur;
17 char *word;
18 int wordlen;
20 Pairs *pairs;
21 int pcur;
22 int plen;
23 } HSParser;
25 #define RESIZEPRSBUF \
26 do { \
27 if ( state->cur - state->word + 1 >= state->wordlen ) \
28 { \
29 int4 clen = state->cur - state->word; \
30 state->wordlen *= 2; \
31 state->word = (char*)repalloc( (void*)state->word, state->wordlen ); \
32 state->cur = state->word + clen; \
33 } \
34 } while (0)
37 #define GV_WAITVAL 0
38 #define GV_INVAL 1
39 #define GV_INESCVAL 2
40 #define GV_WAITESCIN 3
41 #define GV_WAITESCESCIN 4
43 static bool
44 get_val(HSParser * state, bool ignoreeq, bool *escaped)
46 int st = GV_WAITVAL;
48 state->wordlen = 32;
49 state->cur = state->word = palloc(state->wordlen);
50 *escaped = false;
52 while (1)
54 if (st == GV_WAITVAL)
56 if (*(state->ptr) == '"')
58 *escaped = true;
59 st = GV_INESCVAL;
61 else if (*(state->ptr) == '\0')
63 return false;
65 else if (*(state->ptr) == '=' && !ignoreeq)
67 elog(ERROR, "Syntax error near '%c' at postion %d", *(state->ptr), (int4) (state->ptr - state->begin));
69 else if (*(state->ptr) == '\\')
71 st = GV_WAITESCIN;
73 else if (!isspace((unsigned char) *(state->ptr)))
75 *(state->cur) = *(state->ptr);
76 state->cur++;
77 st = GV_INVAL;
80 else if (st == GV_INVAL)
82 if (*(state->ptr) == '\\')
84 st = GV_WAITESCIN;
86 else if (*(state->ptr) == '=' && !ignoreeq)
88 state->ptr--;
89 return true;
91 else if (*(state->ptr) == ',' && ignoreeq)
93 state->ptr--;
94 return true;
96 else if (isspace((unsigned char) *(state->ptr)))
98 return true;
100 else if (*(state->ptr) == '\0')
102 state->ptr--;
103 return true;
105 else
107 RESIZEPRSBUF;
108 *(state->cur) = *(state->ptr);
109 state->cur++;
112 else if (st == GV_INESCVAL)
114 if (*(state->ptr) == '\\')
116 st = GV_WAITESCESCIN;
118 else if (*(state->ptr) == '"')
120 return true;
122 else if (*(state->ptr) == '\0')
124 elog(ERROR, "Unexpected end of string");
126 else
128 RESIZEPRSBUF;
129 *(state->cur) = *(state->ptr);
130 state->cur++;
133 else if (st == GV_WAITESCIN)
135 if (*(state->ptr) == '\0')
136 elog(ERROR, "Unexpected end of string");
137 RESIZEPRSBUF;
138 *(state->cur) = *(state->ptr);
139 state->cur++;
140 st = GV_INVAL;
142 else if (st == GV_WAITESCESCIN)
144 if (*(state->ptr) == '\0')
145 elog(ERROR, "Unexpected end of string");
146 RESIZEPRSBUF;
147 *(state->cur) = *(state->ptr);
148 state->cur++;
149 st = GV_INESCVAL;
151 else
152 elog(ERROR, "Unknown state %d at position line %d in file '%s'", st, __LINE__, __FILE__);
154 state->ptr++;
157 return false;
160 #define WKEY 0
161 #define WVAL 1
162 #define WEQ 2
163 #define WGT 3
164 #define WDEL 4
167 static void
168 parse_hstore(HSParser * state)
170 int st = WKEY;
171 bool escaped = false;
173 state->plen = 16;
174 state->pairs = (Pairs *) palloc(sizeof(Pairs) * state->plen);
175 state->pcur = 0;
176 state->ptr = state->begin;
177 state->word = NULL;
179 while (1)
181 if (st == WKEY)
183 if (!get_val(state, false, &escaped))
184 return;
185 if (state->pcur >= state->plen)
187 state->plen *= 2;
188 state->pairs = (Pairs *) repalloc(state->pairs, sizeof(Pairs) * state->plen);
190 state->pairs[state->pcur].key = state->word;
191 state->pairs[state->pcur].keylen = state->cur - state->word;
192 state->pairs[state->pcur].val = NULL;
193 state->word = NULL;
194 st = WEQ;
196 else if (st == WEQ)
198 if (*(state->ptr) == '=')
200 st = WGT;
202 else if (*(state->ptr) == '\0')
204 elog(ERROR, "Unexpected end of string");
206 else if (!isspace((unsigned char) *(state->ptr)))
208 elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int4) (state->ptr - state->begin));
211 else if (st == WGT)
213 if (*(state->ptr) == '>')
215 st = WVAL;
217 else if (*(state->ptr) == '\0')
219 elog(ERROR, "Unexpected end of string");
221 else
223 elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int4) (state->ptr - state->begin));
226 else if (st == WVAL)
228 if (!get_val(state, true, &escaped))
229 elog(ERROR, "Unexpected end of string");
230 state->pairs[state->pcur].val = state->word;
231 state->pairs[state->pcur].vallen = state->cur - state->word;
232 state->pairs[state->pcur].isnull = false;
233 state->pairs[state->pcur].needfree = true;
234 if (state->cur - state->word == 4 && !escaped)
236 state->word[4] = '\0';
237 if (0 == pg_strcasecmp(state->word, "null"))
238 state->pairs[state->pcur].isnull = true;
240 state->word = NULL;
241 state->pcur++;
242 st = WDEL;
244 else if (st == WDEL)
246 if (*(state->ptr) == ',')
248 st = WKEY;
250 else if (*(state->ptr) == '\0')
252 return;
254 else if (!isspace((unsigned char) *(state->ptr)))
256 elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int4) (state->ptr - state->begin));
259 else
260 elog(ERROR, "Unknown state %d at line %d in file '%s'", st, __LINE__, __FILE__);
262 state->ptr++;
267 comparePairs(const void *a, const void *b)
269 if (((Pairs *) a)->keylen == ((Pairs *) b)->keylen)
271 int res = strncmp(
272 ((Pairs *) a)->key,
273 ((Pairs *) b)->key,
274 ((Pairs *) a)->keylen
277 if (res)
278 return res;
280 /* guarantee that needfree will be later */
281 if (((Pairs *) b)->needfree == ((Pairs *) a)->needfree)
282 return 0;
283 else if (((Pairs *) a)->needfree)
284 return 1;
285 else
286 return -1;
288 return (((Pairs *) a)->keylen > ((Pairs *) b)->keylen) ? 1 : -1;
292 uniquePairs(Pairs * a, int4 l, int4 *buflen)
294 Pairs *ptr,
295 *res;
297 *buflen = 0;
298 if (l < 2)
300 if (l == 1)
301 *buflen = a->keylen + ((a->isnull) ? 0 : a->vallen);
302 return l;
305 qsort((void *) a, l, sizeof(Pairs), comparePairs);
306 ptr = a + 1;
307 res = a;
308 while (ptr - a < l)
310 if (ptr->keylen == res->keylen && strncmp(ptr->key, res->key, res->keylen) == 0)
312 if (ptr->needfree)
314 pfree(ptr->key);
315 pfree(ptr->val);
318 else
320 *buflen += res->keylen + ((res->isnull) ? 0 : res->vallen);
321 res++;
322 memcpy(res, ptr, sizeof(Pairs));
325 ptr++;
328 *buflen += res->keylen + ((res->isnull) ? 0 : res->vallen);
329 return res + 1 - a;
332 static void
333 freeHSParse(HSParser * state)
335 int i;
337 if (state->word)
338 pfree(state->word);
339 for (i = 0; i < state->pcur; i++)
340 if (state->pairs[i].needfree)
342 if (state->pairs[i].key)
343 pfree(state->pairs[i].key);
344 if (state->pairs[i].val)
345 pfree(state->pairs[i].val);
347 pfree(state->pairs);
350 PG_FUNCTION_INFO_V1(hstore_in);
351 Datum hstore_in(PG_FUNCTION_ARGS);
352 Datum
353 hstore_in(PG_FUNCTION_ARGS)
355 HSParser state;
356 int4 len,
357 buflen,
359 HStore *out;
360 HEntry *entries;
361 char *ptr;
363 state.begin = PG_GETARG_CSTRING(0);
365 parse_hstore(&state);
367 if (state.pcur == 0)
369 freeHSParse(&state);
370 len = CALCDATASIZE(0, 0);
371 out = palloc(len);
372 SET_VARSIZE(out, len);
373 out->size = 0;
374 PG_RETURN_POINTER(out);
377 state.pcur = uniquePairs(state.pairs, state.pcur, &buflen);
379 len = CALCDATASIZE(state.pcur, buflen);
380 out = palloc(len);
381 SET_VARSIZE(out, len);
382 out->size = state.pcur;
384 entries = ARRPTR(out);
385 ptr = STRPTR(out);
387 for (i = 0; i < out->size; i++)
389 entries[i].keylen = state.pairs[i].keylen;
390 entries[i].pos = ptr - STRPTR(out);
391 memcpy(ptr, state.pairs[i].key, state.pairs[i].keylen);
392 ptr += entries[i].keylen;
394 entries[i].valisnull = state.pairs[i].isnull;
395 if (entries[i].valisnull)
396 entries[i].vallen = 4; /* null */
397 else
399 entries[i].vallen = state.pairs[i].vallen;
400 memcpy(ptr, state.pairs[i].val, state.pairs[i].vallen);
401 ptr += entries[i].vallen;
405 freeHSParse(&state);
406 PG_RETURN_POINTER(out);
409 static char *
410 cpw(char *dst, char *src, int len)
412 char *ptr = src;
414 while (ptr - src < len)
416 if (*ptr == '"' || *ptr == '\\')
417 *dst++ = '\\';
418 *dst++ = *ptr++;
420 return dst;
423 PG_FUNCTION_INFO_V1(hstore_out);
424 Datum hstore_out(PG_FUNCTION_ARGS);
425 Datum
426 hstore_out(PG_FUNCTION_ARGS)
428 HStore *in = PG_GETARG_HS(0);
429 int buflen,
431 char *out,
432 *ptr;
433 char *base = STRPTR(in);
434 HEntry *entries = ARRPTR(in);
436 if (in->size == 0)
438 out = palloc(1);
439 *out = '\0';
440 PG_FREE_IF_COPY(in, 0);
441 PG_RETURN_CSTRING(out);
444 buflen = (4 /* " */ + 2 /* => */ + 2 /* , */ ) * in->size +
445 2 /* esc */ * (VARSIZE(in) - CALCDATASIZE(in->size, 0));
447 out = ptr = palloc(buflen);
448 for (i = 0; i < in->size; i++)
450 *ptr++ = '"';
451 ptr = cpw(ptr, base + entries[i].pos, entries[i].keylen);
452 *ptr++ = '"';
453 *ptr++ = '=';
454 *ptr++ = '>';
455 if (entries[i].valisnull)
457 *ptr++ = 'N';
458 *ptr++ = 'U';
459 *ptr++ = 'L';
460 *ptr++ = 'L';
462 else
464 *ptr++ = '"';
465 ptr = cpw(ptr, base + entries[i].pos + entries[i].keylen, entries[i].vallen);
466 *ptr++ = '"';
469 if (i + 1 != in->size)
471 *ptr++ = ',';
472 *ptr++ = ' ';
475 *ptr = '\0';
477 PG_FREE_IF_COPY(in, 0);
478 PG_RETURN_CSTRING(out);