// namespace.h
/**
* Copyright (C) 2008 10gen Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
#pragma once
#include "../util/hashtab.h"
#include "../util/mmap.h"
class Cursor;
#pragma pack(push,1)
class Namespace {
public:
Namespace(const char *ns) {
*this = ns;
}
Namespace& operator=(const char *ns) {
memset(buf, 0, 128); /* this is just to keep stuff clean in the files for easy dumping and reading */
strcpy_s(buf, 128, ns); return *this;
}
void kill() {
buf[0] = 0x7f;
}
bool operator==(const char *r) { return strcmp(buf, r) == 0; }
bool operator==(const Namespace& r) { return strcmp(buf, r.buf) == 0; }
int hash() const {
unsigned x = 0;
const char *p = buf;
while( *p ) {
x = x * 131 + *p;
p++;
}
return (x & 0x7fffffff) | 0x8000000; // must be > 0
}
char buf[128];
};
const int Buckets = 19;
const int MaxBucket = 18;
const int MaxIndexes = 10;
class IndexDetails {
public:
DiskLoc head; /* btree head */
/* Location of index info object. Format:
{ name:"nameofindex", ns:"parentnsname", key: {keypattobject} }
This object is in the system.indexes collection. Note that since we
have a pointer to the object here, the object in system.indexes must
never move.
*/
DiskLoc info;
/* pull out the relevant key objects from obj, so we
can index them. Note that the set is multiple elements
only when it's a "multikey" array.
keys will be left empty if key not found in the object.
*/
void getKeysFromObject(JSObj& obj, set& keys);
/* get the key pattern for this object.
e.g., { lastname:1, firstname:1 }
*/
JSObj key() {
return info.obj().getObjectField("key");
}
// returns name of this index's storage area
// client.table.$index
string indexNamespace() {
JSObj io = info.obj();
string s;
s.reserve(128);
s = io.getStringField("ns");
assert( !s.empty() );
s += ".$";
s += io.getStringField("name");
return s;
}
string indexName() { // e.g. "ts_1"
JSObj io = info.obj();
return io.getStringField("name");
}
/* gets not our namespace name (indexNamespace for that),
but the collection we index, its name.
*/
string parentNS() {
JSObj io = info.obj();
return io.getStringField("ns");
}
/* delete this index. does NOT celan up the system catalog
(system.indexes or system.namespaces) -- only NamespaceIndex.
*/
void kill();
};
extern int bucketSizes[];
/* this is the "header" for a collection that has all its details. in the .ns file.
*/
class NamespaceDetails {
public:
NamespaceDetails() {
/* be sure to initialize new fields here -- doesn't default to zeroes the way we use it */
datasize = nrecords = 0;
lastExtentSize = 0;
nIndexes = 0;
capped = 0;
max = 0x7fffffff;
paddingFactor = 1.0;
flags = 0;
memset(reserved, 0, sizeof(reserved));
}
DiskLoc firstExtent;
DiskLoc lastExtent;
DiskLoc deletedList[Buckets];
long long datasize;
long long nrecords;
int lastExtentSize;
int nIndexes;
IndexDetails indexes[MaxIndexes];
int capped;
int max; // max # of objects for a capped table.
double paddingFactor; // 1.0 = no padding.
int flags;
char reserved[256-16-4-4-8*MaxIndexes-8-8-8-4];
enum {
Flag_HaveIdIndex = 1 // set when we have _id index (ONLY if ensureIdIndex was called -- 0 if that has never been called)
};
/* you MUST call when adding an index. see pdfile.cpp */
void addingIndex(const char *thisns, IndexDetails& details);
void aboutToDeleteAnIndex() { flags &= ~Flag_HaveIdIndex; }
/* returns index of the first index in which the field is present. -1 if not present. */
int fieldIsIndexed(const char *fieldName);
void paddingFits() {
double x = paddingFactor - 0.01;
if( x >= 1.0 )
paddingFactor = x;
}
void paddingTooSmall() {
double x = paddingFactor + 0.6;
if( x <= 2.0 )
paddingFactor = x;
}
//returns offset in indexes[]
int findIndexByName(const char *name) {
for( int i = 0; i < nIndexes; i++ ) {
if( strcmp(indexes[i].info.obj().getStringField("name"),name) == 0 )
return i;
}
return -1;
}
/* return which "deleted bucket" for this size object */
static int bucket(int n) {
for( int i = 0; i < Buckets; i++ )
if( bucketSizes[i] > n )
return i;
return Buckets-1;
}
/* allocate a new record. lenToAlloc includes headers. */
DiskLoc alloc(const char *ns, int lenToAlloc, DiskLoc& extentLoc);
/* add a given record to the deleted chains for this NS */
void addDeletedRec(DeletedRecord *d, DiskLoc dloc);
void dumpDeleted(set *extents = 0);
private:
DiskLoc __stdAlloc(int len);
DiskLoc _alloc(const char *ns, int len);
void compact();
};
#pragma pack(pop)
/* these are things we know / compute about a namespace that are transient -- things
we don't actually store in the .ns file. so mainly caching of frequently used
information.
CAUTION: Are you maintaining this properly on a collection drop()? A dropdatabase()? Be careful.
The current field "allIndexKeys" may have too many keys in it on such an occurrence;
as currently used that does not cause anything terrible to happen.
*/
class NamespaceDetailsTransient : boost::noncopyable {
string ns;
bool haveIndexKeys;
set allIndexKeys;
void computeIndexKeys();
public:
NamespaceDetailsTransient(const char *_ns) : ns(_ns) { haveIndexKeys=false; /*lazy load them*/ }
/* get set of index keys for this namespace. handy to quickly check if a given
field is indexed (Note it might be a seconary component of a compound index.)
*/
set& indexKeys() {
if( !haveIndexKeys ) { haveIndexKeys=true; computeIndexKeys(); }
return allIndexKeys;
}
void addedIndex() { haveIndexKeys=false; }
private:
static map map;
public:
static NamespaceDetailsTransient& get(const char *ns);
};
/* NamespaceIndex is the ".ns" file you see in the data directory. It is the "system catalog"
if you will: at least the core parts. (Additional info in system.* collections.)
*/
class NamespaceIndex {
friend class NamespaceCursor;
public:
NamespaceIndex() { }
/* returns true if we created (did not exist) during init() */
bool init(const char *dir, const char *client) {
string path = dir;
path += client;
path += ".ns";
bool created = !boost::filesystem::exists(path);
const int LEN = 16 * 1024 * 1024;
void *p = f.map(path.c_str(), LEN);
if( p == 0 ) {
problem() << "couldn't open namespace.idx " << path.c_str() << " terminating" << endl;
exit(-3);
}
ht = new HashTable(p, LEN, "namespace index");
return created;
}
void add(const char *ns, DiskLoc& loc) {
Namespace n(ns);
NamespaceDetails details;
details.lastExtent = details.firstExtent = loc;
ht->put(n, details);
}
/* just for diagnostics */
size_t detailsOffset(NamespaceDetails *d) {
return ((char *) d) - (char *) ht->nodes;
}
NamespaceDetails* details(const char *ns) {
Namespace n(ns);
return ht->get(n);
}
void kill(const char *ns) {
Namespace n(ns);
ht->kill(n);
}
bool find(const char *ns, DiskLoc& loc) {
NamespaceDetails *l = details(ns);
if( l ) {
loc = l->firstExtent;
return true;
}
return false;
}
private:
MemoryMappedFile f;
HashTable *ht;
};
extern const char *dbpath;
// "client.a.b.c" -> "client"
const int MaxClientLen = 256;
inline void nsToClient(const char *ns, char *client) {
const char *p = ns;
char *q = client;
while( *p != '.' ) {
if( *p == 0 ) {
assert(false);
*client = 0;
return;
}
*q++ = *p++;
}
*q = 0;
if(q-client>=MaxClientLen) {
problem() << "nsToClient: ns too long. terminating, buf overrun condition" << endl;
dbexit(60);
}
}