/* * Copyright (c) 1995, 1996 Gunther Schadow. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "pg_config.h" IDENT("@(#) odbm-index.cc (Gunther Schadow) 12/19/96"); #include "odbm.h" #include #include #include #include /****************************************************************** * Index Facility * * an index entry is a special key/data pair, whose key is the value of * the index variable in a tuple and whose data is the key of the same * tuple. * the key of the index entry has the following layout: * * 1. Tag (one byte) -- identifies the key as an index(>0) entry * 2. Counter (u_int) -- used to create unique keys * 3. Value (string) -- the index value * * The value of the Tag byte is the ordinal number of the corresponding * string, i.e. the index to the strings[] array is this value decreased * by 1. * * The value of the index is the key of the data entry appended with one * byte, that tells the index manager whether it is an aoutomatically * created index (index_on()) or a manually created index (index()). * The values are: 0 for automatic and 1 for manual. In order to faciliate * the use of the index type byte, the function build_key() allocates it * already. * * The next free counter for a partiular index value is stored in an index * counter entry in the database. This index counter entry is a key/value * pair whose key is the index key with a counter of (u_int)-1 and * whose value contains an unsigned integer. */ /* * set an index on a variable */ void odbm::index_on(char * const *variable) const { odbm *that = (odbm *)this; that->indices.set(stringno(variable) - 1); } /* * find the ordinal number of a string (for building the index tag) * 0 is the key * > 0 is an index * -1 is neither key nor index (i.e. can not be found in the database) */ int odbm::stringno(const char *x) const { if(x == NULL || *keyp == NULL) return -1; if(x == *keyp) return 0; else for(int i = 0; i < nostrings; i++) if(x == strings[i]) return i + 1; return -1; } int odbm::stringno(const char * const *x) const { int diff; if((char **)x == keyp) return 0; else { diff = x - strings; if(0 <= diff && diff < nostrings) return diff + 1; else return -1; } } #define AUTO_IDX ((char)0) #define MANU_IDX ((char)1) /* * insert the automatic indices for a record * datum *k points to the actual key */ void odbm::insert_indices() const { for(int i = indices.first(); i >= 0; i = indices.next(i)) { char *idx = strings[i]; u_int index_cnt = 0; datum index_key; datum index_cnt_value; if(idx != NULL) index_key.dsize = 1 + sizeof(u_int) + strlen(idx) + 1; else index_key.dsize = 1 + sizeof(u_int) + 1; index_key.dptr = (char *)alloca(index_key.dsize); index_key.dptr[0] = (char)(i + 1); if(idx != NULL) strcpy(&index_key.dptr[1 + sizeof(u_int)], idx); else index_key.dptr[1 + sizeof(u_int)] = 0; // index counter entry { u_int numbuf = (u_int)-1; memcpy(&index_key.dptr[1], &numbuf, sizeof(u_int)); } index_cnt_value = gdbm_fetch(*dbfp, index_key); if(index_cnt_value.dptr == NULL) { index_cnt_value.dsize = sizeof(u_int); index_cnt_value.dptr = (char *)alloca(sizeof(u_int)); } else index_cnt = *(u_int*)index_cnt_value.dptr; *(u_int*)index_cnt_value.dptr = index_cnt + 1; if(gdbm_store(*dbfp, index_key, index_cnt_value, GDBM_REPLACE) != SUCCESS) ERROS("gdbm_store:index_cnt %d`%s': %s", index_cnt, idx, gdbm_strerror(gdbm_errno)); // index entry memcpy(&index_key.dptr[1], &index_cnt, sizeof(u_int)); datum &the_key = *(datum*)&key; // make the key writable the_key.dptr[the_key.dsize++] = AUTO_IDX; // the index type if(gdbm_store(*dbfp, index_key, the_key, GDBM_INSERT) != SUCCESS) ERROS("gdbm_store:index %d`%s': %s", index_cnt, idx, gdbm_strerror(gdbm_errno)); the_key.dsize--; // restore the size of the key } } /* * Dereferenciate an index and return the data record. Retries until the * maxcursor value for that index is reached. If it fails, returns NULL * in the `dptr' value and leaves the key argument untouched. */ datum odbm::deref_index(int stringno, const char *idx, u_int *cursor, datum &key) const { datum index_key, data, old_key = key; key = (datum){NULL, 0}; if(idx != NULL) index_key.dsize = 1 + sizeof(u_int) + strlen(idx) + 1; else index_key.dsize = 1 + sizeof(u_int) + 1; index_key.dptr = (char *)alloca(index_key.dsize); index_key.dptr[0] = stringno; if(idx != NULL) strcpy(&index_key.dptr[1 + sizeof(u_int)], idx); else index_key.dptr[1 + sizeof(u_int)] = 0; // prepare for trying up to max_cursor value // get the index counter entry for max_cursor u_int max_cursor; { u_int numbuf = (u_int)-1; memcpy(&index_key.dptr[1], &numbuf, sizeof(u_int)); } datum index_cnt_value = gdbm_fetch(*dbfp, index_key); if(index_cnt_value.dptr == NULL) goto fail; // index does not exist else max_cursor = *(u_int*)index_cnt_value.dptr; // try up to max_cursor value while(*cursor < max_cursor) { memcpy(&index_key.dptr[1], cursor, sizeof(u_int)); key = gdbm_fetch(*dbfp, index_key); if(key.dptr != NULL) { key.dsize--; // restore the size of the key (was: index type byte) data = gdbm_fetch(*dbfp, key); if(data.dptr != NULL) return data; } (*cursor)++; } fail: if(key.dptr != NULL) delete [] key.dptr; key = old_key; return (datum){NULL, 0}; // failed } /* * Manually set an index to the current record. A manually set index * has the index type byte set to one. This indicates that reindex won't * remove this index entry (it may still change it's cursor counter). */ void odbm::index(char * const *variable, const char *value) const { int strno = stringno(variable); u_int index_cnt = 0; datum index_key; datum index_cnt_value; if(value != NULL) index_key.dsize = 1 + sizeof(u_int) + strlen(value) + 1; else index_key.dsize = 1 + sizeof(u_int) + 1; index_key.dptr = (char *)alloca(index_key.dsize); index_key.dptr[0] = strno; if(value != NULL) strcpy(&index_key.dptr[1 + sizeof(u_int)], value); else index_key.dptr[1 + sizeof(u_int)] = 0; // index counter entry { u_int numbuf = (u_int)-1; memcpy(&index_key.dptr[1], &numbuf, sizeof(u_int)); } index_cnt_value = gdbm_fetch(*dbfp, index_key); if(index_cnt_value.dptr == NULL) { index_cnt_value.dsize = sizeof(u_int); index_cnt_value.dptr = (char *)alloca(sizeof(u_int)); } else index_cnt = *(u_int*)index_cnt_value.dptr; *(u_int*)index_cnt_value.dptr = index_cnt + 1; if(gdbm_store(*dbfp, index_key, index_cnt_value, GDBM_REPLACE) != SUCCESS) ERROS("gdbm_store:index_cnt %d`%s': %s", index_cnt, value, gdbm_strerror(gdbm_errno)); // index entry memcpy(&index_key.dptr[1], &index_cnt, sizeof(u_int)); datum &the_key = *(datum*)&key; // make the key writable the_key.dptr[the_key.dsize++] = MANU_IDX; // the index type if(gdbm_store(*dbfp, index_key, the_key, GDBM_INSERT) != SUCCESS) ERROS("gdbm_store:index %d`%s': %s", index_cnt, value, gdbm_strerror(gdbm_errno)); the_key.dsize--; // restore the size of the key } u_int odbm::index_counter(int field, const char *val) const { // 1. Allocate a data block datum index_key; if(val != NULL) index_key.dsize = 1 + sizeof(u_int) + strlen(val) + 1; else index_key.dsize = 1 + sizeof(u_int) + 1; index_key.dptr = (char *)alloca(index_key.dsize); // 2. Fill in the field number (1st byte) as the index tag index_key.dptr[0] = field; // 3. Fill in the cursor (u_int)-1 as the counter tag u_int numbuf = (u_int)-1; memcpy(&index_key.dptr[1], &numbuf, sizeof(u_int)); // 4. Fill in the actual index value if(val != NULL) strcpy(&index_key.dptr[1 + sizeof(u_int)], val); else index_key.dptr[1 + sizeof(u_int)] = '\0'; // NULL -> "" // 5. Fetch the index counter record and return counter value datum index_cnt_value = gdbm_fetch(*dbfp, index_key); if(index_cnt_value.dptr == NULL) return 0; // index does not exist else return *(u_int*)index_cnt_value.dptr; }