1 /*
2 * Copyright 2001-2004 Brandon Long
3 * All Rights Reserved.
4 *
5 * ClearSilver Templating System
6 *
7 * This code is made available under the terms of the ClearSilver License.
8 * http://www.clearsilver.net/license.hdf
9 *
10 */
11 /*
12 * The wdb is a wrapper around the sleepycat db library which adds
13 * a relatively simple data/column definition. In many respects, this
14 * code is way more complicated than it ever needed to be, but it works,
15 * so I'm loathe to "fix" it.
16 *
17 * One of they key features of this is the ability to update the
18 * "schema" of the wdb without changing all of the existing rows of
19 * data.
20 */
21
22 #include "cs_config.h"
23
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <limits.h>
30 #include <db.h>
31 #include <ctype.h>
32
33 #include "neo_misc.h"
34 #include "neo_err.h"
35 #include "dict.h"
36 #include "ulist.h"
37 #include "skiplist.h"
38 #include "wdb.h"
39
40
41 #define DEFN_VERSION_1 "WDB-VERSION-200006301"
42 #define PACK_VERSION_1 1
43
string_rstrip(char * s)44 static void string_rstrip (char *s)
45 {
46 size_t len;
47
48 len = strlen(s);
49 len--;
50 while (len > 0 && isspace(s[len]))
51 {
52 s[len] = '\0';
53 len--;
54 }
55 return;
56 }
57
to_hex(unsigned char ch,unsigned char * s)58 static int to_hex (unsigned char ch, unsigned char *s)
59 {
60 unsigned int uvalue = ch;
61
62 s[1] = "0123456789ABCDEF"[uvalue % 16];
63 uvalue = (uvalue / 16); s[0] = "0123456789ABCDEF"[uvalue % 16];
64 return 0;
65 }
66
67 int Index_hex[128] = {
68 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
69 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
70 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
71 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1,
72 -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
73 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
74 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
75 };
76
77 #define hexval(c) Index_hex[(unsigned int)(c)]
78
79 /* Encoding means any non-printable characters and : and % */
wdb_encode_str_alloc(const char * s,char ** o)80 static NEOERR *wdb_encode_str_alloc (const char *s, char **o)
81 {
82 int x = 0;
83 int c = 0;
84 char *out;
85 unsigned char ch;
86
87 while (s[x])
88 {
89 ch = (unsigned char) s[x];
90 if ((ch < 32) || (ch > 127) || (ch == ':') || (ch == '%') || isspace(ch))
91 c++;
92 x++;
93 }
94
95 out = (char *) malloc (sizeof (char) * (x + c * 3) + 1);
96 if (out == NULL)
97 return nerr_raise (NERR_NOMEM,
98 "Unable to allocate memory for encoding %s", s);
99
100 x = 0;
101 c = 0;
102
103 *o = out;
104
105 while (s[x])
106 {
107 ch = (unsigned char) s[x];
108 if ((ch < 32) || (ch > 127) || (ch == ':') || (ch == '%') || isspace(ch))
109 {
110 out[c++] = '%';
111 to_hex (s[x], &out[c]);
112 c+=2;
113 }
114 else
115 {
116 out[c++] = s[x];
117 }
118 x++;
119 }
120 out[c] = '\0';
121
122 return STATUS_OK;
123 }
124
wdb_decode_str_alloc(const char * s,char ** o)125 static NEOERR *wdb_decode_str_alloc (const char *s, char **o)
126 {
127 int x = 0;
128 int c = 0;
129 char *out;
130 unsigned char ch;
131
132
133 x = strlen(s);
134 /* Overkill, the decoded string will be smaller */
135 out = (char *) malloc (sizeof (char) * (x + 1));
136 if (out == NULL)
137 return nerr_raise (NERR_NOMEM,
138 "Unable to allocate memory for decoding %s", s);
139
140 x = 0;
141 c = 0;
142
143 while (s[x])
144 {
145 if (s[x] == '%')
146 {
147 x++;
148 ch = hexval(s[x]) << 4;
149 x++;
150 ch |= hexval(s[x]);
151 out[c++] = ch;
152 }
153 else
154 {
155 out[c++] = s[x];
156 }
157 x++;
158 }
159 out[c] = '\0';
160 *o = out;
161
162 return STATUS_OK;
163 }
164
free_cb(void * value,void * rock)165 static void free_cb (void *value, void *rock)
166 {
167 free (value);
168 }
169
free_col_cb(void * value,void * rock)170 static void free_col_cb (void *value, void *rock)
171 {
172 WDBColumn *col;
173
174 col = (WDBColumn *)value;
175
176 free (col->name);
177 free (col);
178 }
179
wdb_alloc(WDB ** wdb,int flags)180 static NEOERR *wdb_alloc (WDB **wdb, int flags)
181 {
182 WDB *my_wdb;
183 NEOERR *err = STATUS_OK;
184
185 my_wdb = (WDB *) calloc (1, sizeof (WDB));
186
187 if (my_wdb == NULL)
188 return nerr_raise (NERR_NOMEM, "Unable to allocate memory for WDB");
189
190 do
191 {
192 err = dictCreate (&(my_wdb->attrs), 0, 2, 5, 0, 0, free_cb, NULL);
193 if (err != STATUS_OK) break;
194 err = dictCreate (&(my_wdb->cols), 0, 2, 5, 0, 0, free_col_cb, NULL);
195 if (err != STATUS_OK) break;
196 err = uListInit (&(my_wdb->cols_l), 0, 0);
197 if (err != STATUS_OK) break;
198 err = skipNewList(&(my_wdb->ondisk), 0, 4, 2, 0, NULL, NULL);
199 if (err != STATUS_OK) break;
200
201 *wdb = my_wdb;
202
203 return STATUS_OK;
204 } while (0);
205
206 wdb_destroy(&my_wdb);
207 return nerr_pass (err);
208 }
209
210
211 #define STATE_REQUIRED 1
212 #define STATE_ATTRIBUTES 2
213 #define STATE_COLUMN_DEF 3
214
wdb_load_defn_v1(WDB * wdb,FILE * fp)215 static NEOERR *wdb_load_defn_v1 (WDB *wdb, FILE *fp)
216 {
217 char line[1024];
218 int state = 1;
219 char *k, *v;
220 NEOERR *err = STATUS_OK;
221 int colindex = 1;
222 WDBColumn *col;
223
224 while (fgets(line, sizeof(line), fp) != NULL)
225 {
226 string_rstrip(line);
227 switch (state)
228 {
229 case STATE_REQUIRED:
230 if (!strcmp(line, "attributes"))
231 state = STATE_ATTRIBUTES;
232 else if (!strcmp(line, "columns"))
233 state = STATE_COLUMN_DEF;
234 else
235 {
236 k = line;
237 v = strchr(line, ':');
238 /* HACK */
239 if (!strcmp(k, "name") && ((v == NULL) || (v[1] == '\0')))
240 {
241 v = "dNone";
242 }
243 else
244 {
245 if (v == NULL)
246 return nerr_raise (NERR_PARSE, "Error parsing %s", line);
247 if (v[1] == '\0')
248 return nerr_raise (NERR_PARSE, "Error parsing %s", line);
249 }
250 v[0] = '\0';
251 v++;
252 if (!strcmp(k, "key"))
253 {
254 err = wdb_decode_str_alloc (v, &(wdb->key));
255 if (err) return nerr_pass(err);
256 }
257 else if (!strcmp(k, "name"))
258 {
259 err = wdb_decode_str_alloc (v, &(wdb->name));
260 if (err) return nerr_pass(err);
261 }
262 else if (!strcmp(k, "ondisk"))
263 {
264 wdb->last_ondisk = atoi (v);
265 }
266 }
267 break;
268 case STATE_ATTRIBUTES:
269 if (!strcmp(line, "columns"))
270 state = STATE_COLUMN_DEF;
271 else
272 {
273 k = line;
274 v = strchr(line, ':');
275 if (v == NULL)
276 return nerr_raise (NERR_PARSE, "Error parsing %s", line);
277 v[0] = '\0';
278 v++;
279 err = wdb_decode_str_alloc (k, &k);
280 if (err) return nerr_pass(err);
281 err = wdb_decode_str_alloc (v, &v);
282 if (err) return nerr_pass(err);
283 err = dictSetValue(wdb->attrs, k, v);
284 free(k);
285 if (err)
286 return nerr_pass_ctx(err, "Error parsing %s", line);
287 }
288 break;
289 case STATE_COLUMN_DEF:
290 k = line;
291 v = strchr(line, ':');
292 if (v == NULL)
293 return nerr_raise (NERR_PARSE, "Error parsing %s", line);
294 if (v[1] == '\0')
295 return nerr_raise (NERR_PARSE, "Error parsing %s", line);
296 v[0] = '\0';
297 v++;
298 err = wdb_decode_str_alloc (k, &k);
299 if (err) return nerr_pass(err);
300 col = (WDBColumn *) calloc (1, sizeof (WDBColumn));
301 col->name = k;
302 col->inmem_index = colindex++;
303 col->type = *v;
304 v+=2;
305 col->ondisk_index = atoi(v);
306 err = dictSetValue(wdb->cols, k, col);
307 if (err)
308 return nerr_raise (NERR_PARSE, "Error parsing %s", line);
309 err = uListAppend(wdb->cols_l, col);
310 if (err) return nerr_pass(err);
311 /* stupid skiplist will assert */
312 if (col->ondisk_index == 0)
313 {
314 return nerr_raise (NERR_ASSERT, "Invalid ondisk mapping for %s", k);
315 }
316 err = skipInsert (wdb->ondisk, col->ondisk_index,
317 (void *)(col->inmem_index), 0);
318 if (err)
319 return nerr_pass_ctx(err, "Unable to update ondisk mapping for %s", k);
320 break;
321 default:
322 return nerr_raise (NERR_ASSERT, "Invalid state %d", state);
323 }
324 }
325 return STATUS_OK;
326 }
327
wdb_save_defn_v1(WDB * wdb,FILE * fp)328 static NEOERR *wdb_save_defn_v1 (WDB *wdb, FILE *fp)
329 {
330 NEOERR *err = STATUS_OK;
331 WDBColumn *col;
332 char *s = NULL;
333 char *key = NULL;
334 int r, x, len;
335 char *k = NULL;
336 char *v = NULL;
337
338 /* Write version string */
339 r = fprintf (fp, "%s\n", DEFN_VERSION_1);
340 if (!r) goto save_err;
341
342 err = wdb_encode_str_alloc (wdb->name, &s);
343 if (err) goto save_err;
344 r = fprintf (fp, "name:%s\n", s);
345 if (!r) goto save_err;
346 free (s);
347
348 err = wdb_encode_str_alloc (wdb->key, &s);
349 if (err != STATUS_OK) goto save_err;
350 r = fprintf (fp, "key:%s\n", s);
351 if (!r) goto save_err;
352 free (s);
353 s = NULL;
354
355 r = fprintf (fp, "ondisk:%d\n", wdb->last_ondisk);
356 if (!r) goto save_err;
357
358 r = fprintf (fp, "attributes\n");
359 if (!r) goto save_err;
360 key = NULL;
361 s = (char *) dictNext (wdb->attrs, &key, NULL);
362 while (s)
363 {
364 err = wdb_encode_str_alloc (key, &k);
365 if (err != STATUS_OK) goto save_err;
366 err = wdb_encode_str_alloc (s, &v);
367 if (err != STATUS_OK) goto save_err;
368 r = fprintf (fp, "%s:%s\n", k, v);
369 if (!r) goto save_err;
370 free (k);
371 free (v);
372 k = NULL;
373 v = NULL;
374 s = (char *) dictNext (wdb->attrs, &key, NULL);
375 }
376 s = NULL;
377
378 r = fprintf (fp, "columns\n");
379 if (!r) goto save_err;
380
381 len = uListLength(wdb->cols_l);
382
383 for (x = 0; x < len; x++)
384 {
385 err = uListGet (wdb->cols_l, x, (void *)&col);
386 if (err) goto save_err;
387 err = wdb_encode_str_alloc (col->name, &s);
388 if (err != STATUS_OK) goto save_err;
389 r = fprintf (fp, "%s:%c:%d\n", s, col->type, col->ondisk_index);
390 if (!r) goto save_err;
391 free(s);
392 s = NULL;
393 }
394
395 return STATUS_OK;
396
397 save_err:
398 if (s != NULL) free (s);
399 if (k != NULL) free (k);
400 if (v != NULL) free (v);
401 if (err == STATUS_OK) return nerr_pass(err);
402 return nerr_raise (r, "Unable to save defn");
403 }
404
wdb_load_defn(WDB * wdb,const char * name)405 static NEOERR *wdb_load_defn (WDB *wdb, const char *name)
406 {
407 char path[_POSIX_PATH_MAX];
408 char line[1024];
409 FILE *fp;
410 NEOERR *err = STATUS_OK;
411
412 snprintf (path, sizeof(path), "%s.wdf", name);
413 fp = fopen (path, "r");
414 if (fp == NULL)
415 {
416 if (errno == ENOENT)
417 return nerr_raise (NERR_NOT_FOUND, "Unable to open defn %s", name);
418 return nerr_raise_errno (NERR_IO, "Unable to open defn %s", name);
419 }
420
421 /* Read Version string */
422 if (fgets (line, sizeof(line), fp) == NULL)
423 {
424 fclose(fp);
425 return nerr_raise_errno (NERR_IO, "Unable to read defn %s", name);
426 }
427 string_rstrip(line);
428
429 if (!strcmp(line, DEFN_VERSION_1))
430 {
431 err = wdb_load_defn_v1(wdb, fp);
432 fclose(fp);
433 if (err) return nerr_pass(err);
434 }
435 else
436 {
437 fclose(fp);
438 return nerr_raise (NERR_ASSERT, "Unknown defn version %s: %s", line, name);
439 }
440
441 wdb->table_version = rand();
442
443 return STATUS_OK;
444 }
445
wdb_save_defn(WDB * wdb,const char * name)446 static NEOERR *wdb_save_defn (WDB *wdb, const char *name)
447 {
448 char path[_POSIX_PATH_MAX];
449 char path2[_POSIX_PATH_MAX];
450 FILE *fp;
451 NEOERR *err = STATUS_OK;
452 int r;
453
454 snprintf (path, sizeof(path), "%s.wdf.new", name);
455 snprintf (path2, sizeof(path2), "%s.wdf", name);
456 fp = fopen (path, "w");
457 if (fp == NULL)
458 return nerr_raise_errno (NERR_IO, "Unable to open defn %s", name);
459
460 err = wdb_save_defn_v1 (wdb, fp);
461 fclose (fp);
462 if (err != STATUS_OK)
463 {
464 unlink (path);
465 return nerr_pass (err);
466 }
467
468 r = unlink (path2);
469 if (r == -1 && errno != ENOENT)
470 return nerr_raise_errno (NERR_IO, "Unable to unlink %s", path2);
471 r = link (path, path2);
472 if (r == -1)
473 return nerr_raise_errno (NERR_IO, "Unable to link %s to %s", path, path2);
474 r = unlink (path);
475
476 wdb->defn_dirty = 0;
477 wdb->table_version = rand();
478
479 return STATUS_OK;
480 }
481
wdb_open(WDB ** wdb,const char * name,int flags)482 NEOERR *wdb_open (WDB **wdb, const char *name, int flags)
483 {
484 WDB *my_wdb;
485 char path[_POSIX_PATH_MAX];
486 NEOERR *err = STATUS_OK;
487 int r;
488
489 *wdb = NULL;
490
491 err = wdb_alloc (&my_wdb, flags);
492 if (err) return nerr_pass(err);
493
494 my_wdb->path = strdup (name);
495 if (err)
496 {
497 wdb_destroy (&my_wdb);
498 return nerr_pass(err);
499 }
500
501 err = wdb_load_defn (my_wdb, name);
502 if (err)
503 {
504 wdb_destroy (&my_wdb);
505 return nerr_pass(err);
506 }
507
508 snprintf (path, sizeof(path), "%s.wdb", name);
509 r = db_open(path, DB_BTREE, 0, 0, NULL, NULL, &(my_wdb->db));
510 if (r)
511 {
512 wdb_destroy (&my_wdb);
513 return nerr_raise (NERR_DB, "Unable to open database %s: %d", name, r);
514 }
515
516 *wdb = my_wdb;
517
518 return STATUS_OK;
519 }
520
wdb_save(WDB * wdb)521 NEOERR *wdb_save (WDB *wdb)
522 {
523 if (wdb->defn_dirty)
524 {
525 wdb_save_defn (wdb, wdb->path);
526 }
527 return STATUS_OK;
528 }
529
wdb_destroy(WDB ** wdb)530 void wdb_destroy (WDB **wdb)
531 {
532 WDB *my_wdb;
533
534 my_wdb = *wdb;
535
536 if (my_wdb == NULL) return;
537
538 if (my_wdb->defn_dirty)
539 {
540 wdb_save_defn (my_wdb, my_wdb->path);
541 }
542
543 if (my_wdb->attrs != NULL)
544 {
545 dictDestroy (my_wdb->attrs);
546 }
547
548 if (my_wdb->cols != NULL)
549 {
550 dictDestroy (my_wdb->cols);
551 }
552
553 if (my_wdb->cols_l != NULL)
554 {
555 uListDestroy(&(my_wdb->cols_l), 0);
556 }
557
558 if (my_wdb->ondisk != NULL)
559 {
560 skipFreeList(my_wdb->ondisk);
561 }
562
563 if (my_wdb->db != NULL)
564 {
565 my_wdb->db->close (my_wdb->db, 0);
566 my_wdb->db = NULL;
567 }
568
569 if (my_wdb->path != NULL)
570 {
571 free(my_wdb->path);
572 my_wdb->path = NULL;
573 }
574 if (my_wdb->name != NULL)
575 {
576 free(my_wdb->name);
577 my_wdb->name = NULL;
578 }
579 if (my_wdb->key != NULL)
580 {
581 free(my_wdb->key);
582 my_wdb->key = NULL;
583 }
584
585 free (my_wdb);
586 *wdb = NULL;
587
588 return;
589 }
590
591 #define PACK_UB4(pdata, plen, pmax, pn) \
592 { \
593 if (plen + 4 > pmax) \
594 { \
595 pmax *= 2; \
596 pdata = realloc ((void *)pdata, pmax); \
597 if (pdata == NULL) goto pack_err; \
598 } \
599 pdata[plen++] = (0x0ff & (pn >> 0)); \
600 pdata[plen++] = (0x0ff & (pn >> 8)); \
601 pdata[plen++] = (0x0ff & (pn >> 16)); \
602 pdata[plen++] = (0x0ff & (pn >> 24)); \
603 }
604
605 #define UNPACK_UB4(pdata, plen, pn, pd) \
606 { \
607 if (pn + 4 > plen) \
608 goto pack_err; \
609 pd = ((0x0ff & pdata[pn+0])<<0) | ((0x0ff & pdata[pn+1])<<8) | \
610 ((0x0ff & pdata[pn+2])<<16) | ((0x0ff & pdata[pn+3])<<24); \
611 pn+=4; \
612 }
613
614 #define PACK_BYTE(pdata, plen, pmax, pn) \
615 { \
616 if (plen + 1 > pmax) \
617 { \
618 pmax *= 2; \
619 pdata = realloc ((void *)pdata, pmax); \
620 if (pdata == NULL) goto pack_err; \
621 } \
622 pdata[plen++] = (0x0ff & (pn >> 0)); \
623 }
624
625 #define UNPACK_BYTE(pdata, plen, pn, pd) \
626 { \
627 if (pn + 1 > plen) \
628 goto pack_err; \
629 pd = pdata[pn++]; \
630 }
631
632 #define PACK_STRING(pdata, plen, pmax, dl, ds) \
633 { \
634 if (plen + 4 + dl > pmax) \
635 { \
636 while (plen + 4 + dl > pmax) \
637 pmax *= 2; \
638 pdata = realloc ((void *)pdata, pmax); \
639 if (pdata == NULL) goto pack_err; \
640 } \
641 pdata[plen++] = (0x0ff & (dl >> 0)); \
642 pdata[plen++] = (0x0ff & (dl >> 8)); \
643 pdata[plen++] = (0x0ff & (dl >> 16)); \
644 pdata[plen++] = (0x0ff & (dl >> 24)); \
645 memcpy (&pdata[plen], ds, dl); \
646 plen+=dl;\
647 }
648
649 #define UNPACK_STRING(pdata, plen, pn, ps) \
650 { \
651 int pl; \
652 if (pn + 4 > plen) \
653 goto pack_err; \
654 pl = ((0x0ff & pdata[pn+0])<<0) | ((0x0ff & pdata[pn+1])<<8) | \
655 ((0x0ff & pdata[pn+2])<<16) | ((0x0ff & pdata[pn+3])<<24); \
656 pn+=4; \
657 if (pl) \
658 { \
659 ps = (char *)malloc(sizeof(char)*(pl+1)); \
660 if (ps == NULL) \
661 goto pack_err; \
662 memcpy (ps, &pdata[pn], pl); \
663 ps[pl] = '\0'; \
664 pn += pl; \
665 } else { \
666 ps = NULL; \
667 } \
668 }
669
670 /* A VERSION_1 Row consists of the following data:
671 * UB4 VERSION
672 * UB4 DATA COUNT
673 * DATA where
674 * UB4 ONDISK INDEX
675 * UB1 TYPE
676 * if INT, then UB4
677 * if STR, then UB4 length and length UB1s
678 */
679
pack_row(WDB * wdb,WDBRow * row,void ** rdata,int * rdlen)680 static NEOERR *pack_row (WDB *wdb, WDBRow *row, void **rdata, int *rdlen)
681 {
682 char *data;
683 int x, len, dlen, dmax;
684 char *s;
685 int n;
686 WDBColumn *col;
687 NEOERR *err;
688
689 *rdata = NULL;
690 *rdlen = 0;
691 /* allocate */
692 data = (char *)malloc(sizeof (char) * 1024);
693 if (data == NULL)
694 return nerr_raise (NERR_NOMEM, "Unable to allocate memory to pack row");
695
696 dmax = 1024;
697 dlen = 0;
698
699 PACK_UB4 (data, dlen, dmax, PACK_VERSION_1);
700 /* PACK_UB4 (data, dlen, dmax, time(NULL)); */
701
702 len = uListLength(wdb->cols_l);
703 if (len > row->data_count)
704 len = row->data_count;
705 PACK_UB4 (data, dlen, dmax, len);
706
707 for (x = 0; x < len; x++)
708 {
709 err = uListGet (wdb->cols_l, x, (void *)&col);
710 if (err) goto pack_err;
711 PACK_UB4 (data, dlen, dmax, col->ondisk_index);
712 PACK_BYTE (data, dlen, dmax, col->type);
713 switch (col->type)
714 {
715 case WDB_TYPE_INT:
716 n = (int)(row->data[x]);
717 PACK_UB4 (data, dlen, dmax, n);
718 break;
719 case WDB_TYPE_STR:
720 s = (char *)(row->data[x]);
721 if (s == NULL)
722 {
723 s = "";
724 }
725 n = strlen(s);
726 PACK_STRING (data, dlen, dmax, n, s);
727 break;
728 default:
729 free (data);
730 return nerr_raise (NERR_ASSERT, "Unknown type %d", col->type);
731 }
732 }
733
734 *rdata = data;
735 *rdlen = dlen;
736 return STATUS_OK;
737
738 pack_err:
739 if (data != NULL)
740 free (data);
741 if (err == STATUS_OK)
742 return nerr_raise(NERR_NOMEM, "Unable to allocate memory for pack_row");
743 return nerr_pass(err);
744 }
745
unpack_row(WDB * wdb,void * rdata,int dlen,WDBRow * row)746 static NEOERR *unpack_row (WDB *wdb, void *rdata, int dlen, WDBRow *row)
747 {
748 unsigned char *data = rdata;
749 int version, n;
750 int count, x, ondisk_index, type, d_int, inmem_index;
751 char *s;
752
753 n = 0;
754
755 UNPACK_UB4(data, dlen, n, version);
756
757 switch (version)
758 {
759 case PACK_VERSION_1:
760 UNPACK_UB4(data, dlen, n, count);
761 for (x = 0; x<count; x++)
762 {
763 UNPACK_UB4 (data, dlen, n, ondisk_index);
764 UNPACK_BYTE (data, dlen, n, type);
765 inmem_index = (int) skipSearch (wdb->ondisk, ondisk_index, NULL);
766
767 switch (type)
768 {
769 case WDB_TYPE_INT:
770 UNPACK_UB4 (data, dlen, n, d_int);
771 if (inmem_index != 0)
772 row->data[inmem_index-1] = (void *) d_int;
773 break;
774 case WDB_TYPE_STR:
775 UNPACK_STRING (data, dlen, n, s);
776 if (inmem_index != 0)
777 row->data[inmem_index-1] = s;
778 break;
779 default:
780 return nerr_raise (NERR_ASSERT, "Unknown type %d for col %d", type, ondisk_index);
781 }
782 }
783 break;
784 default:
785 return nerr_raise (NERR_ASSERT, "Unknown version %d", version);
786 }
787
788 return STATUS_OK;
789 pack_err:
790 return nerr_raise(NERR_PARSE, "Unable to unpack row %s", row->key_value);
791 }
792
wdb_column_insert(WDB * wdb,int loc,const char * key,char type)793 NEOERR *wdb_column_insert (WDB *wdb, int loc, const char *key, char type)
794 {
795 NEOERR *err;
796 WDBColumn *col, *ocol;
797 int x, len;
798
799 col = (WDBColumn *) dictSearch (wdb->cols, key, NULL);
800
801 if (col != NULL)
802 return nerr_raise (NERR_DUPLICATE,
803 "Duplicate key %s:%d", key, col->inmem_index);
804
805 col = (WDBColumn *) calloc (1, sizeof (WDBColumn));
806 if (col == NULL)
807 {
808 return nerr_raise (NERR_NOMEM,
809 "Unable to allocate memory for creation of col %s:%d", key, loc);
810 }
811
812 col->name = strdup(key);
813 if (col->name == NULL)
814 {
815 free(col);
816 return nerr_raise (NERR_NOMEM,
817 "Unable to allocate memory for creation of col %s:%d", key, loc);
818 }
819 col->type = type;
820 col->ondisk_index = wdb->last_ondisk++;
821 /* -1 == append */
822 if (loc == -1)
823 {
824 err = dictSetValue(wdb->cols, key, col);
825 if (err)
826 {
827 free (col->name);
828 free (col);
829 return nerr_pass_ctx (err,
830 "Unable to insert for creation of col %s:%d", key, loc);
831 }
832 err = uListAppend (wdb->cols_l, (void *)col);
833 if (err) return nerr_pass(err);
834 x = uListLength (wdb->cols_l);
835 col->inmem_index = x;
836 err = skipInsert (wdb->ondisk, col->ondisk_index,
837 (void *)(col->inmem_index), 0);
838 if (err)
839 return nerr_pass_ctx (err, "Unable to update ondisk mapping for %s", key);
840 }
841 else
842 {
843 /* We are inserting this in middle, so the skipList ondisk is now
844 * invalid, as is the inmem_index for all cols */
845 err = dictSetValue(wdb->cols, key, col);
846 if (err)
847 {
848 free (col->name);
849 free (col);
850 return nerr_pass_ctx (err,
851 "Unable to insert for creation of col %s:%d", key, loc);
852 }
853 err = uListInsert (wdb->cols_l, loc, (void *)col);
854 if (err) return nerr_pass(err);
855 len = uListLength (wdb->cols_l);
856 /* Fix up inmem_index and ondisk skipList */
857 for (x = 0; x < len; x++)
858 {
859 err = uListGet (wdb->cols_l, x, (void *)&ocol);
860 if (err) return nerr_pass(err);
861 ocol->inmem_index = x + 1;
862 err = skipInsert (wdb->ondisk, ocol->ondisk_index,
863 (void *)(ocol->inmem_index), TRUE);
864 if (err)
865 return nerr_pass_ctx (err, "Unable to update ondisk mapping for %s", key);
866 }
867 }
868
869 wdb->defn_dirty = 1;
870 wdb->table_version = rand();
871
872 return STATUS_OK;
873 }
874
wdb_column_update(WDB * wdb,const char * oldkey,const char * newkey)875 NEOERR *wdb_column_update (WDB *wdb, const char *oldkey, const char *newkey)
876 {
877 WDBColumn *ocol, *col;
878 WDBColumn *vcol;
879 NEOERR *err = STATUS_OK;
880 int x, len, r;
881
882 ocol = (WDBColumn *) dictSearch (wdb->cols, oldkey, NULL);
883
884 if (ocol == NULL)
885 return nerr_raise (NERR_NOT_FOUND,
886 "Unable to find column for key %s", oldkey);
887
888 col = (WDBColumn *) calloc (1, sizeof (WDBColumn));
889 if (col == NULL)
890 {
891 return nerr_raise (NERR_NOMEM,
892 "Unable to allocate memory for column update %s", newkey);
893 }
894
895 *col = *ocol;
896 col->name = strdup(newkey);
897 if (col->name == NULL)
898 {
899 free(col);
900 return nerr_raise (NERR_NOMEM,
901 "Unable to allocate memory for column update %s", oldkey);
902 }
903 len = uListLength(wdb->cols_l);
904 for (x = 0; x < len; x++)
905 {
906 err = uListGet (wdb->cols_l, x, (void *)&vcol);
907 if (err) return nerr_pass(err);
908 if (!strcmp(vcol->name, oldkey))
909 {
910 err = uListSet (wdb->cols_l, x, (void *)col);
911 if (err) return nerr_pass(err);
912 break;
913 }
914 }
915 if (x>len)
916 {
917 return nerr_raise (NERR_ASSERT, "Unable to find cols_l for key %s", oldkey);
918 }
919
920 r = dictRemove (wdb->cols, oldkey); /* Only failure is key not found */
921 err = dictSetValue(wdb->cols, newkey, col);
922 if (err)
923 {
924 free (col->name);
925 free (col);
926 return nerr_pass_ctx (err,
927 "Unable to insert for update of col %s->%s", oldkey, newkey);
928 }
929
930 wdb->defn_dirty = 1;
931 wdb->table_version = rand();
932
933 return STATUS_OK;
934 }
935
wdb_column_delete(WDB * wdb,const char * name)936 NEOERR *wdb_column_delete (WDB *wdb, const char *name)
937 {
938 WDBColumn *col;
939 NEOERR *err = STATUS_OK;
940 int len, x, r;
941
942 len = uListLength(wdb->cols_l);
943 for (x = 0; x < len; x++)
944 {
945 err = uListGet (wdb->cols_l, x, (void *)&col);
946 if (err) return nerr_pass(err);
947 if (!strcmp(col->name, name))
948 {
949 err = uListDelete (wdb->cols_l, x, NULL);
950 if (err) return nerr_pass(err);
951 break;
952 }
953 }
954
955 r = dictRemove (wdb->cols, name); /* Only failure is key not found */
956 if (!r)
957 {
958 return nerr_raise (NERR_NOT_FOUND,
959 "Unable to find column for key %s", name);
960 }
961 wdb->defn_dirty = 1;
962 wdb->table_version = rand();
963
964 return STATUS_OK;
965 }
966
wdb_column_exchange(WDB * wdb,const char * key1,const char * key2)967 NEOERR *wdb_column_exchange (WDB *wdb, const char *key1, const char *key2)
968 {
969 return nerr_raise (NERR_ASSERT,
970 "wdb_column_exchange: Not Implemented");
971 }
972
973 /* Not that there's that much point in changing the key name ... */
wdb_update(WDB * wdb,const char * name,const char * key)974 NEOERR *wdb_update (WDB *wdb, const char *name, const char *key)
975 {
976 if (name != NULL && strcmp(wdb->name, name))
977 {
978 if (wdb->name != NULL)
979 free(wdb->name);
980 wdb->name = strdup(name);
981 if (wdb->name == NULL)
982 return nerr_raise (NERR_NOMEM,
983 "Unable to allocate memory to update name to %s", name);
984 wdb->defn_dirty = 1;
985 wdb->table_version = rand();
986 }
987 if (key != NULL && strcmp(wdb->key, key))
988 {
989 if (wdb->key != NULL)
990 free(wdb->key);
991 wdb->key = strdup(key);
992 if (wdb->key == NULL)
993 {
994 wdb->defn_dirty = 0;
995 return nerr_raise (NERR_NOMEM,
996 "Unable to allocate memory to update key to %s", key);
997 }
998 wdb->defn_dirty = 1;
999 wdb->table_version = rand();
1000 }
1001 return STATUS_OK;
1002 }
1003
wdb_create(WDB ** wdb,const char * path,const char * name,const char * key,ULIST * col_def,int flags)1004 NEOERR *wdb_create (WDB **wdb, const char *path, const char *name,
1005 const char *key, ULIST *col_def, int flags)
1006 {
1007 WDB *my_wdb;
1008 char d_path[_POSIX_PATH_MAX];
1009 NEOERR *err = STATUS_OK;
1010 int x, len, r;
1011 char *s;
1012
1013 *wdb = NULL;
1014
1015 err = wdb_alloc (&my_wdb, flags);
1016 if (err) return nerr_pass(err);
1017
1018 my_wdb->name = strdup (name);
1019 my_wdb->key = strdup (key);
1020 my_wdb->path = strdup(path);
1021 if (my_wdb->name == NULL || my_wdb->key == NULL || my_wdb->path == NULL)
1022 {
1023 wdb_destroy (&my_wdb);
1024 return nerr_raise (NERR_NOMEM,
1025 "Unable to allocate memory for creation of %s", name);
1026 }
1027
1028 /* ondisk must start at one because of skipList */
1029 my_wdb->last_ondisk = 1;
1030 len = uListLength(col_def);
1031 for (x = 0; x < len; x++)
1032 {
1033 err = uListGet (col_def, x, (void *)&s);
1034 if (err)
1035 {
1036 wdb_destroy (&my_wdb);
1037 return nerr_pass(err);
1038 }
1039 err = wdb_column_insert (my_wdb, -1, s, WDB_TYPE_STR);
1040 my_wdb->defn_dirty = 0; /* So we don't save on error destroy */
1041 if (err)
1042 {
1043 wdb_destroy (&my_wdb);
1044 return nerr_pass(err);
1045 }
1046 }
1047
1048 err = wdb_save_defn (my_wdb, path);
1049 if (err)
1050 {
1051 wdb_destroy (&my_wdb);
1052 return nerr_pass(err);
1053 }
1054
1055 snprintf (d_path, sizeof(d_path), "%s.wdb", path);
1056 r = db_open(d_path, DB_BTREE, DB_CREATE | DB_TRUNCATE, 0, NULL, NULL, &(my_wdb->db));
1057 if (r)
1058 {
1059 wdb_destroy (&my_wdb);
1060 return nerr_raise (NERR_DB, "Unable to create db file %s: %d", d_path, r);
1061 }
1062
1063 *wdb = my_wdb;
1064
1065 return STATUS_OK;
1066 }
1067
wdb_attr_next(WDB * wdb,char ** key,char ** value)1068 NEOERR *wdb_attr_next (WDB *wdb, char **key, char **value)
1069 {
1070 *value = (char *) dictNext (wdb->attrs, key, NULL);
1071
1072 return STATUS_OK;
1073 }
1074
wdb_attr_get(WDB * wdb,const char * key,char ** value)1075 NEOERR *wdb_attr_get (WDB *wdb, const char *key, char **value)
1076 {
1077 void *v;
1078
1079 v = dictSearch (wdb->attrs, key, NULL);
1080
1081 if (v == NULL)
1082 return nerr_raise (NERR_NOT_FOUND, "Unable to find attr %s", key);
1083
1084 *value = (char *)v;
1085
1086 return STATUS_OK;
1087 }
1088
wdb_attr_set(WDB * wdb,const char * key,const char * value)1089 NEOERR *wdb_attr_set (WDB *wdb, const char *key, const char *value)
1090 {
1091 NEOERR *err = STATUS_OK;
1092 char *v;
1093
1094 v = strdup(value);
1095 if (v == NULL)
1096 return nerr_raise (NERR_NOMEM, "No memory for new attr");
1097
1098 err = dictSetValue(wdb->attrs, key, v);
1099 if (err)
1100 return nerr_pass_ctx (err, "Unable to set attr %s", key);
1101
1102 wdb->defn_dirty = 1;
1103
1104 return STATUS_OK;
1105 }
1106
wdbr_get(WDB * wdb,WDBRow * row,const char * key,void ** value)1107 NEOERR *wdbr_get (WDB *wdb, WDBRow *row, const char *key, void **value)
1108 {
1109 WDBColumn *col;
1110 void *v;
1111
1112 col = (WDBColumn *) dictSearch (wdb->cols, key, NULL);
1113
1114 if (col == NULL)
1115 return nerr_raise (NERR_NOT_FOUND, "Unable to find key %s", key);
1116
1117 if (col->inmem_index-1 > row->data_count)
1118 return nerr_raise (NERR_ASSERT, "Index for key %s is greater than row data, was table altered?", key);
1119
1120 v = row->data[col->inmem_index-1];
1121
1122 *value = v;
1123
1124 return STATUS_OK;
1125 }
1126
wdbr_set(WDB * wdb,WDBRow * row,const char * key,void * value)1127 NEOERR *wdbr_set (WDB *wdb, WDBRow *row, const char *key, void *value)
1128 {
1129 WDBColumn *col;
1130
1131 col = (WDBColumn *) dictSearch (wdb->cols, key, NULL);
1132
1133 if (col == NULL)
1134 return nerr_raise (NERR_NOT_FOUND, "Unable to find key %s", key);
1135
1136 if (col->inmem_index-1 > row->data_count)
1137 return nerr_raise (NERR_ASSERT, "Index for key %s is greater than row data, was table altered?", key);
1138
1139 if (col->type == WDB_TYPE_STR && row->data[col->inmem_index-1] != NULL)
1140 {
1141 free (row->data[col->inmem_index-1]);
1142 }
1143 row->data[col->inmem_index-1] = value;
1144
1145 return STATUS_OK;
1146 }
1147
alloc_row(WDB * wdb,WDBRow ** row)1148 static NEOERR *alloc_row (WDB *wdb, WDBRow **row)
1149 {
1150 WDBRow *my_row;
1151 int len;
1152
1153 *row = NULL;
1154
1155 len = uListLength (wdb->cols_l);
1156
1157 my_row = (WDBRow *) calloc (1, sizeof (WDBRow) + len * (sizeof (void *)));
1158 if (my_row == NULL)
1159 return nerr_raise (NERR_NOMEM, "No memory for new row");
1160
1161 my_row->data_count = len;
1162 my_row->table_version = wdb->table_version;
1163
1164 *row = my_row;
1165
1166 return STATUS_OK;
1167 }
1168
wdbr_destroy(WDB * wdb,WDBRow ** row)1169 NEOERR *wdbr_destroy (WDB *wdb, WDBRow **row)
1170 {
1171 WDBColumn *col;
1172 WDBRow *my_row;
1173 int len, x;
1174 NEOERR *err;
1175
1176 err = STATUS_OK;
1177 if (*row == NULL)
1178 return err;
1179
1180 my_row = *row;
1181
1182 /* Verify this row maps to this table, or else we could do something
1183 * bad */
1184
1185 if (wdb->table_version != my_row->table_version)
1186 return nerr_raise (NERR_ASSERT, "Row %s doesn't match current table", my_row->key_value);
1187
1188 if (my_row->key_value != NULL)
1189 free (my_row->key_value);
1190
1191 len = uListLength(wdb->cols_l);
1192
1193 for (x = 0; x < len; x++)
1194 {
1195 if (my_row->data[x] != NULL)
1196 {
1197 err = uListGet (wdb->cols_l, x, (void *)&col);
1198 if (err) break;
1199 switch (col->type)
1200 {
1201 case WDB_TYPE_INT:
1202 break;
1203 case WDB_TYPE_STR:
1204 free (my_row->data[x]);
1205 break;
1206 default:
1207 return nerr_raise (NERR_ASSERT, "Unknown type %d", col->type);
1208 }
1209 }
1210 }
1211
1212 free (my_row);
1213 *row = NULL;
1214
1215 return nerr_pass(err);
1216 }
1217
wdbr_lookup(WDB * wdb,const char * key,WDBRow ** row)1218 NEOERR *wdbr_lookup (WDB *wdb, const char *key, WDBRow **row)
1219 {
1220 DBT dkey, data;
1221 NEOERR *err = STATUS_OK;
1222 WDBRow *my_row;
1223 int r;
1224
1225 *row = NULL;
1226
1227 memset(&dkey, 0, sizeof(dkey));
1228 memset(&data, 0, sizeof(data));
1229
1230 dkey.flags = DB_DBT_USERMEM;
1231 data.flags = DB_DBT_MALLOC;
1232
1233 dkey.data = (void *)key;
1234 dkey.size = strlen(key);
1235
1236 r = wdb->db->get (wdb->db, NULL, &dkey, &data, 0);
1237 if (r == DB_NOTFOUND)
1238 return nerr_raise (NERR_NOT_FOUND, "Unable to find key %s", key);
1239 else if (r)
1240 return nerr_raise (NERR_DB, "Error retrieving key %s: %d", key, r);
1241
1242 /* allocate row */
1243 err = alloc_row (wdb, &my_row);
1244 if (err != STATUS_OK)
1245 {
1246 free (data.data);
1247 return nerr_pass(err);
1248 }
1249
1250 my_row->key_value = strdup(key);
1251 if (my_row->key_value == NULL)
1252 {
1253 free (data.data);
1254 free (my_row);
1255 return nerr_raise (NERR_NOMEM, "No memory for new row");
1256 }
1257
1258 /* unpack row */
1259 err = unpack_row (wdb, data.data, data.size, my_row);
1260 free (data.data);
1261 if (err)
1262 {
1263 free (my_row);
1264 return nerr_pass(err);
1265 }
1266
1267 *row = my_row;
1268
1269 return STATUS_OK;
1270 }
1271
wdbr_create(WDB * wdb,const char * key,WDBRow ** row)1272 NEOERR *wdbr_create (WDB *wdb, const char *key, WDBRow **row)
1273 {
1274 WDBRow *my_row;
1275 NEOERR *err = STATUS_OK;
1276
1277 *row = NULL;
1278
1279 /* allocate row */
1280 err = alloc_row (wdb, &my_row);
1281 if (err) return nerr_pass(err);
1282
1283 my_row->key_value = strdup(key);
1284 if (my_row->key_value == NULL)
1285 {
1286 wdbr_destroy (wdb, &my_row);
1287 return nerr_raise (NERR_NOMEM, "No memory for new row");
1288 }
1289
1290 *row = my_row;
1291
1292 return STATUS_OK;
1293 }
1294
wdbr_save(WDB * wdb,WDBRow * row,int flags)1295 NEOERR *wdbr_save (WDB *wdb, WDBRow *row, int flags)
1296 {
1297 DBT dkey, data;
1298 int dflags = 0;
1299 NEOERR *err = STATUS_OK;
1300 int r;
1301
1302 memset(&dkey, 0, sizeof(dkey));
1303 memset(&data, 0, sizeof(data));
1304
1305 dkey.data = row->key_value;
1306 dkey.size = strlen(row->key_value);
1307
1308 err = pack_row (wdb, row, &(data.data), &data.size);
1309 if (err != STATUS_OK) return nerr_pass(err);
1310
1311 if (flags & WDBR_INSERT)
1312 {
1313 dflags = DB_NOOVERWRITE;
1314 }
1315
1316 r = wdb->db->put (wdb->db, NULL, &dkey, &data, dflags);
1317 free (data.data);
1318 if (r == DB_KEYEXIST)
1319 return nerr_raise (NERR_DUPLICATE, "Key %s already exists", row->key_value);
1320 if (r)
1321 return nerr_raise (NERR_DB, "Error saving key %s: %d",
1322 row->key_value, r);
1323
1324 return STATUS_OK;
1325 }
1326
wdbr_delete(WDB * wdb,const char * key)1327 NEOERR *wdbr_delete (WDB *wdb, const char *key)
1328 {
1329 DBT dkey;
1330 int r;
1331
1332 memset(&dkey, 0, sizeof(dkey));
1333
1334 dkey.flags = DB_DBT_USERMEM;
1335
1336 dkey.data = (void *)key;
1337 dkey.size = strlen(key);
1338
1339 r = wdb->db->del (wdb->db, NULL, &dkey, 0);
1340 if (r == DB_NOTFOUND)
1341 return nerr_raise (NERR_NOT_FOUND, "Key %s not found", key);
1342 else if (r)
1343 return nerr_raise (NERR_DB, "Error deleting key %s: %d", key, r);
1344
1345
1346 return STATUS_OK;
1347 }
1348
wdbr_dump(WDB * wdb,WDBRow * row)1349 NEOERR *wdbr_dump (WDB *wdb, WDBRow *row)
1350 {
1351 int x;
1352
1353 ne_warn ("version: %d", row->table_version);
1354 ne_warn ("key: %s", row->key_value);
1355 ne_warn ("count: %d", row->data_count);
1356 for (x=0; x < row->data_count; x++)
1357 ne_warn ("data[%d]: %s", x, row->data[x]);
1358
1359 return STATUS_OK;
1360 }
1361
wdbc_create(WDB * wdb,WDBCursor ** cursor)1362 NEOERR *wdbc_create (WDB *wdb, WDBCursor **cursor)
1363 {
1364 DBC *db_cursor;
1365 WDBCursor *new_cursor;
1366 int r;
1367
1368 *cursor = NULL;
1369
1370 #if (DB_VERSION_MINOR==4)
1371 r = (wdb->db)->cursor (wdb->db, NULL, &db_cursor);
1372 #else
1373 r = (wdb->db)->cursor (wdb->db, NULL, &db_cursor, 0);
1374 #endif
1375 if (r)
1376 return nerr_raise (NERR_DB, "Unable to create cursor: %d", r);
1377
1378 new_cursor = (WDBCursor *) calloc (1, sizeof (WDBCursor));
1379 if (new_cursor == NULL)
1380 {
1381 db_cursor->c_close (db_cursor);
1382 return nerr_raise (NERR_NOMEM, "Unable to create cursor");
1383 }
1384
1385 new_cursor->table_version = wdb->table_version;
1386 new_cursor->db_cursor = db_cursor;
1387
1388 *cursor = new_cursor;
1389
1390 return STATUS_OK;
1391 }
1392
wdbc_destroy(WDB * wdb,WDBCursor ** cursor)1393 NEOERR *wdbc_destroy (WDB *wdb, WDBCursor **cursor)
1394 {
1395 if (*cursor != NULL)
1396 {
1397 (*cursor)->db_cursor->c_close ((*cursor)->db_cursor);
1398 free (*cursor);
1399 *cursor = NULL;
1400 }
1401 return STATUS_OK;
1402 }
1403
wdbr_next(WDB * wdb,WDBCursor * cursor,WDBRow ** row,int flags)1404 NEOERR *wdbr_next (WDB *wdb, WDBCursor *cursor, WDBRow **row, int flags)
1405 {
1406 DBT dkey, data;
1407 WDBRow *my_row;
1408 NEOERR *err = STATUS_OK;
1409 int r;
1410
1411 *row = NULL;
1412
1413 if (wdb->table_version != cursor->table_version)
1414 {
1415 return nerr_raise (NERR_ASSERT, "Cursor doesn't match database");
1416 }
1417
1418 memset(&dkey, 0, sizeof(dkey));
1419 memset(&data, 0, sizeof(data));
1420 dkey.flags = DB_DBT_MALLOC;
1421 data.flags = DB_DBT_MALLOC;
1422
1423 /* First call */
1424 if (flags & WDBC_FIRST)
1425 {
1426 r = cursor->db_cursor->c_get (cursor->db_cursor, &dkey, &data, DB_FIRST);
1427 if (r == DB_NOTFOUND)
1428 return nerr_raise (NERR_NOT_FOUND, "Cursor empty");
1429 else if (r)
1430 return nerr_raise (NERR_DB, "Unable to get first item from cursor: %d",
1431 r);
1432 }
1433 else
1434 {
1435 r = cursor->db_cursor->c_get (cursor->db_cursor, &dkey, &data, DB_NEXT);
1436 if (r == DB_NOTFOUND)
1437 return STATUS_OK;
1438 else if (r)
1439 return nerr_raise (NERR_DB, "Unable to get next item from cursor: %d", r);
1440 }
1441
1442 /* allocate row */
1443 err = alloc_row (wdb, &my_row);
1444 if (err)
1445 {
1446 free (data.data);
1447 return nerr_pass(err);
1448 }
1449
1450 my_row->key_value = (char *) malloc (dkey.size + 1);
1451 if (my_row->key_value == NULL)
1452 {
1453 free (data.data);
1454 free (my_row);
1455 return nerr_raise (NERR_NOMEM, "No memory for new row");
1456 }
1457
1458 memcpy (my_row->key_value, dkey.data, dkey.size);
1459 my_row->key_value[dkey.size] = '\0';
1460
1461 /* unpack row */
1462 err = unpack_row (wdb, data.data, data.size, my_row);
1463 free (data.data);
1464 free (dkey.data);
1465 if (err)
1466 {
1467 free (my_row);
1468 return nerr_pass(err);
1469 }
1470
1471 *row = my_row;
1472
1473 return STATUS_OK;
1474 }
1475
wdbr_find(WDB * wdb,WDBCursor * cursor,const char * key,WDBRow ** row)1476 NEOERR *wdbr_find (WDB *wdb, WDBCursor *cursor, const char *key, WDBRow **row)
1477 {
1478 DBT dkey, data;
1479 WDBRow *my_row;
1480 NEOERR *err = STATUS_OK;
1481 int r;
1482
1483 *row = NULL;
1484
1485 if (wdb->table_version != cursor->table_version)
1486 {
1487 return nerr_raise (NERR_ASSERT, "Cursor doesn't match database");
1488 }
1489
1490 memset(&dkey, 0, sizeof(dkey));
1491 memset(&data, 0, sizeof(data));
1492 dkey.flags = DB_DBT_USERMEM;
1493 data.flags = DB_DBT_MALLOC;
1494
1495 dkey.data = (void *)key;
1496 dkey.size = strlen(key);
1497
1498 r = cursor->db_cursor->c_get (cursor->db_cursor, &dkey, &data, DB_SET_RANGE);
1499 if (r == DB_NOTFOUND)
1500 return STATUS_OK;
1501 else if (r)
1502 return nerr_raise (r, "Unable to get find item for key %s", key);
1503
1504 /* allocate row */
1505 err = alloc_row (wdb, &my_row);
1506 if (err)
1507 {
1508 free (data.data);
1509 return nerr_pass(err);
1510 }
1511
1512 my_row->key_value = (char *) malloc (dkey.size + 1);
1513 if (my_row->key_value == NULL)
1514 {
1515 free (data.data);
1516 free (my_row);
1517 return nerr_raise (NERR_NOMEM, "No memory for new row");
1518 }
1519
1520 memcpy (my_row->key_value, dkey.data, dkey.size);
1521 my_row->key_value[dkey.size] = '\0';
1522
1523 /* unpack row */
1524 err = unpack_row (wdb, data.data, data.size, my_row);
1525 free (data.data);
1526 if (err)
1527 {
1528 free (my_row);
1529 return nerr_pass(err);
1530 }
1531
1532 *row = my_row;
1533
1534 return STATUS_OK;
1535 }
1536
wdb_keys(WDB * wdb,char ** primary_key,ULIST ** data)1537 NEOERR *wdb_keys (WDB *wdb, char **primary_key, ULIST **data)
1538 {
1539 NEOERR *err;
1540 int x, len;
1541 WDBColumn *col;
1542 ULIST *my_data;
1543 char *my_key = NULL;
1544 char *my_col = NULL;
1545
1546 *data = NULL;
1547 *primary_key = NULL;
1548 my_key = strdup(wdb->key);
1549 if (my_key == NULL)
1550 return nerr_raise (NERR_NOMEM, "Unable to allocate memory for keys");
1551
1552 len = uListLength(wdb->cols_l);
1553 err = uListInit (&my_data, len, 0);
1554 if (err != STATUS_OK)
1555 {
1556 free(my_key);
1557 return nerr_pass(err);
1558 }
1559
1560 for (x = 0; x < len; x++)
1561 {
1562 err = uListGet (wdb->cols_l, x, (void *)&col);
1563 if (err) goto key_err;
1564 my_col = strdup(col->name);
1565 if (my_col == NULL)
1566 {
1567 err = nerr_raise (NERR_NOMEM, "Unable to allocate memory for keys");
1568 goto key_err;
1569 }
1570 err = uListAppend (my_data, my_col);
1571 my_col = NULL;
1572 if (err) goto key_err;
1573 }
1574
1575 *data = my_data;
1576 *primary_key = my_key;
1577 return STATUS_OK;
1578
1579 key_err:
1580 if (my_key != NULL) free (my_key);
1581 if (my_col != NULL) free (my_col);
1582 *primary_key = NULL;
1583 uListDestroy (&my_data, 0);
1584 return nerr_pass(err);
1585 }
1586
1587 /*
1588 * Known Issues:
1589 * - Probably need to store the actual key value in the packed row..
1590 * Maybe not, because any cursor you use on a sleepycat db will
1591 * return the key...
1592 * - um, memory. Especially when dealing with rows, need to keep track
1593 * of when we allocate, when we dealloc, and who owns that memory to
1594 * free it
1595 * - function to delete entry from wdb
1596 */
1597