• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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