| /* |
| * Copyright 2001-2004 Brandon Long |
| * All Rights Reserved. |
| * |
| * ClearSilver Templating System |
| * |
| * This code is made available under the terms of the ClearSilver License. |
| * http://www.clearsilver.net/license.hdf |
| * |
| */ |
| /* |
| * The wdb is a wrapper around the sleepycat db library which adds |
| * a relatively simple data/column definition. In many respects, this |
| * code is way more complicated than it ever needed to be, but it works, |
| * so I'm loathe to "fix" it. |
| * |
| * One of they key features of this is the ability to update the |
| * "schema" of the wdb without changing all of the existing rows of |
| * data. |
| */ |
| |
| #include "cs_config.h" |
| |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <limits.h> |
| #include <db.h> |
| #include <ctype.h> |
| |
| #include "neo_misc.h" |
| #include "neo_err.h" |
| #include "dict.h" |
| #include "ulist.h" |
| #include "skiplist.h" |
| #include "wdb.h" |
| |
| |
| #define DEFN_VERSION_1 "WDB-VERSION-200006301" |
| #define PACK_VERSION_1 1 |
| |
| static void string_rstrip (char *s) |
| { |
| size_t len; |
| |
| len = strlen(s); |
| len--; |
| while (len > 0 && isspace(s[len])) |
| { |
| s[len] = '\0'; |
| len--; |
| } |
| return; |
| } |
| |
| static int to_hex (unsigned char ch, unsigned char *s) |
| { |
| unsigned int uvalue = ch; |
| |
| s[1] = "0123456789ABCDEF"[uvalue % 16]; |
| uvalue = (uvalue / 16); s[0] = "0123456789ABCDEF"[uvalue % 16]; |
| return 0; |
| } |
| |
| int Index_hex[128] = { |
| -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, |
| -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, |
| -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, |
| 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1, |
| -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, |
| -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, |
| -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1 |
| }; |
| |
| #define hexval(c) Index_hex[(unsigned int)(c)] |
| |
| /* Encoding means any non-printable characters and : and % */ |
| static NEOERR *wdb_encode_str_alloc (const char *s, char **o) |
| { |
| int x = 0; |
| int c = 0; |
| char *out; |
| unsigned char ch; |
| |
| while (s[x]) |
| { |
| ch = (unsigned char) s[x]; |
| if ((ch < 32) || (ch > 127) || (ch == ':') || (ch == '%') || isspace(ch)) |
| c++; |
| x++; |
| } |
| |
| out = (char *) malloc (sizeof (char) * (x + c * 3) + 1); |
| if (out == NULL) |
| return nerr_raise (NERR_NOMEM, |
| "Unable to allocate memory for encoding %s", s); |
| |
| x = 0; |
| c = 0; |
| |
| *o = out; |
| |
| while (s[x]) |
| { |
| ch = (unsigned char) s[x]; |
| if ((ch < 32) || (ch > 127) || (ch == ':') || (ch == '%') || isspace(ch)) |
| { |
| out[c++] = '%'; |
| to_hex (s[x], &out[c]); |
| c+=2; |
| } |
| else |
| { |
| out[c++] = s[x]; |
| } |
| x++; |
| } |
| out[c] = '\0'; |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR *wdb_decode_str_alloc (const char *s, char **o) |
| { |
| int x = 0; |
| int c = 0; |
| char *out; |
| unsigned char ch; |
| |
| |
| x = strlen(s); |
| /* Overkill, the decoded string will be smaller */ |
| out = (char *) malloc (sizeof (char) * (x + 1)); |
| if (out == NULL) |
| return nerr_raise (NERR_NOMEM, |
| "Unable to allocate memory for decoding %s", s); |
| |
| x = 0; |
| c = 0; |
| |
| while (s[x]) |
| { |
| if (s[x] == '%') |
| { |
| x++; |
| ch = hexval(s[x]) << 4; |
| x++; |
| ch |= hexval(s[x]); |
| out[c++] = ch; |
| } |
| else |
| { |
| out[c++] = s[x]; |
| } |
| x++; |
| } |
| out[c] = '\0'; |
| *o = out; |
| |
| return STATUS_OK; |
| } |
| |
| static void free_cb (void *value, void *rock) |
| { |
| free (value); |
| } |
| |
| static void free_col_cb (void *value, void *rock) |
| { |
| WDBColumn *col; |
| |
| col = (WDBColumn *)value; |
| |
| free (col->name); |
| free (col); |
| } |
| |
| static NEOERR *wdb_alloc (WDB **wdb, int flags) |
| { |
| WDB *my_wdb; |
| NEOERR *err = STATUS_OK; |
| |
| my_wdb = (WDB *) calloc (1, sizeof (WDB)); |
| |
| if (my_wdb == NULL) |
| return nerr_raise (NERR_NOMEM, "Unable to allocate memory for WDB"); |
| |
| do |
| { |
| err = dictCreate (&(my_wdb->attrs), 0, 2, 5, 0, 0, free_cb, NULL); |
| if (err != STATUS_OK) break; |
| err = dictCreate (&(my_wdb->cols), 0, 2, 5, 0, 0, free_col_cb, NULL); |
| if (err != STATUS_OK) break; |
| err = uListInit (&(my_wdb->cols_l), 0, 0); |
| if (err != STATUS_OK) break; |
| err = skipNewList(&(my_wdb->ondisk), 0, 4, 2, 0, NULL, NULL); |
| if (err != STATUS_OK) break; |
| |
| *wdb = my_wdb; |
| |
| return STATUS_OK; |
| } while (0); |
| |
| wdb_destroy(&my_wdb); |
| return nerr_pass (err); |
| } |
| |
| |
| #define STATE_REQUIRED 1 |
| #define STATE_ATTRIBUTES 2 |
| #define STATE_COLUMN_DEF 3 |
| |
| static NEOERR *wdb_load_defn_v1 (WDB *wdb, FILE *fp) |
| { |
| char line[1024]; |
| int state = 1; |
| char *k, *v; |
| NEOERR *err = STATUS_OK; |
| int colindex = 1; |
| WDBColumn *col; |
| |
| while (fgets(line, sizeof(line), fp) != NULL) |
| { |
| string_rstrip(line); |
| switch (state) |
| { |
| case STATE_REQUIRED: |
| if (!strcmp(line, "attributes")) |
| state = STATE_ATTRIBUTES; |
| else if (!strcmp(line, "columns")) |
| state = STATE_COLUMN_DEF; |
| else |
| { |
| k = line; |
| v = strchr(line, ':'); |
| /* HACK */ |
| if (!strcmp(k, "name") && ((v == NULL) || (v[1] == '\0'))) |
| { |
| v = "dNone"; |
| } |
| else |
| { |
| if (v == NULL) |
| return nerr_raise (NERR_PARSE, "Error parsing %s", line); |
| if (v[1] == '\0') |
| return nerr_raise (NERR_PARSE, "Error parsing %s", line); |
| } |
| v[0] = '\0'; |
| v++; |
| if (!strcmp(k, "key")) |
| { |
| err = wdb_decode_str_alloc (v, &(wdb->key)); |
| if (err) return nerr_pass(err); |
| } |
| else if (!strcmp(k, "name")) |
| { |
| err = wdb_decode_str_alloc (v, &(wdb->name)); |
| if (err) return nerr_pass(err); |
| } |
| else if (!strcmp(k, "ondisk")) |
| { |
| wdb->last_ondisk = atoi (v); |
| } |
| } |
| break; |
| case STATE_ATTRIBUTES: |
| if (!strcmp(line, "columns")) |
| state = STATE_COLUMN_DEF; |
| else |
| { |
| k = line; |
| v = strchr(line, ':'); |
| if (v == NULL) |
| return nerr_raise (NERR_PARSE, "Error parsing %s", line); |
| v[0] = '\0'; |
| v++; |
| err = wdb_decode_str_alloc (k, &k); |
| if (err) return nerr_pass(err); |
| err = wdb_decode_str_alloc (v, &v); |
| if (err) return nerr_pass(err); |
| err = dictSetValue(wdb->attrs, k, v); |
| free(k); |
| if (err) |
| return nerr_pass_ctx(err, "Error parsing %s", line); |
| } |
| break; |
| case STATE_COLUMN_DEF: |
| k = line; |
| v = strchr(line, ':'); |
| if (v == NULL) |
| return nerr_raise (NERR_PARSE, "Error parsing %s", line); |
| if (v[1] == '\0') |
| return nerr_raise (NERR_PARSE, "Error parsing %s", line); |
| v[0] = '\0'; |
| v++; |
| err = wdb_decode_str_alloc (k, &k); |
| if (err) return nerr_pass(err); |
| col = (WDBColumn *) calloc (1, sizeof (WDBColumn)); |
| col->name = k; |
| col->inmem_index = colindex++; |
| col->type = *v; |
| v+=2; |
| col->ondisk_index = atoi(v); |
| err = dictSetValue(wdb->cols, k, col); |
| if (err) |
| return nerr_raise (NERR_PARSE, "Error parsing %s", line); |
| err = uListAppend(wdb->cols_l, col); |
| if (err) return nerr_pass(err); |
| /* stupid skiplist will assert */ |
| if (col->ondisk_index == 0) |
| { |
| return nerr_raise (NERR_ASSERT, "Invalid ondisk mapping for %s", k); |
| } |
| err = skipInsert (wdb->ondisk, col->ondisk_index, |
| (void *)(col->inmem_index), 0); |
| if (err) |
| return nerr_pass_ctx(err, "Unable to update ondisk mapping for %s", k); |
| break; |
| default: |
| return nerr_raise (NERR_ASSERT, "Invalid state %d", state); |
| } |
| } |
| return STATUS_OK; |
| } |
| |
| static NEOERR *wdb_save_defn_v1 (WDB *wdb, FILE *fp) |
| { |
| NEOERR *err = STATUS_OK; |
| WDBColumn *col; |
| char *s = NULL; |
| char *key = NULL; |
| int r, x, len; |
| char *k = NULL; |
| char *v = NULL; |
| |
| /* Write version string */ |
| r = fprintf (fp, "%s\n", DEFN_VERSION_1); |
| if (!r) goto save_err; |
| |
| err = wdb_encode_str_alloc (wdb->name, &s); |
| if (err) goto save_err; |
| r = fprintf (fp, "name:%s\n", s); |
| if (!r) goto save_err; |
| free (s); |
| |
| err = wdb_encode_str_alloc (wdb->key, &s); |
| if (err != STATUS_OK) goto save_err; |
| r = fprintf (fp, "key:%s\n", s); |
| if (!r) goto save_err; |
| free (s); |
| s = NULL; |
| |
| r = fprintf (fp, "ondisk:%d\n", wdb->last_ondisk); |
| if (!r) goto save_err; |
| |
| r = fprintf (fp, "attributes\n"); |
| if (!r) goto save_err; |
| key = NULL; |
| s = (char *) dictNext (wdb->attrs, &key, NULL); |
| while (s) |
| { |
| err = wdb_encode_str_alloc (key, &k); |
| if (err != STATUS_OK) goto save_err; |
| err = wdb_encode_str_alloc (s, &v); |
| if (err != STATUS_OK) goto save_err; |
| r = fprintf (fp, "%s:%s\n", k, v); |
| if (!r) goto save_err; |
| free (k); |
| free (v); |
| k = NULL; |
| v = NULL; |
| s = (char *) dictNext (wdb->attrs, &key, NULL); |
| } |
| s = NULL; |
| |
| r = fprintf (fp, "columns\n"); |
| if (!r) goto save_err; |
| |
| len = uListLength(wdb->cols_l); |
| |
| for (x = 0; x < len; x++) |
| { |
| err = uListGet (wdb->cols_l, x, (void *)&col); |
| if (err) goto save_err; |
| err = wdb_encode_str_alloc (col->name, &s); |
| if (err != STATUS_OK) goto save_err; |
| r = fprintf (fp, "%s:%c:%d\n", s, col->type, col->ondisk_index); |
| if (!r) goto save_err; |
| free(s); |
| s = NULL; |
| } |
| |
| return STATUS_OK; |
| |
| save_err: |
| if (s != NULL) free (s); |
| if (k != NULL) free (k); |
| if (v != NULL) free (v); |
| if (err == STATUS_OK) return nerr_pass(err); |
| return nerr_raise (r, "Unable to save defn"); |
| } |
| |
| static NEOERR *wdb_load_defn (WDB *wdb, const char *name) |
| { |
| char path[_POSIX_PATH_MAX]; |
| char line[1024]; |
| FILE *fp; |
| NEOERR *err = STATUS_OK; |
| |
| snprintf (path, sizeof(path), "%s.wdf", name); |
| fp = fopen (path, "r"); |
| if (fp == NULL) |
| { |
| if (errno == ENOENT) |
| return nerr_raise (NERR_NOT_FOUND, "Unable to open defn %s", name); |
| return nerr_raise_errno (NERR_IO, "Unable to open defn %s", name); |
| } |
| |
| /* Read Version string */ |
| if (fgets (line, sizeof(line), fp) == NULL) |
| { |
| fclose(fp); |
| return nerr_raise_errno (NERR_IO, "Unable to read defn %s", name); |
| } |
| string_rstrip(line); |
| |
| if (!strcmp(line, DEFN_VERSION_1)) |
| { |
| err = wdb_load_defn_v1(wdb, fp); |
| fclose(fp); |
| if (err) return nerr_pass(err); |
| } |
| else |
| { |
| fclose(fp); |
| return nerr_raise (NERR_ASSERT, "Unknown defn version %s: %s", line, name); |
| } |
| |
| wdb->table_version = rand(); |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR *wdb_save_defn (WDB *wdb, const char *name) |
| { |
| char path[_POSIX_PATH_MAX]; |
| char path2[_POSIX_PATH_MAX]; |
| FILE *fp; |
| NEOERR *err = STATUS_OK; |
| int r; |
| |
| snprintf (path, sizeof(path), "%s.wdf.new", name); |
| snprintf (path2, sizeof(path2), "%s.wdf", name); |
| fp = fopen (path, "w"); |
| if (fp == NULL) |
| return nerr_raise_errno (NERR_IO, "Unable to open defn %s", name); |
| |
| err = wdb_save_defn_v1 (wdb, fp); |
| fclose (fp); |
| if (err != STATUS_OK) |
| { |
| unlink (path); |
| return nerr_pass (err); |
| } |
| |
| r = unlink (path2); |
| if (r == -1 && errno != ENOENT) |
| return nerr_raise_errno (NERR_IO, "Unable to unlink %s", path2); |
| r = link (path, path2); |
| if (r == -1) |
| return nerr_raise_errno (NERR_IO, "Unable to link %s to %s", path, path2); |
| r = unlink (path); |
| |
| wdb->defn_dirty = 0; |
| wdb->table_version = rand(); |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdb_open (WDB **wdb, const char *name, int flags) |
| { |
| WDB *my_wdb; |
| char path[_POSIX_PATH_MAX]; |
| NEOERR *err = STATUS_OK; |
| int r; |
| |
| *wdb = NULL; |
| |
| err = wdb_alloc (&my_wdb, flags); |
| if (err) return nerr_pass(err); |
| |
| my_wdb->path = strdup (name); |
| if (err) |
| { |
| wdb_destroy (&my_wdb); |
| return nerr_pass(err); |
| } |
| |
| err = wdb_load_defn (my_wdb, name); |
| if (err) |
| { |
| wdb_destroy (&my_wdb); |
| return nerr_pass(err); |
| } |
| |
| snprintf (path, sizeof(path), "%s.wdb", name); |
| r = db_open(path, DB_BTREE, 0, 0, NULL, NULL, &(my_wdb->db)); |
| if (r) |
| { |
| wdb_destroy (&my_wdb); |
| return nerr_raise (NERR_DB, "Unable to open database %s: %d", name, r); |
| } |
| |
| *wdb = my_wdb; |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdb_save (WDB *wdb) |
| { |
| if (wdb->defn_dirty) |
| { |
| wdb_save_defn (wdb, wdb->path); |
| } |
| return STATUS_OK; |
| } |
| |
| void wdb_destroy (WDB **wdb) |
| { |
| WDB *my_wdb; |
| |
| my_wdb = *wdb; |
| |
| if (my_wdb == NULL) return; |
| |
| if (my_wdb->defn_dirty) |
| { |
| wdb_save_defn (my_wdb, my_wdb->path); |
| } |
| |
| if (my_wdb->attrs != NULL) |
| { |
| dictDestroy (my_wdb->attrs); |
| } |
| |
| if (my_wdb->cols != NULL) |
| { |
| dictDestroy (my_wdb->cols); |
| } |
| |
| if (my_wdb->cols_l != NULL) |
| { |
| uListDestroy(&(my_wdb->cols_l), 0); |
| } |
| |
| if (my_wdb->ondisk != NULL) |
| { |
| skipFreeList(my_wdb->ondisk); |
| } |
| |
| if (my_wdb->db != NULL) |
| { |
| my_wdb->db->close (my_wdb->db, 0); |
| my_wdb->db = NULL; |
| } |
| |
| if (my_wdb->path != NULL) |
| { |
| free(my_wdb->path); |
| my_wdb->path = NULL; |
| } |
| if (my_wdb->name != NULL) |
| { |
| free(my_wdb->name); |
| my_wdb->name = NULL; |
| } |
| if (my_wdb->key != NULL) |
| { |
| free(my_wdb->key); |
| my_wdb->key = NULL; |
| } |
| |
| free (my_wdb); |
| *wdb = NULL; |
| |
| return; |
| } |
| |
| #define PACK_UB4(pdata, plen, pmax, pn) \ |
| { \ |
| if (plen + 4 > pmax) \ |
| { \ |
| pmax *= 2; \ |
| pdata = realloc ((void *)pdata, pmax); \ |
| if (pdata == NULL) goto pack_err; \ |
| } \ |
| pdata[plen++] = (0x0ff & (pn >> 0)); \ |
| pdata[plen++] = (0x0ff & (pn >> 8)); \ |
| pdata[plen++] = (0x0ff & (pn >> 16)); \ |
| pdata[plen++] = (0x0ff & (pn >> 24)); \ |
| } |
| |
| #define UNPACK_UB4(pdata, plen, pn, pd) \ |
| { \ |
| if (pn + 4 > plen) \ |
| goto pack_err; \ |
| pd = ((0x0ff & pdata[pn+0])<<0) | ((0x0ff & pdata[pn+1])<<8) | \ |
| ((0x0ff & pdata[pn+2])<<16) | ((0x0ff & pdata[pn+3])<<24); \ |
| pn+=4; \ |
| } |
| |
| #define PACK_BYTE(pdata, plen, pmax, pn) \ |
| { \ |
| if (plen + 1 > pmax) \ |
| { \ |
| pmax *= 2; \ |
| pdata = realloc ((void *)pdata, pmax); \ |
| if (pdata == NULL) goto pack_err; \ |
| } \ |
| pdata[plen++] = (0x0ff & (pn >> 0)); \ |
| } |
| |
| #define UNPACK_BYTE(pdata, plen, pn, pd) \ |
| { \ |
| if (pn + 1 > plen) \ |
| goto pack_err; \ |
| pd = pdata[pn++]; \ |
| } |
| |
| #define PACK_STRING(pdata, plen, pmax, dl, ds) \ |
| { \ |
| if (plen + 4 + dl > pmax) \ |
| { \ |
| while (plen + 4 + dl > pmax) \ |
| pmax *= 2; \ |
| pdata = realloc ((void *)pdata, pmax); \ |
| if (pdata == NULL) goto pack_err; \ |
| } \ |
| pdata[plen++] = (0x0ff & (dl >> 0)); \ |
| pdata[plen++] = (0x0ff & (dl >> 8)); \ |
| pdata[plen++] = (0x0ff & (dl >> 16)); \ |
| pdata[plen++] = (0x0ff & (dl >> 24)); \ |
| memcpy (&pdata[plen], ds, dl); \ |
| plen+=dl;\ |
| } |
| |
| #define UNPACK_STRING(pdata, plen, pn, ps) \ |
| { \ |
| int pl; \ |
| if (pn + 4 > plen) \ |
| goto pack_err; \ |
| pl = ((0x0ff & pdata[pn+0])<<0) | ((0x0ff & pdata[pn+1])<<8) | \ |
| ((0x0ff & pdata[pn+2])<<16) | ((0x0ff & pdata[pn+3])<<24); \ |
| pn+=4; \ |
| if (pl) \ |
| { \ |
| ps = (char *)malloc(sizeof(char)*(pl+1)); \ |
| if (ps == NULL) \ |
| goto pack_err; \ |
| memcpy (ps, &pdata[pn], pl); \ |
| ps[pl] = '\0'; \ |
| pn += pl; \ |
| } else { \ |
| ps = NULL; \ |
| } \ |
| } |
| |
| /* A VERSION_1 Row consists of the following data: |
| * UB4 VERSION |
| * UB4 DATA COUNT |
| * DATA where |
| * UB4 ONDISK INDEX |
| * UB1 TYPE |
| * if INT, then UB4 |
| * if STR, then UB4 length and length UB1s |
| */ |
| |
| static NEOERR *pack_row (WDB *wdb, WDBRow *row, void **rdata, int *rdlen) |
| { |
| char *data; |
| int x, len, dlen, dmax; |
| char *s; |
| int n; |
| WDBColumn *col; |
| NEOERR *err; |
| |
| *rdata = NULL; |
| *rdlen = 0; |
| /* allocate */ |
| data = (char *)malloc(sizeof (char) * 1024); |
| if (data == NULL) |
| return nerr_raise (NERR_NOMEM, "Unable to allocate memory to pack row"); |
| |
| dmax = 1024; |
| dlen = 0; |
| |
| PACK_UB4 (data, dlen, dmax, PACK_VERSION_1); |
| /* PACK_UB4 (data, dlen, dmax, time(NULL)); */ |
| |
| len = uListLength(wdb->cols_l); |
| if (len > row->data_count) |
| len = row->data_count; |
| PACK_UB4 (data, dlen, dmax, len); |
| |
| for (x = 0; x < len; x++) |
| { |
| err = uListGet (wdb->cols_l, x, (void *)&col); |
| if (err) goto pack_err; |
| PACK_UB4 (data, dlen, dmax, col->ondisk_index); |
| PACK_BYTE (data, dlen, dmax, col->type); |
| switch (col->type) |
| { |
| case WDB_TYPE_INT: |
| n = (int)(row->data[x]); |
| PACK_UB4 (data, dlen, dmax, n); |
| break; |
| case WDB_TYPE_STR: |
| s = (char *)(row->data[x]); |
| if (s == NULL) |
| { |
| s = ""; |
| } |
| n = strlen(s); |
| PACK_STRING (data, dlen, dmax, n, s); |
| break; |
| default: |
| free (data); |
| return nerr_raise (NERR_ASSERT, "Unknown type %d", col->type); |
| } |
| } |
| |
| *rdata = data; |
| *rdlen = dlen; |
| return STATUS_OK; |
| |
| pack_err: |
| if (data != NULL) |
| free (data); |
| if (err == STATUS_OK) |
| return nerr_raise(NERR_NOMEM, "Unable to allocate memory for pack_row"); |
| return nerr_pass(err); |
| } |
| |
| static NEOERR *unpack_row (WDB *wdb, void *rdata, int dlen, WDBRow *row) |
| { |
| unsigned char *data = rdata; |
| int version, n; |
| int count, x, ondisk_index, type, d_int, inmem_index; |
| char *s; |
| |
| n = 0; |
| |
| UNPACK_UB4(data, dlen, n, version); |
| |
| switch (version) |
| { |
| case PACK_VERSION_1: |
| UNPACK_UB4(data, dlen, n, count); |
| for (x = 0; x<count; x++) |
| { |
| UNPACK_UB4 (data, dlen, n, ondisk_index); |
| UNPACK_BYTE (data, dlen, n, type); |
| inmem_index = (int) skipSearch (wdb->ondisk, ondisk_index, NULL); |
| |
| switch (type) |
| { |
| case WDB_TYPE_INT: |
| UNPACK_UB4 (data, dlen, n, d_int); |
| if (inmem_index != 0) |
| row->data[inmem_index-1] = (void *) d_int; |
| break; |
| case WDB_TYPE_STR: |
| UNPACK_STRING (data, dlen, n, s); |
| if (inmem_index != 0) |
| row->data[inmem_index-1] = s; |
| break; |
| default: |
| return nerr_raise (NERR_ASSERT, "Unknown type %d for col %d", type, ondisk_index); |
| } |
| } |
| break; |
| default: |
| return nerr_raise (NERR_ASSERT, "Unknown version %d", version); |
| } |
| |
| return STATUS_OK; |
| pack_err: |
| return nerr_raise(NERR_PARSE, "Unable to unpack row %s", row->key_value); |
| } |
| |
| NEOERR *wdb_column_insert (WDB *wdb, int loc, const char *key, char type) |
| { |
| NEOERR *err; |
| WDBColumn *col, *ocol; |
| int x, len; |
| |
| col = (WDBColumn *) dictSearch (wdb->cols, key, NULL); |
| |
| if (col != NULL) |
| return nerr_raise (NERR_DUPLICATE, |
| "Duplicate key %s:%d", key, col->inmem_index); |
| |
| col = (WDBColumn *) calloc (1, sizeof (WDBColumn)); |
| if (col == NULL) |
| { |
| return nerr_raise (NERR_NOMEM, |
| "Unable to allocate memory for creation of col %s:%d", key, loc); |
| } |
| |
| col->name = strdup(key); |
| if (col->name == NULL) |
| { |
| free(col); |
| return nerr_raise (NERR_NOMEM, |
| "Unable to allocate memory for creation of col %s:%d", key, loc); |
| } |
| col->type = type; |
| col->ondisk_index = wdb->last_ondisk++; |
| /* -1 == append */ |
| if (loc == -1) |
| { |
| err = dictSetValue(wdb->cols, key, col); |
| if (err) |
| { |
| free (col->name); |
| free (col); |
| return nerr_pass_ctx (err, |
| "Unable to insert for creation of col %s:%d", key, loc); |
| } |
| err = uListAppend (wdb->cols_l, (void *)col); |
| if (err) return nerr_pass(err); |
| x = uListLength (wdb->cols_l); |
| col->inmem_index = x; |
| err = skipInsert (wdb->ondisk, col->ondisk_index, |
| (void *)(col->inmem_index), 0); |
| if (err) |
| return nerr_pass_ctx (err, "Unable to update ondisk mapping for %s", key); |
| } |
| else |
| { |
| /* We are inserting this in middle, so the skipList ondisk is now |
| * invalid, as is the inmem_index for all cols */ |
| err = dictSetValue(wdb->cols, key, col); |
| if (err) |
| { |
| free (col->name); |
| free (col); |
| return nerr_pass_ctx (err, |
| "Unable to insert for creation of col %s:%d", key, loc); |
| } |
| err = uListInsert (wdb->cols_l, loc, (void *)col); |
| if (err) return nerr_pass(err); |
| len = uListLength (wdb->cols_l); |
| /* Fix up inmem_index and ondisk skipList */ |
| for (x = 0; x < len; x++) |
| { |
| err = uListGet (wdb->cols_l, x, (void *)&ocol); |
| if (err) return nerr_pass(err); |
| ocol->inmem_index = x + 1; |
| err = skipInsert (wdb->ondisk, ocol->ondisk_index, |
| (void *)(ocol->inmem_index), TRUE); |
| if (err) |
| return nerr_pass_ctx (err, "Unable to update ondisk mapping for %s", key); |
| } |
| } |
| |
| wdb->defn_dirty = 1; |
| wdb->table_version = rand(); |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdb_column_update (WDB *wdb, const char *oldkey, const char *newkey) |
| { |
| WDBColumn *ocol, *col; |
| WDBColumn *vcol; |
| NEOERR *err = STATUS_OK; |
| int x, len, r; |
| |
| ocol = (WDBColumn *) dictSearch (wdb->cols, oldkey, NULL); |
| |
| if (ocol == NULL) |
| return nerr_raise (NERR_NOT_FOUND, |
| "Unable to find column for key %s", oldkey); |
| |
| col = (WDBColumn *) calloc (1, sizeof (WDBColumn)); |
| if (col == NULL) |
| { |
| return nerr_raise (NERR_NOMEM, |
| "Unable to allocate memory for column update %s", newkey); |
| } |
| |
| *col = *ocol; |
| col->name = strdup(newkey); |
| if (col->name == NULL) |
| { |
| free(col); |
| return nerr_raise (NERR_NOMEM, |
| "Unable to allocate memory for column update %s", oldkey); |
| } |
| len = uListLength(wdb->cols_l); |
| for (x = 0; x < len; x++) |
| { |
| err = uListGet (wdb->cols_l, x, (void *)&vcol); |
| if (err) return nerr_pass(err); |
| if (!strcmp(vcol->name, oldkey)) |
| { |
| err = uListSet (wdb->cols_l, x, (void *)col); |
| if (err) return nerr_pass(err); |
| break; |
| } |
| } |
| if (x>len) |
| { |
| return nerr_raise (NERR_ASSERT, "Unable to find cols_l for key %s", oldkey); |
| } |
| |
| r = dictRemove (wdb->cols, oldkey); /* Only failure is key not found */ |
| err = dictSetValue(wdb->cols, newkey, col); |
| if (err) |
| { |
| free (col->name); |
| free (col); |
| return nerr_pass_ctx (err, |
| "Unable to insert for update of col %s->%s", oldkey, newkey); |
| } |
| |
| wdb->defn_dirty = 1; |
| wdb->table_version = rand(); |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdb_column_delete (WDB *wdb, const char *name) |
| { |
| WDBColumn *col; |
| NEOERR *err = STATUS_OK; |
| int len, x, r; |
| |
| len = uListLength(wdb->cols_l); |
| for (x = 0; x < len; x++) |
| { |
| err = uListGet (wdb->cols_l, x, (void *)&col); |
| if (err) return nerr_pass(err); |
| if (!strcmp(col->name, name)) |
| { |
| err = uListDelete (wdb->cols_l, x, NULL); |
| if (err) return nerr_pass(err); |
| break; |
| } |
| } |
| |
| r = dictRemove (wdb->cols, name); /* Only failure is key not found */ |
| if (!r) |
| { |
| return nerr_raise (NERR_NOT_FOUND, |
| "Unable to find column for key %s", name); |
| } |
| wdb->defn_dirty = 1; |
| wdb->table_version = rand(); |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdb_column_exchange (WDB *wdb, const char *key1, const char *key2) |
| { |
| return nerr_raise (NERR_ASSERT, |
| "wdb_column_exchange: Not Implemented"); |
| } |
| |
| /* Not that there's that much point in changing the key name ... */ |
| NEOERR *wdb_update (WDB *wdb, const char *name, const char *key) |
| { |
| if (name != NULL && strcmp(wdb->name, name)) |
| { |
| if (wdb->name != NULL) |
| free(wdb->name); |
| wdb->name = strdup(name); |
| if (wdb->name == NULL) |
| return nerr_raise (NERR_NOMEM, |
| "Unable to allocate memory to update name to %s", name); |
| wdb->defn_dirty = 1; |
| wdb->table_version = rand(); |
| } |
| if (key != NULL && strcmp(wdb->key, key)) |
| { |
| if (wdb->key != NULL) |
| free(wdb->key); |
| wdb->key = strdup(key); |
| if (wdb->key == NULL) |
| { |
| wdb->defn_dirty = 0; |
| return nerr_raise (NERR_NOMEM, |
| "Unable to allocate memory to update key to %s", key); |
| } |
| wdb->defn_dirty = 1; |
| wdb->table_version = rand(); |
| } |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdb_create (WDB **wdb, const char *path, const char *name, |
| const char *key, ULIST *col_def, int flags) |
| { |
| WDB *my_wdb; |
| char d_path[_POSIX_PATH_MAX]; |
| NEOERR *err = STATUS_OK; |
| int x, len, r; |
| char *s; |
| |
| *wdb = NULL; |
| |
| err = wdb_alloc (&my_wdb, flags); |
| if (err) return nerr_pass(err); |
| |
| my_wdb->name = strdup (name); |
| my_wdb->key = strdup (key); |
| my_wdb->path = strdup(path); |
| if (my_wdb->name == NULL || my_wdb->key == NULL || my_wdb->path == NULL) |
| { |
| wdb_destroy (&my_wdb); |
| return nerr_raise (NERR_NOMEM, |
| "Unable to allocate memory for creation of %s", name); |
| } |
| |
| /* ondisk must start at one because of skipList */ |
| my_wdb->last_ondisk = 1; |
| len = uListLength(col_def); |
| for (x = 0; x < len; x++) |
| { |
| err = uListGet (col_def, x, (void *)&s); |
| if (err) |
| { |
| wdb_destroy (&my_wdb); |
| return nerr_pass(err); |
| } |
| err = wdb_column_insert (my_wdb, -1, s, WDB_TYPE_STR); |
| my_wdb->defn_dirty = 0; /* So we don't save on error destroy */ |
| if (err) |
| { |
| wdb_destroy (&my_wdb); |
| return nerr_pass(err); |
| } |
| } |
| |
| err = wdb_save_defn (my_wdb, path); |
| if (err) |
| { |
| wdb_destroy (&my_wdb); |
| return nerr_pass(err); |
| } |
| |
| snprintf (d_path, sizeof(d_path), "%s.wdb", path); |
| r = db_open(d_path, DB_BTREE, DB_CREATE | DB_TRUNCATE, 0, NULL, NULL, &(my_wdb->db)); |
| if (r) |
| { |
| wdb_destroy (&my_wdb); |
| return nerr_raise (NERR_DB, "Unable to create db file %s: %d", d_path, r); |
| } |
| |
| *wdb = my_wdb; |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdb_attr_next (WDB *wdb, char **key, char **value) |
| { |
| *value = (char *) dictNext (wdb->attrs, key, NULL); |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdb_attr_get (WDB *wdb, const char *key, char **value) |
| { |
| void *v; |
| |
| v = dictSearch (wdb->attrs, key, NULL); |
| |
| if (v == NULL) |
| return nerr_raise (NERR_NOT_FOUND, "Unable to find attr %s", key); |
| |
| *value = (char *)v; |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdb_attr_set (WDB *wdb, const char *key, const char *value) |
| { |
| NEOERR *err = STATUS_OK; |
| char *v; |
| |
| v = strdup(value); |
| if (v == NULL) |
| return nerr_raise (NERR_NOMEM, "No memory for new attr"); |
| |
| err = dictSetValue(wdb->attrs, key, v); |
| if (err) |
| return nerr_pass_ctx (err, "Unable to set attr %s", key); |
| |
| wdb->defn_dirty = 1; |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdbr_get (WDB *wdb, WDBRow *row, const char *key, void **value) |
| { |
| WDBColumn *col; |
| void *v; |
| |
| col = (WDBColumn *) dictSearch (wdb->cols, key, NULL); |
| |
| if (col == NULL) |
| return nerr_raise (NERR_NOT_FOUND, "Unable to find key %s", key); |
| |
| if (col->inmem_index-1 > row->data_count) |
| return nerr_raise (NERR_ASSERT, "Index for key %s is greater than row data, was table altered?", key); |
| |
| v = row->data[col->inmem_index-1]; |
| |
| *value = v; |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdbr_set (WDB *wdb, WDBRow *row, const char *key, void *value) |
| { |
| WDBColumn *col; |
| |
| col = (WDBColumn *) dictSearch (wdb->cols, key, NULL); |
| |
| if (col == NULL) |
| return nerr_raise (NERR_NOT_FOUND, "Unable to find key %s", key); |
| |
| if (col->inmem_index-1 > row->data_count) |
| return nerr_raise (NERR_ASSERT, "Index for key %s is greater than row data, was table altered?", key); |
| |
| if (col->type == WDB_TYPE_STR && row->data[col->inmem_index-1] != NULL) |
| { |
| free (row->data[col->inmem_index-1]); |
| } |
| row->data[col->inmem_index-1] = value; |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR *alloc_row (WDB *wdb, WDBRow **row) |
| { |
| WDBRow *my_row; |
| int len; |
| |
| *row = NULL; |
| |
| len = uListLength (wdb->cols_l); |
| |
| my_row = (WDBRow *) calloc (1, sizeof (WDBRow) + len * (sizeof (void *))); |
| if (my_row == NULL) |
| return nerr_raise (NERR_NOMEM, "No memory for new row"); |
| |
| my_row->data_count = len; |
| my_row->table_version = wdb->table_version; |
| |
| *row = my_row; |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdbr_destroy (WDB *wdb, WDBRow **row) |
| { |
| WDBColumn *col; |
| WDBRow *my_row; |
| int len, x; |
| NEOERR *err; |
| |
| err = STATUS_OK; |
| if (*row == NULL) |
| return err; |
| |
| my_row = *row; |
| |
| /* Verify this row maps to this table, or else we could do something |
| * bad */ |
| |
| if (wdb->table_version != my_row->table_version) |
| return nerr_raise (NERR_ASSERT, "Row %s doesn't match current table", my_row->key_value); |
| |
| if (my_row->key_value != NULL) |
| free (my_row->key_value); |
| |
| len = uListLength(wdb->cols_l); |
| |
| for (x = 0; x < len; x++) |
| { |
| if (my_row->data[x] != NULL) |
| { |
| err = uListGet (wdb->cols_l, x, (void *)&col); |
| if (err) break; |
| switch (col->type) |
| { |
| case WDB_TYPE_INT: |
| break; |
| case WDB_TYPE_STR: |
| free (my_row->data[x]); |
| break; |
| default: |
| return nerr_raise (NERR_ASSERT, "Unknown type %d", col->type); |
| } |
| } |
| } |
| |
| free (my_row); |
| *row = NULL; |
| |
| return nerr_pass(err); |
| } |
| |
| NEOERR *wdbr_lookup (WDB *wdb, const char *key, WDBRow **row) |
| { |
| DBT dkey, data; |
| NEOERR *err = STATUS_OK; |
| WDBRow *my_row; |
| int r; |
| |
| *row = NULL; |
| |
| memset(&dkey, 0, sizeof(dkey)); |
| memset(&data, 0, sizeof(data)); |
| |
| dkey.flags = DB_DBT_USERMEM; |
| data.flags = DB_DBT_MALLOC; |
| |
| dkey.data = (void *)key; |
| dkey.size = strlen(key); |
| |
| r = wdb->db->get (wdb->db, NULL, &dkey, &data, 0); |
| if (r == DB_NOTFOUND) |
| return nerr_raise (NERR_NOT_FOUND, "Unable to find key %s", key); |
| else if (r) |
| return nerr_raise (NERR_DB, "Error retrieving key %s: %d", key, r); |
| |
| /* allocate row */ |
| err = alloc_row (wdb, &my_row); |
| if (err != STATUS_OK) |
| { |
| free (data.data); |
| return nerr_pass(err); |
| } |
| |
| my_row->key_value = strdup(key); |
| if (my_row->key_value == NULL) |
| { |
| free (data.data); |
| free (my_row); |
| return nerr_raise (NERR_NOMEM, "No memory for new row"); |
| } |
| |
| /* unpack row */ |
| err = unpack_row (wdb, data.data, data.size, my_row); |
| free (data.data); |
| if (err) |
| { |
| free (my_row); |
| return nerr_pass(err); |
| } |
| |
| *row = my_row; |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdbr_create (WDB *wdb, const char *key, WDBRow **row) |
| { |
| WDBRow *my_row; |
| NEOERR *err = STATUS_OK; |
| |
| *row = NULL; |
| |
| /* allocate row */ |
| err = alloc_row (wdb, &my_row); |
| if (err) return nerr_pass(err); |
| |
| my_row->key_value = strdup(key); |
| if (my_row->key_value == NULL) |
| { |
| wdbr_destroy (wdb, &my_row); |
| return nerr_raise (NERR_NOMEM, "No memory for new row"); |
| } |
| |
| *row = my_row; |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdbr_save (WDB *wdb, WDBRow *row, int flags) |
| { |
| DBT dkey, data; |
| int dflags = 0; |
| NEOERR *err = STATUS_OK; |
| int r; |
| |
| memset(&dkey, 0, sizeof(dkey)); |
| memset(&data, 0, sizeof(data)); |
| |
| dkey.data = row->key_value; |
| dkey.size = strlen(row->key_value); |
| |
| err = pack_row (wdb, row, &(data.data), &data.size); |
| if (err != STATUS_OK) return nerr_pass(err); |
| |
| if (flags & WDBR_INSERT) |
| { |
| dflags = DB_NOOVERWRITE; |
| } |
| |
| r = wdb->db->put (wdb->db, NULL, &dkey, &data, dflags); |
| free (data.data); |
| if (r == DB_KEYEXIST) |
| return nerr_raise (NERR_DUPLICATE, "Key %s already exists", row->key_value); |
| if (r) |
| return nerr_raise (NERR_DB, "Error saving key %s: %d", |
| row->key_value, r); |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdbr_delete (WDB *wdb, const char *key) |
| { |
| DBT dkey; |
| int r; |
| |
| memset(&dkey, 0, sizeof(dkey)); |
| |
| dkey.flags = DB_DBT_USERMEM; |
| |
| dkey.data = (void *)key; |
| dkey.size = strlen(key); |
| |
| r = wdb->db->del (wdb->db, NULL, &dkey, 0); |
| if (r == DB_NOTFOUND) |
| return nerr_raise (NERR_NOT_FOUND, "Key %s not found", key); |
| else if (r) |
| return nerr_raise (NERR_DB, "Error deleting key %s: %d", key, r); |
| |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdbr_dump (WDB *wdb, WDBRow *row) |
| { |
| int x; |
| |
| ne_warn ("version: %d", row->table_version); |
| ne_warn ("key: %s", row->key_value); |
| ne_warn ("count: %d", row->data_count); |
| for (x=0; x < row->data_count; x++) |
| ne_warn ("data[%d]: %s", x, row->data[x]); |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdbc_create (WDB *wdb, WDBCursor **cursor) |
| { |
| DBC *db_cursor; |
| WDBCursor *new_cursor; |
| int r; |
| |
| *cursor = NULL; |
| |
| #if (DB_VERSION_MINOR==4) |
| r = (wdb->db)->cursor (wdb->db, NULL, &db_cursor); |
| #else |
| r = (wdb->db)->cursor (wdb->db, NULL, &db_cursor, 0); |
| #endif |
| if (r) |
| return nerr_raise (NERR_DB, "Unable to create cursor: %d", r); |
| |
| new_cursor = (WDBCursor *) calloc (1, sizeof (WDBCursor)); |
| if (new_cursor == NULL) |
| { |
| db_cursor->c_close (db_cursor); |
| return nerr_raise (NERR_NOMEM, "Unable to create cursor"); |
| } |
| |
| new_cursor->table_version = wdb->table_version; |
| new_cursor->db_cursor = db_cursor; |
| |
| *cursor = new_cursor; |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdbc_destroy (WDB *wdb, WDBCursor **cursor) |
| { |
| if (*cursor != NULL) |
| { |
| (*cursor)->db_cursor->c_close ((*cursor)->db_cursor); |
| free (*cursor); |
| *cursor = NULL; |
| } |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdbr_next (WDB *wdb, WDBCursor *cursor, WDBRow **row, int flags) |
| { |
| DBT dkey, data; |
| WDBRow *my_row; |
| NEOERR *err = STATUS_OK; |
| int r; |
| |
| *row = NULL; |
| |
| if (wdb->table_version != cursor->table_version) |
| { |
| return nerr_raise (NERR_ASSERT, "Cursor doesn't match database"); |
| } |
| |
| memset(&dkey, 0, sizeof(dkey)); |
| memset(&data, 0, sizeof(data)); |
| dkey.flags = DB_DBT_MALLOC; |
| data.flags = DB_DBT_MALLOC; |
| |
| /* First call */ |
| if (flags & WDBC_FIRST) |
| { |
| r = cursor->db_cursor->c_get (cursor->db_cursor, &dkey, &data, DB_FIRST); |
| if (r == DB_NOTFOUND) |
| return nerr_raise (NERR_NOT_FOUND, "Cursor empty"); |
| else if (r) |
| return nerr_raise (NERR_DB, "Unable to get first item from cursor: %d", |
| r); |
| } |
| else |
| { |
| r = cursor->db_cursor->c_get (cursor->db_cursor, &dkey, &data, DB_NEXT); |
| if (r == DB_NOTFOUND) |
| return STATUS_OK; |
| else if (r) |
| return nerr_raise (NERR_DB, "Unable to get next item from cursor: %d", r); |
| } |
| |
| /* allocate row */ |
| err = alloc_row (wdb, &my_row); |
| if (err) |
| { |
| free (data.data); |
| return nerr_pass(err); |
| } |
| |
| my_row->key_value = (char *) malloc (dkey.size + 1); |
| if (my_row->key_value == NULL) |
| { |
| free (data.data); |
| free (my_row); |
| return nerr_raise (NERR_NOMEM, "No memory for new row"); |
| } |
| |
| memcpy (my_row->key_value, dkey.data, dkey.size); |
| my_row->key_value[dkey.size] = '\0'; |
| |
| /* unpack row */ |
| err = unpack_row (wdb, data.data, data.size, my_row); |
| free (data.data); |
| free (dkey.data); |
| if (err) |
| { |
| free (my_row); |
| return nerr_pass(err); |
| } |
| |
| *row = my_row; |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdbr_find (WDB *wdb, WDBCursor *cursor, const char *key, WDBRow **row) |
| { |
| DBT dkey, data; |
| WDBRow *my_row; |
| NEOERR *err = STATUS_OK; |
| int r; |
| |
| *row = NULL; |
| |
| if (wdb->table_version != cursor->table_version) |
| { |
| return nerr_raise (NERR_ASSERT, "Cursor doesn't match database"); |
| } |
| |
| memset(&dkey, 0, sizeof(dkey)); |
| memset(&data, 0, sizeof(data)); |
| dkey.flags = DB_DBT_USERMEM; |
| data.flags = DB_DBT_MALLOC; |
| |
| dkey.data = (void *)key; |
| dkey.size = strlen(key); |
| |
| r = cursor->db_cursor->c_get (cursor->db_cursor, &dkey, &data, DB_SET_RANGE); |
| if (r == DB_NOTFOUND) |
| return STATUS_OK; |
| else if (r) |
| return nerr_raise (r, "Unable to get find item for key %s", key); |
| |
| /* allocate row */ |
| err = alloc_row (wdb, &my_row); |
| if (err) |
| { |
| free (data.data); |
| return nerr_pass(err); |
| } |
| |
| my_row->key_value = (char *) malloc (dkey.size + 1); |
| if (my_row->key_value == NULL) |
| { |
| free (data.data); |
| free (my_row); |
| return nerr_raise (NERR_NOMEM, "No memory for new row"); |
| } |
| |
| memcpy (my_row->key_value, dkey.data, dkey.size); |
| my_row->key_value[dkey.size] = '\0'; |
| |
| /* unpack row */ |
| err = unpack_row (wdb, data.data, data.size, my_row); |
| free (data.data); |
| if (err) |
| { |
| free (my_row); |
| return nerr_pass(err); |
| } |
| |
| *row = my_row; |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *wdb_keys (WDB *wdb, char **primary_key, ULIST **data) |
| { |
| NEOERR *err; |
| int x, len; |
| WDBColumn *col; |
| ULIST *my_data; |
| char *my_key = NULL; |
| char *my_col = NULL; |
| |
| *data = NULL; |
| *primary_key = NULL; |
| my_key = strdup(wdb->key); |
| if (my_key == NULL) |
| return nerr_raise (NERR_NOMEM, "Unable to allocate memory for keys"); |
| |
| len = uListLength(wdb->cols_l); |
| err = uListInit (&my_data, len, 0); |
| if (err != STATUS_OK) |
| { |
| free(my_key); |
| return nerr_pass(err); |
| } |
| |
| for (x = 0; x < len; x++) |
| { |
| err = uListGet (wdb->cols_l, x, (void *)&col); |
| if (err) goto key_err; |
| my_col = strdup(col->name); |
| if (my_col == NULL) |
| { |
| err = nerr_raise (NERR_NOMEM, "Unable to allocate memory for keys"); |
| goto key_err; |
| } |
| err = uListAppend (my_data, my_col); |
| my_col = NULL; |
| if (err) goto key_err; |
| } |
| |
| *data = my_data; |
| *primary_key = my_key; |
| return STATUS_OK; |
| |
| key_err: |
| if (my_key != NULL) free (my_key); |
| if (my_col != NULL) free (my_col); |
| *primary_key = NULL; |
| uListDestroy (&my_data, 0); |
| return nerr_pass(err); |
| } |
| |
| /* |
| * Known Issues: |
| * - Probably need to store the actual key value in the packed row.. |
| * Maybe not, because any cursor you use on a sleepycat db will |
| * return the key... |
| * - um, memory. Especially when dealing with rows, need to keep track |
| * of when we allocate, when we dealloc, and who owns that memory to |
| * free it |
| * - function to delete entry from wdb |
| */ |