/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
 * 
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is Netscape 
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1999 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s): 
 */

#ifndef _MDB_
#define _MDB_ 1

//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789

// { %%%%% begin scalar typedefs %%%%%
typedef unsigned char  mdb_u1;  // make sure this is one byte
typedef unsigned short mdb_u2;  // make sure this is two bytes
typedef short          mdb_i2;  // make sure this is two bytes
typedef unsigned long  mdb_u4;  // make sure this is four bytes
typedef long           mdb_i4;  // make sure this is four bytes
typedef long           mdb_ip;  // make sure sizeof(mdb_ip) == sizeof(void*)

typedef mdb_u1 mdb_bool;  // unsigned byte with zero=false, nonzero=true

/* canonical boolean constants provided only for code clarity: */
#define mdbBool_kTrue  ((mdb_bool) 1) /* actually any nonzero means true */
#define mdbBool_kFalse ((mdb_bool) 0) /* only zero means false */

typedef mdb_u4 mdb_id;    // unsigned object identity in a scope
typedef mdb_id mdb_rid;          // unsigned row identity inside scope
typedef mdb_id mdb_tid;          // unsigned table identity inside scope
typedef mdb_u4 mdb_token; // unsigned token for atomized string
typedef mdb_token mdb_scope;     // token used to id scope for rows
typedef mdb_token mdb_kind;      // token used to id kind for tables
typedef mdb_token mdb_column;    // token used to id columns for rows
typedef mdb_token mdb_cscode;    // token used to id charset names
typedef mdb_u4 mdb_seed;  // unsigned collection change counter
typedef mdb_u4 mdb_count; // unsigned collection member count
typedef mdb_u4 mdb_size;  // unsigned physical media size
typedef mdb_u4 mdb_fill;  // unsigned logical content size
typedef mdb_u4 mdb_more;  // more available bytes for larger buffer

#define mdbId_kNone ((mdb_id) -1) /* never a valid Mork object ID */

typedef mdb_u4 mdb_percent; // 0..100, with values >100 same as 100

typedef mdb_u1 mdb_priority; // 0..9, for a total of ten different values

// temporary substitute for NS_RESULT, for mdb.h standalone compilation:
typedef mdb_u4 mdb_err;   // equivalent to NS_RESULT

// sequence position is signed; negative is useful to mean "before first":
typedef mdb_i4 mdb_pos; // signed zero-based ordinal collection position

#define mdbPos_kBeforeFirst ((mdb_pos) -1) /* any negative is before zero */

// order is also signed, so we can use three states for comparison order:
typedef mdb_i4 mdb_order; // neg:lessthan, zero:equalto, pos:greaterthan 

typedef mdb_order (* mdbAny_Order)(const void* inA, const void* inB, 
  const void* inClosure);

// } %%%%% end scalar typedefs %%%%%

// { %%%%% begin C structs %%%%%

#ifndef mdbScopeStringSet_typedef
typedef struct mdbScopeStringSet mdbScopeStringSet;
#define mdbScopeStringSet_typedef 1
#endif

/*| mdbScopeStringSet: a set of null-terminated C strings that enumerate some
**| names of row scopes, so that row scopes intended for use by an application
**| can be declared by an app when trying to open or create a database file.
**| (We use strings and not tokens because we cannot know the tokens for any
**| particular db without having first opened the db.)  The goal is to inform
**| a db runtime that scopes not appearing in this list can be given relatively
**| short shrift in runtime representation, with the expectation that other
**| scopes will not actually be used.  However, a db should still be prepared
**| to handle accessing row scopes not in this list, rather than raising errors.
**| But it could be quite expensive to access a row scope not on the list.
**| Note a zero count for the string set means no such string set is being
**| specified, and that a db should handle all row scopes efficiently. 
**| (It does NOT mean an app plans to use no content whatsoever.)
|*/
#ifndef mdbScopeStringSet_struct
#define mdbScopeStringSet_struct 1
struct mdbScopeStringSet { // vector of scopes for use in db opening policy
  // when mScopeStringSet_Count is zero, this means no scope constraints 
  mdb_count     mScopeStringSet_Count;    // number of strings in vector below
  const char**  mScopeStringSet_Strings;  // null-ended ascii scope strings
};
#endif /*mdbScopeStringSet_struct*/

#ifndef mdbOpenPolicy_typedef
typedef struct mdbOpenPolicy mdbOpenPolicy;
#define mdbOpenPolicy_typedef 1
#endif

#ifndef mdbOpenPolicy_struct
#define mdbOpenPolicy_struct 1
struct mdbOpenPolicy { // policies affecting db usage for ports and stores
  mdbScopeStringSet  mOpenPolicy_ScopePlan; // predeclare scope usage plan
  mdb_bool           mOpenPolicy_MaxLazy;   // nonzero: do least work
  mdb_bool           mOpenPolicy_MinMemory; // nonzero: use least memory
};
#endif /*mdbOpenPolicy_struct*/

#ifndef mdbTokenSet_typedef
typedef struct mdbTokenSet mdbTokenSet;
#define mdbTokenSet_typedef 1
#endif

#ifndef mdbTokenSet_struct
#define mdbTokenSet_struct 1
struct mdbTokenSet { // array for a set of tokens, and actual slots used
  mdb_count   mTokenSet_Count;   // number of token slots in the array
  mdb_fill    mTokenSet_Fill;    // the subset of count slots actually used
  mdb_more    mTokenSet_More;    // more tokens available for bigger array
  mdb_token*  mTokenSet_Tokens;  // array of count mdb_token instances
};
#endif /*mdbTokenSet_struct*/

#ifndef mdbUsagePolicy_typedef
typedef struct mdbUsagePolicy mdbUsagePolicy;
#define mdbUsagePolicy_typedef 1
#endif

/*| mdbUsagePolicy: another version of mdbOpenPolicy which uses tokens instead
**| of scope strings, because usage policies can be constructed for use with a
**| db that is already open, while an open policy must be constructed before a
**| db has yet been opened.
|*/
#ifndef mdbUsagePolicy_struct
#define mdbUsagePolicy_struct 1
struct mdbUsagePolicy { // policies affecting db usage for ports and stores
  mdbTokenSet  mUsagePolicy_ScopePlan; // current scope usage plan
  mdb_bool     mUsagePolicy_MaxLazy;   // nonzero: do least work
  mdb_bool     mUsagePolicy_MinMemory; // nonzero: use least memory
};
#endif /*mdbUsagePolicy_struct*/

#ifndef mdbOid_typedef
typedef struct mdbOid mdbOid;
#define mdbOid_typedef 1
#endif

#ifndef mdbOid_struct
#define mdbOid_struct 1
struct mdbOid { // identity of some row or table inside a database
  mdb_scope   mOid_Scope;  // scope token for an id's namespace
  mdb_id      mOid_Id;     // identity of object inside scope namespace
};
#endif /*mdbOid_struct*/

#ifndef mdbRange_typedef
typedef struct mdbRange mdbRange;
#define mdbRange_typedef 1
#endif

#ifndef mdbRange_struct
#define mdbRange_struct 1
struct mdbRange { // range of row positions in a table
  mdb_pos   mRange_FirstPos;  // position of first row
  mdb_pos   mRange_LastPos;   // position of last row
};
#endif /*mdbRange_struct*/

#ifndef mdbColumnSet_typedef
typedef struct mdbColumnSet mdbColumnSet;
#define mdbColumnSet_typedef 1
#endif

#ifndef mdbColumnSet_struct
#define mdbColumnSet_struct 1
struct mdbColumnSet { // array of column tokens (just the same as mdbTokenSet)
  mdb_count    mColumnSet_Count;    // number of columns
  mdb_column*  mColumnSet_Columns;  // count mdb_column instances
};
#endif /*mdbColumnSet_struct*/

#ifndef mdbYarn_typedef
typedef struct mdbYarn mdbYarn;
#define mdbYarn_typedef 1
#endif

#ifdef MDB_BEGIN_C_LINKAGE_define
#define MDB_BEGIN_C_LINKAGE_define 1
#define MDB_BEGIN_C_LINKAGE extern "C" {
#define MDB_END_C_LINKAGE }
#endif /*MDB_BEGIN_C_LINKAGE_define*/

/*| mdbYarn_mGrow: an abstract API for growing the size of a mdbYarn
**| instance.  With respect to a specific API that requires a caller
**| to supply a string (mdbYarn) that a callee fills with content
**| that might exceed the specified size, mdbYarn_mGrow is a caller-
**| supplied means of letting a callee attempt to increase the string
**| size to become large enough to receive all content available.
**|
**|| Grow(): a method for requesting that a yarn instance be made
**| larger in size.  Note that such requests need not be honored, and
**| need not be honored in full if only partial size growth is desired.
**| (Note that no nsIMdbEnv instance is passed as argument, although one
**| might be needed in some circumstances.  So if an nsIMdbEnv is needed,
**| a reference to one might be held inside a mdbYarn member slot.)
**|
**|| self: a yarn instance to be grown.  Presumably this yarn is
**| the instance which holds the mYarn_Grow method pointer.  Yarn
**| instancesshould only be passed to grow methods which they were
**| specifically designed to fit, as indicated by the mYarn_Grow slot.
**|
**|| inNewSize: the new desired value for slot mYarn_Size in self.
**| If mYarn_Size is already this big, then nothing should be done.
**| If inNewSize is larger than seems feasible or desirable to honor,
**| then any size restriction policy can be used to grow to some size
**| greater than mYarn_Size.  (Grow() might even grow to a size
**| greater than inNewSize in order to make the increase in size seem
**| worthwhile, rather than growing in many smaller steps over time.)
|*/
typedef void (* mdbYarn_mGrow)(mdbYarn* self, mdb_size inNewSize);
// mdbYarn_mGrow methods must be declared with C linkage in C++

/*| mdbYarn: a variable length "string" of arbitrary binary bytes,
**| whose length is mYarn_Fill, inside a buffer mYarn_Buf that has
**| at most mYarn_Size byte of physical space.
**|
**|| mYarn_Buf: a pointer to space containing content.  This slot
**| might never be nil when mYarn_Size is nonzero, but checks for nil
**| are recommended anyway.
**| (Implementations of mdbYarn_mGrow methods should take care to
**| ensure the existence of a replacement before dropping old Bufs.)
**| Content in Buf can be anything in any format, but the mYarn_Form
**| implies the actual format by some caller-to-callee convention.
**| mYarn_Form==0 implies US-ASCII iso-8859-1 Latin1 string content.
**|
**|| mYarn_Size: the physical size of Buf in bytes.  Note that if one
**| intends to terminate a string with a null byte, that it must not
**| be written at or after mYarn_Buf[mYarn_Size] because this is after
**| the last byte in the physical buffer space.  Size can be zero,
**| which means the string has no content whatsoever; note that when
**| Size is zero, this is a suitable reason for Buf==nil as well.
**|
**|| mYarn_Fill: the logical content in Buf in bytes, where Fill must
**| never exceed mYarn_Size.  Note that yarn strings might not have a
**| terminating null byte (since they might not even be C strings), but
**| when they do, such terminating nulls are considered part of content
**| and therefore Fill will count such null bytes.  So an "empty" C
**| string will have Fill==1, because content includes one null byte.
**| Fill does not mean "length" when applied to C strings for this
**| reason.  However, clients using yarns to hold C strings can infer
**| that length is equal to Fill-1 (but should take care to handle the
**| case where Fill==0).  To be paranoid, one can always copy to a
**| destination with size exceeding Fill, and place a redundant null
**| byte in the Fill position when this simplifies matters.
**|
**|| mYarn_Form: a designation of content format within mYarn_Buf.
**| The semantics of this slot are the least well defined, since the
**| actual meaning is context dependent, to the extent that callers
**| and callees must agree on format encoding conventions when such
**| are not standardized in many computing contexts.  However, in the
**| context of a specific mdb database, mYarn_Form is a token for an
**| atomized string in that database that typically names a preferred
**| mime type charset designation.  If and when mdbYarn is used for
**| other purposes away from the mdb interface, folks can use another
**| convention system for encoding content formats.  However, in all
**| contexts is it useful to maintain the convention that Form==0
**| implies Buf contains US-ASCII iso-8859-1 Latin1 string content.
**|
**|| mYarn_Grow: either a mdbYarn_mGrow method, or else nil.  When
**| a mdbYarn_mGrow method is provided, this method can be used to
**| request a yarn buf size increase.  A caller who constructs the 
**| original mdbYarn instance decides whether a grow method is necessary
**| or desirable, and uses only grow methods suitable for the buffering
**| nature of a specific mdbYarn instance.  (For example, Buf might be a
**| staticly allocated string space which switches to something heap-based
**| when grown, and subsequent calls to grow the yarn must distinguish the
**| original static string from heap allocated space, etc.) Note that the
**| method stored in mYarn_Grow can change, and this might be a common way
**| to track memory managent changes in policy for mYarn_Buf.
|*/
#ifndef mdbYarn_struct
#define mdbYarn_struct 1
struct mdbYarn { // buffer with caller space allocation semantics
  void*         mYarn_Buf;   // space for holding any binary content
  mdb_fill      mYarn_Fill;  // logical content in Buf in bytes
  mdb_size      mYarn_Size;  // physical size of Buf in bytes
  mdb_more      mYarn_More;  // more available bytes if Buf is bigger
  mdb_cscode    mYarn_Form;  // charset format encoding
  mdbYarn_mGrow mYarn_Grow;  // optional method to grow mYarn_Buf
  
  // Subclasses might add further slots after mYarn_Grow in order to
  // maintain bookkeeping needs, such as state info about mYarn_Buf.
};
#endif /*mdbYarn_struct*/

// } %%%%% end C structs %%%%%

// { %%%%% begin class forward defines %%%%%
class nsIMdbISupports;
class nsIMdbEnv;
class nsIMdbObject;
class nsIMdbErrorHook;
class nsIMdbCompare;
class nsIMdbThumb;
class nsIMdbFactory;
class nsIMdbFile;
class nsIMdbPort;
class nsIMdbStore;
class nsIMdbCursor;
class nsIMdbPortTableCursor;
class nsIMdbCollection;
class nsIMdbTable;
class nsIMdbTableRowCursor;
class nsIMdbRow;
class nsIMdbRowCellCursor;
class nsIMdbBlob;
class nsIMdbCell;
class nsIMdbSorting;
// } %%%%% end class forward defines %%%%%

// { %%%%% begin temporary dummy base class for class hierarchy %%%%%
class nsIMdbISupports { // msg db base class
public:

// { ===== begin nsIMdbISupports methods =====
  virtual mdb_err AddRef() = 0; // add strong ref with no
  virtual mdb_err Release() = 0; // cut strong ref
// } ===== end nsIMdbObject methods =====
};
// } %%%%% end temporary dummy base class for class hierarchy %%%%%


// { %%%%% begin C++ abstract class interfaces %%%%%

/*| nsIMdbObject: base class for all message db class interfaces
**|
**|| factory: all nsIMdbObjects from the same code suite have the same factory
**|
**|| refcounting: both strong and weak references, to ensure strong refs are
**| acyclic, while weak refs can cause cycles.  CloseMdbObject() is
**| called when (strong) use counts hit zero, but clients can call this close
**| method early for some reason, if absolutely necessary even though it will
**| thwart the other uses of the same object.  Note that implementations must
**| cope with close methods being called arbitrary numbers of times.  The COM
**| calls to AddRef() and release ref map directly to strong use ref calls,
**| but the total ref count for COM objects is the sum of weak & strong refs.
|*/
class nsIMdbObject : public nsIMdbISupports { // msg db base class
public:

// { ===== begin nsIMdbObject methods =====

  // { ----- begin attribute methods -----
  virtual mdb_err IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly) = 0;
  // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port.
  // } ----- end attribute methods -----

  // { ----- begin factory methods -----
  virtual mdb_err GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory) = 0; 
  // } ----- end factory methods -----

  // { ----- begin ref counting for well-behaved cyclic graphs -----
  virtual mdb_err GetWeakRefCount(nsIMdbEnv* ev, // weak refs
    mdb_count* outCount) = 0;  
  virtual mdb_err GetStrongRefCount(nsIMdbEnv* ev, // strong refs
    mdb_count* outCount) = 0;

  virtual mdb_err AddWeakRef(nsIMdbEnv* ev) = 0;
  virtual mdb_err AddStrongRef(nsIMdbEnv* ev) = 0;

  virtual mdb_err CutWeakRef(nsIMdbEnv* ev) = 0;
  virtual mdb_err CutStrongRef(nsIMdbEnv* ev) = 0;
  
  virtual mdb_err CloseMdbObject(nsIMdbEnv* ev) = 0; // called at strong refs zero
  virtual mdb_err IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen) = 0;
  // } ----- end ref counting -----
  
// } ===== end nsIMdbObject methods =====
};

/*| nsIMdbErrorHook: a base class for clients of this API to subclass, in order
**| to provide a callback installable in nsIMdbEnv for error notifications. If
**| apps that subclass nsIMdbErrorHook wish to maintain a reference to the env
**| that contains the hook, then this should be a weak ref to avoid cycles.
**|
**|| OnError: when nsIMdbEnv has an error condition that causes the total count
**| of errors to increase, then nsIMdbEnv should call OnError() to report the
**| error in some fashion when an instance of nsIMdbErrorHook is installed.  The
**| variety of string flavors is currently due to the uncertainty here in the
**| nsIMdbBlob and nsIMdbCell interfaces.  (Note that overloading by using the
**| same method name is not necessary here, and potentially less clear.)
|*/
class nsIMdbErrorHook { // env callback handler to report errors
public:

// { ===== begin error methods =====
  virtual mdb_err OnErrorString(nsIMdbEnv* ev, const char* inAscii) = 0;
  virtual mdb_err OnErrorYarn(nsIMdbEnv* ev, const mdbYarn* inYarn) = 0;
// } ===== end error methods =====

// { ===== begin warning methods =====
  virtual mdb_err OnWarningString(nsIMdbEnv* ev, const char* inAscii) = 0;
  virtual mdb_err OnWarningYarn(nsIMdbEnv* ev, const mdbYarn* inYarn) = 0;
// } ===== end warning methods =====

// { ===== begin abort hint methods =====
  virtual mdb_err OnAbortHintString(nsIMdbEnv* ev, const char* inAscii) = 0;
  virtual mdb_err OnAbortHintYarn(nsIMdbEnv* ev, const mdbYarn* inYarn) = 0;
// } ===== end abort hint methods =====
};

/*| nsIMdbCompare: a caller-supplied yarn comparison interface.  When two yarns
**| are compared to each other with Order(), this method should return a signed
**| long integer denoting relation R between the 1st and 2nd yarn instances
**| such that (First R Second), where negative is less than, zero is equal to,
**| and positive is greater than.  Note that both yarns are readonly, and the
**| Order() method should make no attempt to modify the yarn content.
|*/
class nsIMdbCompare { // caller-supplied yarn comparison
public:

// { ===== begin nsIMdbCompare methods =====
  virtual mdb_err Order(nsIMdbEnv* ev,      // compare first to second yarn
    const mdbYarn* inFirst,   // first yarn in comparison
    const mdbYarn* inSecond,  // second yarn in comparison
    mdb_order* outOrder) = 0; // negative="<", zero="=", positive=">"
    
  virtual mdb_err AddStrongRef(nsIMdbEnv* ev) = 0; // does nothing
  virtual mdb_err CutStrongRef(nsIMdbEnv* ev) = 0; // does nothing
// } ===== end nsIMdbCompare methods =====
  
};

/*| nsIMdbHeap: abstract memory allocation interface. 
**|
**|| Alloc: return a block at least inSize bytes in size with alignment
**| suitable for any native type (such as long integers).  When no such
**| block can be allocated, failure is indicated by a null address in
**| addition to reporting an error in the environment.
**|
**|| Free: deallocate a block allocated or resized earlier by the same
**| heap instance.  If the inBlock parameter is nil, the heap should do
**| nothing (and crashing is strongly discouraged).
|*/
class nsIMdbHeap { // caller-supplied memory management interface
public:
// { ===== begin nsIMdbHeap methods =====
  virtual mdb_err Alloc(nsIMdbEnv* ev, // allocate a piece of memory
    mdb_size inSize,        // requested byte size of new memory block 
    void** outBlock) = 0;   // memory block of inSize bytes, or nil
    
  virtual mdb_err Free(nsIMdbEnv* ev, // free block from Alloc or Resize()
    void* ioBlock) = 0;     // block to be destroyed/deallocated
    
  virtual mdb_err HeapAddStrongRef(nsIMdbEnv* ev) = 0;
  virtual mdb_err HeapCutStrongRef(nsIMdbEnv* ev) = 0;
    
// } ===== end nsIMdbHeap methods =====
};

/*| nsIMdbCPlusHeap: Alloc() with global ::new(), Free() with global ::delete(). 
**| Resize() is done by ::new() followed by ::delete().
|*/
class nsIMdbCPlusHeap { // caller-supplied memory management interface
public:
// { ===== begin nsIMdbHeap methods =====
  virtual mdb_err Alloc(nsIMdbEnv* ev, // allocate a piece of memory
    mdb_size inSize,   // requested size of new memory block 
    void** outBlock);  // memory block of inSize bytes, or nil
    
  virtual mdb_err Free(nsIMdbEnv* ev, // free block allocated earlier by Alloc()
    void* inBlock);
    
  virtual mdb_err HeapAddStrongRef(nsIMdbEnv* ev);
  virtual mdb_err HeapCutStrongRef(nsIMdbEnv* ev);
// } ===== end nsIMdbHeap methods =====
};

/*| nsIMdbThumb: 
|*/
class nsIMdbThumb : public nsIMdbObject { // closure for repeating incremental method
public:

// { ===== begin nsIMdbThumb methods =====
  virtual mdb_err GetProgress(nsIMdbEnv* ev,
    mdb_count* outTotal,    // total somethings to do in operation
    mdb_count* outCurrent,  // subportion of total completed so far
    mdb_bool* outDone,      // is operation finished?
    mdb_bool* outBroken     // is operation irreparably dead and broken?
  ) = 0;
  
  virtual mdb_err DoMore(nsIMdbEnv* ev,
    mdb_count* outTotal,    // total somethings to do in operation
    mdb_count* outCurrent,  // subportion of total completed so far
    mdb_bool* outDone,      // is operation finished?
    mdb_bool* outBroken     // is operation irreparably dead and broken?
  ) = 0;
  
  virtual mdb_err CancelAndBreakThumb( // cancel pending operation
    nsIMdbEnv* ev) = 0;
// } ===== end nsIMdbThumb methods =====
};

/*| nsIMdbEnv: a context parameter used when calling most abstract db methods.
**| The main purpose of such an object is to permit a database implementation
**| to avoid the use of globals to share information between various parts of
**| the implementation behind the abstract db interface.  An environment acts
**| like a session object for a given calling thread, and callers should use
**| at least one different nsIMdbEnv instance for each thread calling the API.
**| While the database implementation might not be threaded, it is highly
**| desirable that the db be thread-safe if calling threads use distinct
**| instances of nsIMdbEnv.  Callers can stop at one nsIMdbEnv per thread, or they
**| might decide to make on nsIMdbEnv instance for every nsIMdbPort opened, so that
**| error information is segregated by database instance.  Callers create
**| instances of nsIMdbEnv by calling the MakeEnv() method in nsIMdbFactory. 
**|
**|| tracing: an environment might support some kind of tracing, and this
**| boolean attribute permits such activity to be enabled or disabled.
**|
**|| errors: when a call to the abstract db interface returns, a caller might
**| check the number of outstanding errors to see whether the operation did
**| actually succeed. Each nsIMdbEnv should have all its errors cleared by a
**| call to ClearErrors() before making each call to the abstract db API,
**| because outstanding errors might disable further database actions.  (This
**| is not done inside the db interface, because the db cannot in general know
**| when a call originates from inside or outside -- only the app knows this.)
**|
**|| error hook: callers can install an instance of nsIMdbErrorHook to receive
**| error notifications whenever the error count increases.  The hook can
**| be uninstalled by passing a null pointer.
**|
|*/
class nsIMdbEnv : public nsIMdbObject { // db specific context parameter
public:

// { ===== begin nsIMdbEnv methods =====

  // { ----- begin attribute methods -----
  virtual mdb_err GetErrorCount(mdb_count* outCount,
    mdb_bool* outShouldAbort) = 0;
  virtual mdb_err GetWarningCount(mdb_count* outCount,
    mdb_bool* outShouldAbort) = 0;
  
  virtual mdb_err GetEnvBeVerbose(mdb_bool* outBeVerbose) = 0;
  virtual mdb_err SetEnvBeVerbose(mdb_bool inBeVerbose) = 0;
  
  virtual mdb_err GetDoTrace(mdb_bool* outDoTrace) = 0;
  virtual mdb_err SetDoTrace(mdb_bool inDoTrace) = 0;
  
  virtual mdb_err GetAutoClear(mdb_bool* outAutoClear) = 0;
  virtual mdb_err SetAutoClear(mdb_bool inAutoClear) = 0;
  
  virtual mdb_err GetErrorHook(nsIMdbErrorHook** acqErrorHook) = 0;
  virtual mdb_err SetErrorHook(
    nsIMdbErrorHook* ioErrorHook) = 0; // becomes referenced
  
  virtual mdb_err GetHeap(nsIMdbHeap** acqHeap) = 0;
  virtual mdb_err SetHeap(
    nsIMdbHeap* ioHeap) = 0; // becomes referenced
  // } ----- end attribute methods -----
  
  virtual mdb_err ClearErrors() = 0; // clear errors beore re-entering db API
  virtual mdb_err ClearWarnings() = 0; // clear warnings
  virtual mdb_err ClearErrorsAndWarnings() = 0; // clear both errors & warnings
// } ===== end nsIMdbEnv methods =====
};

/*| nsIMdbFactory: the main entry points to the abstract db interface.  A DLL
**| that supports this mdb interface need only have a single exported method
**| that will return an instance of nsIMdbFactory, so that further methods in
**| the suite can be accessed from objects returned by nsIMdbFactory methods.
**|
**|| mdbYarn: note all nsIMdbFactory subclasses must guarantee null
**| termination of all strings written into mdbYarn instances, as long as
**| mYarn_Size and mYarn_Buf are nonzero.  Even truncated string values must
**| be null terminated.  This is more strict behavior than mdbYarn requires,
**| but it is part of the nsIMdbFactory interface.
**|
**|| envs: an environment instance is required as per-thread context for
**| most of the db method calls, so nsIMdbFactory creates such instances.
**|
**|| rows: callers must be able to create row instances that are independent
**| of storage space that is part of the db content graph.  Many interfaces
**| for data exchange have strictly copy semantics, so that a row instance
**| has no specific identity inside the db content model, and the text in
**| cells are an independenty copy of unexposed content inside the db model.
**| Callers are expected to maintain one or more row instances as a buffer
**| for staging cell content copied into or out of a table inside the db.
**| Callers are urged to use an instance of nsIMdbRow created by the nsIMdbFactory
**| code suite, because reading and writing might be much more efficient than
**| when using a hand-rolled nsIMdbRow subclass with no relation to the suite.
**|
**|| ports: a port is a readonly interface to a specific database file. Most
**| of the methods to access a db file are suitable for a readonly interface,
**| so a port is the basic minimum for accessing content.  This makes it
**| possible to read other external formats for import purposes, without
**| needing the code or competence necessary to write every such format.  So
**| we can write generic import code just once, as long as every format can
**| show a face based on nsIMdbPort. (However, same suite import can be faster.)
**| Given a file name and the first 512 bytes of a file, a factory can say if
**| a port can be opened by this factory.  Presumably an app maintains chains
**| of factories for different suites, and asks each in turn about opening a
**| a prospective file for reading (as a port) or writing (as a store).  I'm
**| not ready to tackle issues of format fidelity and factory chain ordering.
**|
**|| stores: a store is a mutable interface to a specific database file, and
**| includes the port interface plus any methods particular to writing, which
**| are few in number.  Presumably the set of files that can be opened as
**| stores is a subset of the set of files that can be opened as ports.  A
**| new store can be created with CreateNewFileStore() by supplying a new
**| file name which does not yet exist (callers are always responsible for
**| destroying any existing files before calling this method). 
|*/
class nsIMdbFactory : public nsIMdbObject { // suite entry points
public:

// { ===== begin nsIMdbFactory methods =====

  // { ----- begin file methods -----
  virtual mdb_err OpenOldFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
    const char* inFilePath,
    mdb_bool inFrozen, nsIMdbFile** acqFile) = 0;
  // Choose some subclass of nsIMdbFile to instantiate, in order to read
  // (and write if not frozen) the file known by inFilePath.  The file
  // returned should be open and ready for use, and presumably positioned
  // at the first byte position of the file.  The exact manner in which
  // files must be opened is considered a subclass specific detail, and
  // other portions or Mork source code don't want to know how it's done.

  virtual mdb_err CreateNewFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
    const char* inFilePath,
    nsIMdbFile** acqFile) = 0;
  // Choose some subclass of nsIMdbFile to instantiate, in order to read
  // (and write if not frozen) the file known by inFilePath.  The file
  // returned should be created and ready for use, and presumably positioned
  // at the first byte position of the file.  The exact manner in which
  // files must be opened is considered a subclass specific detail, and
  // other portions or Mork source code don't want to know how it's done.
  // } ----- end file methods -----

  // { ----- begin env methods -----
  virtual mdb_err MakeEnv(nsIMdbHeap* ioHeap, nsIMdbEnv** acqEnv) = 0; // acquire new env
  // ioHeap can be nil, causing a MakeHeap() style heap instance to be used
  // } ----- end env methods -----

  // { ----- begin heap methods -----
  virtual mdb_err MakeHeap(nsIMdbEnv* ev, nsIMdbHeap** acqHeap) = 0; // acquire new heap
  // } ----- end heap methods -----

  // { ----- begin compare methods -----
  virtual mdb_err MakeCompare(nsIMdbEnv* ev, nsIMdbCompare** acqCompare) = 0; // ASCII
  // } ----- end compare methods -----

  // { ----- begin row methods -----
  virtual mdb_err MakeRow(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, nsIMdbRow** acqRow) = 0; // new row
  // ioHeap can be nil, causing the heap associated with ev to be used
  // } ----- end row methods -----
  
  // { ----- begin port methods -----
  virtual mdb_err CanOpenFilePort(
    nsIMdbEnv* ev, // context
    // const char* inFilePath, // the file to investigate
    // const mdbYarn* inFirst512Bytes,
    nsIMdbFile* ioFile, // db abstract file interface
    mdb_bool* outCanOpen, // whether OpenFilePort() might succeed
    mdbYarn* outFormatVersion) = 0; // informal file format description
    
  virtual mdb_err OpenFilePort(
    nsIMdbEnv* ev, // context
    nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
    // const char* inFilePath, // the file to open for readonly import
    nsIMdbFile* ioFile, // db abstract file interface
    const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental port open
  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
  // then call nsIMdbFactory::ThumbToOpenPort() to get the port instance.

  virtual mdb_err ThumbToOpenPort( // redeeming a completed thumb from OpenFilePort()
    nsIMdbEnv* ev, // context
    nsIMdbThumb* ioThumb, // thumb from OpenFilePort() with done status
    nsIMdbPort** acqPort) = 0; // acquire new port object
  // } ----- end port methods -----
  
  // { ----- begin store methods -----
  virtual mdb_err CanOpenFileStore(
    nsIMdbEnv* ev, // context
    // const char* inFilePath, // the file to investigate
    // const mdbYarn* inFirst512Bytes,
    nsIMdbFile* ioFile, // db abstract file interface
    mdb_bool* outCanOpenAsStore, // whether OpenFileStore() might succeed
    mdb_bool* outCanOpenAsPort, // whether OpenFilePort() might succeed
    mdbYarn* outFormatVersion) = 0; // informal file format description
    
  virtual mdb_err OpenFileStore( // open an existing database
    nsIMdbEnv* ev, // context
    nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
    // const char* inFilePath, // the file to open for general db usage
    nsIMdbFile* ioFile, // db abstract file interface
    const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental store open
  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
  // then call nsIMdbFactory::ThumbToOpenStore() to get the store instance.
    
  virtual mdb_err
  ThumbToOpenStore( // redeem completed thumb from OpenFileStore()
    nsIMdbEnv* ev, // context
    nsIMdbThumb* ioThumb, // thumb from OpenFileStore() with done status
    nsIMdbStore** acqStore) = 0; // acquire new db store object
  
  virtual mdb_err CreateNewFileStore( // create a new db with minimal content
    nsIMdbEnv* ev, // context
    nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
    // const char* inFilePath, // name of file which should not yet exist
    nsIMdbFile* ioFile, // db abstract file interface
    const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
    nsIMdbStore** acqStore) = 0; // acquire new db store object
  // } ----- end store methods -----

// } ===== end nsIMdbFactory methods =====
};

extern "C" nsIMdbFactory* MakeMdbFactory(); 

/*| nsIMdbFile: abstract file interface resembling the original morkFile
**| abstract interface (which was in turn modeled on the file interface
**| from public domain IronDoc).  The design of this file interface is
**| complicated by the fact that some DB's will not find this interface
**| adequate for all runtime requirements (even though this file API is
**| enough to implement text-based DB's like Mork).  For this reason,
**| more methods have been added to let a DB library force the file to
**| become closed so the DB can reopen the file in some other manner.
**| Folks are encouraged to suggest ways to tune this interface to suit
**| DB's that cannot manage to pull their maneuvers even given this API.
**|
**|| Tell: get the current i/o position in file
**|
**|| Seek: change the current i/o position in file
**|
**|| Eof: return file's total length in bytes
**|
**|| Read: input inSize bytes into outBuf, returning actual transfer size
**|
**|| Get: read starting at specific file offset (e.g. Seek(); Read();)
**|
**|| Write: output inSize bytes from inBuf, returning actual transfer size
**|
**|| Put: write starting at specific file offset (e.g. Seek(); Write();)
**|
**|| Flush: if written bytes are buffered, push them to final destination
**|
**|| Path: get file path in some string representation.  This is intended
**| either to support the display of file name in a user presentation, or
**| to support the closing and reopening of the file when the DB needs more
**| exotic file access than is presented by the nsIMdbFile interface.
**|
**|| Steal: tell this file to close any associated i/o stream in the file
**| system, because the file ioThief intends to reopen the file in order
**| to provide the MDB implementation with more exotic file access than is
**| offered by the nsIMdbFile alone.  Presumably the thief knows enough
**| from Path() in order to know which file to reopen.  If Steal() is
**| successful, this file should probably delegate all future calls to
**| the nsIMdbFile interface down to the thief files, so that even after
**| the file has been stolen, it can still be read, written, or forcibly
**| closed (by a call to CloseMdbObject()).
**|
**|| Thief: acquire and return thief passed to an earlier call to Steal().
|*/
class nsIMdbFile : public nsIMdbObject { // minimal file interface
public:

// { ===== begin nsIMdbFile methods =====

  // { ----- begin pos methods -----
  virtual mdb_err Tell(nsIMdbEnv* ev, mdb_pos* outPos) = 0;
  virtual mdb_err Seek(nsIMdbEnv* ev, mdb_pos inPos) = 0;
  virtual mdb_err Eof(nsIMdbEnv* ev, mdb_pos* outPos) = 0;
  // } ----- end pos methods -----

  // { ----- begin read methods -----
  virtual mdb_err Read(nsIMdbEnv* ev, void* outBuf, mdb_size inSize,
    mdb_size* outActualSize) = 0;
  virtual mdb_err Get(nsIMdbEnv* ev, void* outBuf, mdb_size inSize,
    mdb_pos inPos, mdb_size* outActualSize) = 0;
  // } ----- end read methods -----
    
  // { ----- begin write methods -----
  virtual mdb_err  Write(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize,
    mdb_size* outActualSize) = 0;
  virtual mdb_err  Put(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize,
    mdb_pos inPos, mdb_size* outActualSize) = 0;
  virtual mdb_err  Flush(nsIMdbEnv* ev) = 0;
  // } ----- end attribute methods -----
    
  // { ----- begin path methods -----
  virtual mdb_err  Path(nsIMdbEnv* ev, mdbYarn* outFilePath) = 0;
  // } ----- end path methods -----
    
  // { ----- begin replacement methods -----
  virtual mdb_err  Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) = 0;
  virtual mdb_err  Thief(nsIMdbEnv* ev, nsIMdbFile** acqThief) = 0;
  // } ----- end replacement methods -----

  // { ----- begin versioning methods -----
  virtual mdb_err BecomeTrunk(nsIMdbEnv* ev) = 0;
  // If this file is a file version branch created by calling AcquireBud(),
  // BecomeTrunk() causes this file's content to replace the original
  // file's content, typically by assuming the original file's identity.
  // This default implementation of BecomeTrunk() does nothing, and this
  // is appropriate behavior for files which are not branches, and is
  // also the right behavior for files returned from AcquireBud() which are
  // in fact the original file that has been truncated down to zero length.

  virtual mdb_err AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
    nsIMdbFile** acqBud) = 0; // acquired file for new version of content
  // AcquireBud() starts a new "branch" version of the file, empty of content,
  // so that a new version of the file can be written.  This new file
  // can later be told to BecomeTrunk() the original file, so the branch
  // created by budding the file will replace the original file.  Some
  // file subclasses might initially take the unsafe but expedient
  // approach of simply truncating this file down to zero length, and
  // then returning the same morkFile pointer as this, with an extra
  // reference count increment.  Note that the caller of AcquireBud() is
  // expected to eventually call CutStrongRef() on the returned file
  // in order to release the strong reference.  High quality versions
  // of morkFile subclasses will create entirely new files which later
  // are renamed to become the old file, so that better transactional
  // behavior is exhibited by the file, so crashes protect old files.
  // Note that AcquireBud() is an illegal operation on readonly files.
  // } ----- end versioning methods -----

// } ===== end nsIMdbFile methods =====
};

/*| nsIMdbPort: a readonly interface to a specific database file. The mutable
**| nsIMdbStore interface is a subclass that includes writing behavior, but
**| most of the needed db methods appear in the readonly nsIMdbPort interface.
**|
**|| mdbYarn: note all nsIMdbPort and nsIMdbStore subclasses must guarantee null
**| termination of all strings written into mdbYarn instances, as long as
**| mYarn_Size and mYarn_Buf are nonzero.  Even truncated string values must
**| be null terminated.  This is more strict behavior than mdbYarn requires,
**| but it is part of the nsIMdbPort and nsIMdbStore interface.
**|
**|| attributes: methods are provided to distinguish a readonly port from a
**| mutable store, and whether a mutable store actually has any dirty content.
**|
**|| filepath: the file path used to open the port from the nsIMdbFactory can be
**| queried and discovered by GetPortFilePath(), which includes format info.
**|
**|| export: a port can write itself in other formats, with perhaps a typical
**| emphasis on text interchange formats used by other systems.  A port can be
**| queried to determine its preferred export interchange format, and a port
**| can be queried to see whether a specific export format is supported.  And
**| actually exporting a port requires a new destination file name and format.
**|
**|| tokens: a port supports queries about atomized strings to map tokens to
**| strings or strings to token integers.  (All atomized strings must be in
**| US-ASCII iso-8859-1 Latin1 charset encoding.)  When a port is actually a
**| mutable store and a string has not yet been atomized, then StringToToken()
**| will actually do so and modify the store.  The QueryToken() method will not
**| atomize a string if it has not already been atomized yet, even in stores.
**|
**|| tables: other than string tokens, all port content is presented through
**| tables, which are ordered collections of rows.  Tables are identified by
**| row scope and table kind, which might or might not be unique in a port,
**| depending on app convention.  When tables are effectively unique, then
**| queries for specific scope and kind pairs will find those tables.  To see
**| all tables that match specific row scope and table kind patterns, even in
**| the presence of duplicates, every port supports a GetPortTableCursor()
**| method that returns an iterator over all matching tables.  Table kind is
**| considered scoped inside row scope, so passing a zero for table kind will
**| find all table kinds for some nonzero row scope.  Passing a zero for row
**| scope will iterate over all tables in the port, in some undefined order.
**| (A new table can be added to a port using nsIMdbStore::NewTable(), even when
**| the requested scope and kind combination is already used by other tables.)
**|
**|| memory: callers can request that a database use less memory footprint in
**| several flavors, from an inconsequential idle flavor to a rather drastic
**| panic flavor. Callers might perform an idle purge very frequently if desired
**| with very little cost, since only normally scheduled memory management will
**| be conducted, such as freeing resources for objects scheduled to be dropped.
**| Callers should perform session memory purges infrequently because they might
**| involve costly scanning of data structures to removed cached content, and
**| session purges are recommended only when a caller experiences memory crunch.
**| Callers should only rarely perform a panic purge, in response to dire memory
**| straits, since this is likely to make db operations much more expensive
**| than they would be otherwise.  A panic purge asks a database to free as much
**| memory as possible while staying effective and operational, because a caller
**| thinks application failure might otherwise occur.  (Apps might better close
**| an open db, so panic purges only make sense when a db is urgently needed.)
|*/
class nsIMdbPort : public nsIMdbObject {
public:

// { ===== begin nsIMdbPort methods =====

  // { ----- begin attribute methods -----
  virtual mdb_err GetIsPortReadonly(nsIMdbEnv* ev, mdb_bool* outBool) = 0;
  virtual mdb_err GetIsStore(nsIMdbEnv* ev, mdb_bool* outBool) = 0;
  virtual mdb_err GetIsStoreAndDirty(nsIMdbEnv* ev, mdb_bool* outBool) = 0;

  virtual mdb_err GetUsagePolicy(nsIMdbEnv* ev, 
    mdbUsagePolicy* ioUsagePolicy) = 0;

  virtual mdb_err SetUsagePolicy(nsIMdbEnv* ev, 
    const mdbUsagePolicy* inUsagePolicy) = 0;
  // } ----- end attribute methods -----

  // { ----- begin memory policy methods -----  
  virtual mdb_err IdleMemoryPurge( // do memory management already scheduled
    nsIMdbEnv* ev, // context
    mdb_size* outEstimatedBytesFreed) = 0; // approximate bytes actually freed

  virtual mdb_err SessionMemoryPurge( // request specific footprint decrease
    nsIMdbEnv* ev, // context
    mdb_size inDesiredBytesFreed, // approximate number of bytes wanted
    mdb_size* outEstimatedBytesFreed) = 0; // approximate bytes actually freed

  virtual mdb_err PanicMemoryPurge( // desperately free all possible memory
    nsIMdbEnv* ev, // context
    mdb_size* outEstimatedBytesFreed) = 0; // approximate bytes actually freed
  // } ----- end memory policy methods -----

  // { ----- begin filepath methods -----
  virtual mdb_err GetPortFilePath(
    nsIMdbEnv* ev, // context
    mdbYarn* outFilePath, // name of file holding port content
    mdbYarn* outFormatVersion) = 0; // file format description
    
  virtual mdb_err GetPortFile(
    nsIMdbEnv* ev, // context
    nsIMdbFile** acqFile) = 0; // acquire file used by port or store
  // } ----- end filepath methods -----

  // { ----- begin export methods -----
  virtual mdb_err BestExportFormat( // determine preferred export format
    nsIMdbEnv* ev, // context
    mdbYarn* outFormatVersion) = 0; // file format description

  // some tentative suggested import/export formats
  // "ns:msg:db:port:format:ldif:ns4.0:passthrough" // necessary
  // "ns:msg:db:port:format:ldif:ns4.5:utf8"        // necessary
  // "ns:msg:db:port:format:ldif:ns4.5:tabbed"
  // "ns:msg:db:port:format:ldif:ns4.5:binary"      // necessary
  // "ns:msg:db:port:format:html:ns3.0:addressbook" // necessary
  // "ns:msg:db:port:format:html:display:verbose"
  // "ns:msg:db:port:format:html:display:concise"
  // "ns:msg:db:port:format:mork:zany:verbose"      // necessary
  // "ns:msg:db:port:format:mork:zany:atomized"     // necessary
  // "ns:msg:db:port:format:rdf:xml"
  // "ns:msg:db:port:format:xml:mork"
  // "ns:msg:db:port:format:xml:display:verbose"
  // "ns:msg:db:port:format:xml:display:concise"
  // "ns:msg:db:port:format:xml:print:verbose"      // recommended
  // "ns:msg:db:port:format:xml:print:concise"

  virtual mdb_err
  CanExportToFormat( // can export content in given specific format?
    nsIMdbEnv* ev, // context
    const char* inFormatVersion, // file format description
    mdb_bool* outCanExport) = 0; // whether ExportSource() might succeed

  virtual mdb_err ExportToFormat( // export content in given specific format
    nsIMdbEnv* ev, // context
    // const char* inFilePath, // the file to receive exported content
    nsIMdbFile* ioFile, // destination abstract file interface
    const char* inFormatVersion, // file format description
    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental export
  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
  // then the export will be finished.

  // } ----- end export methods -----

  // { ----- begin token methods -----
  virtual mdb_err TokenToString( // return a string name for an integer token
    nsIMdbEnv* ev, // context
    mdb_token inToken, // token for inTokenName inside this port
    mdbYarn* outTokenName) = 0; // the type of table to access
  
  virtual mdb_err StringToToken( // return an integer token for scope name
    nsIMdbEnv* ev, // context
    const char* inTokenName, // Latin1 string to tokenize if possible
    mdb_token* outToken) = 0; // token for inTokenName inside this port
    
  // String token zero is never used and never supported. If the port
  // is a mutable store, then StringToToken() to create a new
  // association of inTokenName with a new integer token if possible.
  // But a readonly port will return zero for an unknown scope name.

  virtual mdb_err QueryToken( // like StringToToken(), but without adding
    nsIMdbEnv* ev, // context
    const char* inTokenName, // Latin1 string to tokenize if possible
    mdb_token* outToken) = 0; // token for inTokenName inside this port
  
  // QueryToken() will return a string token if one already exists,
  // but unlike StringToToken(), will not assign a new token if not
  // already in use.

  // } ----- end token methods -----

  // { ----- begin row methods -----  
  virtual mdb_err HasRow( // contains a row with the specified oid?
    nsIMdbEnv* ev, // context
    const mdbOid* inOid,  // hypothetical row oid
    mdb_bool* outHasRow) = 0; // whether GetRow() might succeed

  virtual mdb_err GetRowRefCount( // get number of tables that contain a row 
    nsIMdbEnv* ev, // context
    const mdbOid* inOid,  // hypothetical row oid
    mdb_count* outRefCount) = 0; // number of tables containing inRowKey 
    
  virtual mdb_err GetRow( // access one row with specific oid
    nsIMdbEnv* ev, // context
    const mdbOid* inOid,  // hypothetical row oid
    nsIMdbRow** acqRow) = 0; // acquire specific row (or null)
    
  // virtual mdb_err
  // GetPortRowCursor( // get cursor for all rows in specific scope
  //   nsIMdbEnv* ev, // context
  //   mdb_scope inRowScope, // row scope for row ids
  //   nsIMdbPortRowCursor** acqCursor) = 0; // all such rows in the port

  virtual mdb_err FindRow(nsIMdbEnv* ev, // search for row with matching cell
    mdb_scope inRowScope,   // row scope for row ids
    mdb_column inColumn,   // the column to search (and maintain an index)
    const mdbYarn* inTargetCellValue, // cell value for which to search
    mdbOid* outRowOid, // out row oid on match (or {0,-1} for no match)
    nsIMdbRow** acqRow) = 0; // acquire matching row (or nil for no match)
  // FindRow() searches for one row that has a cell in column inColumn with
  // a contained value with the same form (i.e. charset) and is byte-wise
  // identical to the blob described by yarn inTargetCellValue.  Both content
  // and form of the yarn must be an exact match to find a matching row.
  //
  // (In other words, both a yarn's blob bytes and form are significant.  The
  // form is not expected to vary in columns used for identity anyway.  This
  // is intended to make the cost of FindRow() cheaper for MDB implementors,
  // since any cell value atomization performed internally must necessarily
  // make yarn form significant in order to avoid data loss in atomization.)
  //
  // FindRow() can lazily create an index on attribute inColumn for all rows
  // with that attribute in row space scope inRowScope, so that subsequent
  // calls to FindRow() will perform faster.  Such an index might or might
  // not be persistent (but this seems desirable if it is cheap to do so).
  // Note that lazy index creation in readonly DBs is not very feasible.
  //
  // This FindRow() interface assumes that attribute inColumn is effectively
  // an alternative means of unique identification for a row in a rowspace,
  // so correct behavior is only guaranteed when no duplicates for this col
  // appear in the given set of rows.  (If more than one row has the same cell
  // value in this column, no more than one will be found; and cutting one of
  // two duplicate rows can cause the index to assume no other such row lives
  // in the row space, so future calls return nil for negative search results
  // even though some duplicate row might still live within the rowspace.)
  //
  // In other words, the FindRow() implementation is allowed to assume simple
  // hash tables mapping unqiue column keys to associated row values will be
  // sufficient, where any duplication is not recorded because only one copy
  // of a given key need be remembered.  Implementors are not required to sort
  // all rows by the specified column.
  // } ----- end row methods -----

  // { ----- begin table methods -----  
  virtual mdb_err HasTable( // supports a table with the specified oid?
    nsIMdbEnv* ev, // context
    const mdbOid* inOid,  // hypothetical table oid
    mdb_bool* outHasTable) = 0; // whether GetTable() might succeed
    
  virtual mdb_err GetTable( // access one table with specific oid
    nsIMdbEnv* ev, // context
    const mdbOid* inOid,  // hypothetical table oid
    nsIMdbTable** acqTable) = 0; // acquire specific table (or null)
  
  virtual mdb_err HasTableKind( // supports a table of the specified type?
    nsIMdbEnv* ev, // context
    mdb_scope inRowScope, // rid scope for row ids
    mdb_kind inTableKind, // the type of table to access
    mdb_count* outTableCount, // current number of such tables
    mdb_bool* outSupportsTable) = 0; // whether GetTableKind() might succeed
    
  // row scopes to be supported include the following suggestions:
  // "ns:msg:db:row:scope:address:cards:all"
  // "ns:msg:db:row:scope:mail:messages:all"
  // "ns:msg:db:row:scope:news:articles:all"
 
  // table kinds to be supported include the following suggestions:
  // "ns:msg:db:table:kind:address:cards:main"
  // "ns:msg:db:table:kind:address:lists:all" 
  // "ns:msg:db:table:kind:address:list" 
  // "ns:msg:db:table:kind:news:threads:all" 
  // "ns:msg:db:table:kind:news:thread" 
  // "ns:msg:db:table:kind:mail:threads:all"
  // "ns:msg:db:table:kind:mail:thread"
    
  virtual mdb_err GetTableKind( // access one (random) table of specific type
    nsIMdbEnv* ev, // context
    mdb_scope inRowScope,      // row scope for row ids
    mdb_kind inTableKind,      // the type of table to access
    mdb_count* outTableCount, // current number of such tables
    mdb_bool* outMustBeUnique, // whether port can hold only one of these
    nsIMdbTable** acqTable) = 0;       // acquire scoped collection of rows
    
  virtual mdb_err
  GetPortTableCursor( // get cursor for all tables of specific type
    nsIMdbEnv* ev, // context
    mdb_scope inRowScope, // row scope for row ids
    mdb_kind inTableKind, // the type of table to access
    nsIMdbPortTableCursor** acqCursor) = 0; // all such tables in the port
  // } ----- end table methods -----


  // { ----- begin commit methods -----

  virtual mdb_err ShouldCompress( // store wastes at least inPercentWaste?
    nsIMdbEnv* ev, // context
    mdb_percent inPercentWaste, // 0..100 percent file size waste threshold
    mdb_percent* outActualWaste, // 0..100 percent of file actually wasted
    mdb_bool* outShould) = 0; // true when about inPercentWaste% is wasted
  // ShouldCompress() returns true if the store can determine that the file
  // will shrink by an estimated percentage of inPercentWaste% (or more) if
  // CompressCommit() is called, because that percentage of the file seems
  // to be recoverable free space.  The granularity is only in terms of 
  // percentage points, and any value over 100 is considered equal to 100.
  //
  // If a store only has an approximate idea how much space might be saved
  // during a compress, then a best guess should be made.  For example, the
  // Mork implementation might keep track of how much file space began with
  // text content before the first updating transaction, and then consider
  // all content following the start of the first transaction as potentially
  // wasted space if it is all updates and not just new content.  (This is
  // a safe assumption in the sense that behavior will stabilize on a low
  // estimate of wastage after a commit removes all transaction updates.)
  //
  // Some db formats might attempt to keep a very accurate reckoning of free
  // space size, so a very accurate determination can be made.  But other db
  // formats might have difficulty determining size of free space, and might
  // require some lengthy calculation to answer.  This is the reason for
  // passing in the percentage threshold of interest, so that such lengthy
  // computations can terminate early as soon as at least inPercentWaste is
  // found, so that the entire file need not be groveled when unnecessary.
  // However, we hope implementations will always favor fast but imprecise
  // heuristic answers instead of extremely slow but very precise answers.
  //
  // If the outActualWaste parameter is non-nil, it will be used to return
  // the actual estimated space wasted as a percentage of file size.  (This
  // parameter is provided so callers need not call repeatedly with altered
  // inPercentWaste values to isolate the actual wastage figure.)  Note the
  // actual wastage figure returned can exactly equal inPercentWaste even
  // when this grossly underestimates the real figure involved, if the db
  // finds it very expensive to determine the extent of wastage after it is
  // known to at least exceed inPercentWaste.  Note we expect that whenever
  // outShould returns true, that outActualWaste returns >= inPercentWaste.
  //
  // The effect of different inPercentWaste values is not very uniform over
  // the permitted range.  For example, 50 represents 50% wastage, or a file
  // that is about double what it should be ideally.  But 99 represents 99%
  // wastage, or a file that is about ninety-nine times as big as it should
  // be ideally.  In the smaller direction, 25 represents 25% wastage, or
  // a file that is only 33% larger than it should be ideally.
  //
  // Callers can determine what policy they want to use for considering when
  // a file holds too much wasted space, and express this as a percentage
  // of total file size to pass as in the inPercentWaste parameter.  A zero
  // likely returns always trivially true, and 100 always trivially false.
  // The great majority of callers are expected to use values from 25 to 75,
  // since most plausible thresholds for compressing might fall between the
  // extremes of 133% of ideal size and 400% of ideal size.  (Presumably the
  // larger a file gets, the more important the percentage waste involved, so
  // a sliding scale for compress thresholds might use smaller numbers for
  // much bigger file sizes.)
  
  // } ----- end commit methods -----

// } ===== end nsIMdbPort methods =====
};

/*| nsIMdbStore: a mutable interface to a specific database file.
**|
**|| tables: one can force a new table to exist in a store with NewTable()
**| and nonzero values for both row scope and table kind.  (If one wishes only
**| one table of a certain kind, then one might look for it first using the
**| GetTableKind() method).  One can pass inMustBeUnique to force future
**| users of this store to be unable to create other tables with the same pair
**| of scope and kind attributes.  When inMustBeUnique is true, and the table
**| with the given scope and kind pair already exists, then the existing one
**| is returned instead of making a new table.  Similarly, if one passes false
**| for inMustBeUnique, but the table kind has already been marked unique by a
**| previous user of the store, then the existing unique table is returned.
**|
**|| import: all or some of another port's content can be imported by calling
**| AddPortContent() with a row scope identifying the extent of content to
**| be imported.  A zero row scope will import everything.  A nonzero row
**| scope will only import tables with a matching row scope.  Note that one
**| must somehow find a way to negotiate possible conflicts between existing
**| row content and imported row content, and this involves a specific kind of
**| definition for row identity involving either row IDs or unique attributes,
**| or some combination of these two.  At the moment I am just going to wave
**| my hands, and say the default behavior is to assign all new row identities
**| to all imported content, which will result in no merging of content; this
**| must change later because it is unacceptable in some contexts.
**|
**|| commits: to manage modifications in a mutable store, very few methods are
**| really needed to indicate global policy choices that are independent of 
**| the actual modifications that happen in objects at the level of tables,
**| rows, and cells, etc.  The most important policy to specify is which sets
**| of changes are considered associated in a manner such that they should be
**| applied together atomically to a given store.  We call each such group of
**| changes a transaction.  We handle three different grades of transaction,
**| but they differ only in semantic significance to the application, and are
**| not intended to nest.  (If small transactions were nested inside large
**| transactions, that would imply that a single large transaction must be
**| atomic over all the contained small transactions; but actually we intend
**| smalls transaction never be undone once commited due to, say, aborting a
**| transaction of greater significance.)  The small, large, and session level
**| commits have equal granularity, and differ only in risk of loss from the
**| perspective of an application.  Small commits characterize changes that
**| can be lost with relatively small risk, so small transactions can delay
**| until later if they are expensive or impractical to commit.  Large commits
**| involve changes that would probably inconvenience users if lost, so the
**| need to pay costs of writing is rather greater than with small commits.
**| Session commits are last ditch attempts to save outstanding changes before
**| stopping the use of a particular database, so there will be no later point
**| in time to save changes that have been delayed due to possible high cost.
**| If large commits are never delayed, then a session commit has about the
**| same performance effect as another large commit; but if small and large
**| commits are always delayed, then a session commit is likely to be rather
**| expensive as a runtime cost compared to any earlier database usage.
**|
**|| aborts: the only way to abort changes to a store is by closing the store.
**| So there is no specific method for causing any abort.  Stores must discard
**| all changes made that are uncommited when a store is closed.  This design
**| choice makes the implementations of tables, rows, and cells much less
**| complex because they need not maintain a record of undobable changes.  When
**| a store is closed, presumably this precipitates the closure of all tables,
**| rows, and cells in the store as well.   So an application can revert the
**| state of a store in the user interface by quietly closing and reopening a
**| store, because this will discard uncommited changes and show old content.
**| This implies an app that closes a store will need to send a "scramble"
**| event notification to any views that depend on old discarded content.
|*/
class nsIMdbStore : public nsIMdbPort {
public:

// { ===== begin nsIMdbStore methods =====

  // { ----- begin table methods -----
  virtual mdb_err NewTable( // make one new table of specific type
    nsIMdbEnv* ev, // context
    mdb_scope inRowScope,    // row scope for row ids
    mdb_kind inTableKind,    // the type of table to access
    mdb_bool inMustBeUnique, // whether store can hold only one of these
    const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
    nsIMdbTable** acqTable) = 0;     // acquire scoped collection of rows
    
  virtual mdb_err NewTableWithOid( // make one new table of specific type
    nsIMdbEnv* ev, // context
    const mdbOid* inOid,   // caller assigned oid
    mdb_kind inTableKind,    // the type of table to access
    mdb_bool inMustBeUnique, // whether store can hold only one of these
    const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying 
    nsIMdbTable** acqTable) = 0;     // acquire scoped collection of rows
  // } ----- end table methods -----

  // { ----- begin row scope methods -----
  virtual mdb_err RowScopeHasAssignedIds(nsIMdbEnv* ev,
    mdb_scope inRowScope,   // row scope for row ids
    mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
    mdb_bool* outStoreAssigned) = 0; // nonzero if store db assigned specified

  virtual mdb_err SetCallerAssignedIds(nsIMdbEnv* ev,
    mdb_scope inRowScope,   // row scope for row ids
    mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
    mdb_bool* outStoreAssigned) = 0; // nonzero if store db assigned specified

  virtual mdb_err SetStoreAssignedIds(nsIMdbEnv* ev,
    mdb_scope inRowScope,   // row scope for row ids
    mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
    mdb_bool* outStoreAssigned) = 0; // nonzero if store db assigned specified
  // } ----- end row scope methods -----

  // { ----- begin row methods -----
  virtual mdb_err NewRowWithOid(nsIMdbEnv* ev, // new row w/ caller assigned oid
    const mdbOid* inOid,   // caller assigned oid
    nsIMdbRow** acqRow) = 0; // create new row

  virtual mdb_err NewRow(nsIMdbEnv* ev, // new row with db assigned oid
    mdb_scope inRowScope,   // row scope for row ids
    nsIMdbRow** acqRow) = 0; // create new row
  // Note this row must be added to some table or cell child before the
  // store is closed in order to make this row persist across sesssions.

  // } ----- end row methods -----

  // { ----- begin inport/export methods -----
  virtual mdb_err ImportContent( // import content from port
    nsIMdbEnv* ev, // context
    mdb_scope inRowScope, // scope for rows (or zero for all?)
    nsIMdbPort* ioPort, // the port with content to add to store
    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental import
  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
  // then the import will be finished.

  virtual mdb_err ImportFile( // import content from port
    nsIMdbEnv* ev, // context
    nsIMdbFile* ioFile, // the file with content to add to store
    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental import
  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
  // then the import will be finished.
  // } ----- end inport/export methods -----

  // { ----- begin hinting methods -----
  virtual mdb_err
  ShareAtomColumnsHint( // advise re shared column content atomizing
    nsIMdbEnv* ev, // context
    mdb_scope inScopeHint, // zero, or suggested shared namespace
    const mdbColumnSet* inColumnSet) = 0; // cols desired tokenized together

  virtual mdb_err
  AvoidAtomColumnsHint( // advise column with poor atomizing prospects
    nsIMdbEnv* ev, // context
    const mdbColumnSet* inColumnSet) = 0; // cols with poor atomizing prospects
  // } ----- end hinting methods -----

  // { ----- begin commit methods -----
  virtual mdb_err SmallCommit( // save minor changes if convenient and uncostly
    nsIMdbEnv* ev) = 0; // context
  
  virtual mdb_err LargeCommit( // save important changes if at all possible
    nsIMdbEnv* ev, // context
    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental commit
  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
  // then the commit will be finished.  Note the store is effectively write
  // locked until commit is finished or canceled through the thumb instance.
  // Until the commit is done, the store will report it has readonly status.

  virtual mdb_err SessionCommit( // save all changes if large commits delayed
    nsIMdbEnv* ev, // context
    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental commit
  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
  // then the commit will be finished.  Note the store is effectively write
  // locked until commit is finished or canceled through the thumb instance.
  // Until the commit is done, the store will report it has readonly status.

  virtual mdb_err
  CompressCommit( // commit and make db physically smaller if possible
    nsIMdbEnv* ev, // context
    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental commit
  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
  // then the commit will be finished.  Note the store is effectively write
  // locked until commit is finished or canceled through the thumb instance.
  // Until the commit is done, the store will report it has readonly status.
  
  // } ----- end commit methods -----

// } ===== end nsIMdbStore methods =====
};

/*| nsIMdbCursor: base cursor class for iterating row cells and table rows
**|
**|| count: the number of elements in the collection (table or row)
**|
**|| seed: the change count in the underlying collection, which is synced
**| with the collection when the iteration position is set, and henceforth
**| acts to show whether the iter has lost collection synchronization, in
**| case it matters to clients whether any change happens during iteration.
**|
**|| pos: the position of the current element in the collection.  Negative
**| means a position logically before the first element.  A positive value
**| equal to count (or larger) implies a position after the last element.
**| To iterate over all elements, set the position to negative, so subsequent
**| calls to any 'next' method will access the first collection element.
**|
**|| doFailOnSeedOutOfSync: whether a cursor should return an error if the
**| cursor's snapshot of a table's seed becomes stale with respect the table's
**| current seed value (which implies the iteration is less than total) in
**| between to cursor calls that actually access collection content.  By
**| default, a cursor should assume this attribute is false until specified,
**| so that iterations quietly try to re-sync when they loose coherence.
|*/
class nsIMdbCursor : public nsIMdbObject { // collection iterator
public:

// { ===== begin nsIMdbCursor methods =====

  // { ----- begin attribute methods -----
  virtual mdb_err GetCount(nsIMdbEnv* ev, mdb_count* outCount) = 0; // readonly
  virtual mdb_err GetSeed(nsIMdbEnv* ev, mdb_seed* outSeed) = 0;    // readonly
  
  virtual mdb_err SetPos(nsIMdbEnv* ev, mdb_pos inPos) = 0;   // mutable
  virtual mdb_err GetPos(nsIMdbEnv* ev, mdb_pos* outPos) = 0;
  
  virtual mdb_err SetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool inFail) = 0;
  virtual mdb_err GetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool* outFail) = 0;
  // } ----- end attribute methods -----

// } ===== end nsIMdbCursor methods =====
};

/*| nsIMdbPortTableCursor: cursor class for iterating port tables
**|
**|| port: the cursor is associated with a specific port, which can be
**| set to a different port (which resets the position to -1 so the
**| next table acquired is the first in the port.
**|
|*/
class nsIMdbPortTableCursor : public nsIMdbCursor { // table collection iterator
public:

// { ===== begin nsIMdbPortTableCursor methods =====

  // { ----- begin attribute methods -----
  virtual mdb_err SetPort(nsIMdbEnv* ev, nsIMdbPort* ioPort) = 0; // sets pos to -1
  virtual mdb_err GetPort(nsIMdbEnv* ev, nsIMdbPort** acqPort) = 0;
  
  virtual mdb_err SetRowScope(nsIMdbEnv* ev, // sets pos to -1
    mdb_scope inRowScope) = 0;
  virtual mdb_err GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) = 0; 
  // setting row scope to zero iterates over all row scopes in port
    
  virtual mdb_err SetTableKind(nsIMdbEnv* ev, // sets pos to -1
    mdb_kind inTableKind) = 0;
  virtual mdb_err GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) = 0;
  // setting table kind to zero iterates over all table kinds in row scope
  // } ----- end attribute methods -----

  // { ----- begin table iteration methods -----
  virtual mdb_err NextTable( // get table at next position in the db
    nsIMdbEnv* ev, // context
    nsIMdbTable** acqTable) = 0; // the next table in the iteration
  // } ----- end table iteration methods -----

// } ===== end nsIMdbPortTableCursor methods =====
};

/*| nsIMdbCollection: an object that collects a set of other objects as members.
**| The main purpose of this base class is to unify the perceived semantics
**| of tables and rows where their collection behavior is similar.  This helps
**| isolate the mechanics of collection behavior from the other semantics that
**| are more characteristic of rows and tables.
**|
**|| count: the number of objects in a collection is the member count. (Some
**| collection interfaces call this attribute the 'size', but that can be a
**| little ambiguous, and counting actual members is harder to confuse.)
**|
**|| seed: the seed of a collection is a counter for changes in membership in
**| a specific collection.  This seed should change when members are added to
**| or removed from a collection, but not when a member changes internal state.
**| The seed should also change whenever the internal collection of members has
**| a complex state change that reorders member positions (say by sorting) that
**| would affect the nature of an iteration over that collection of members.
**| The purpose of a seed is to inform any outstanding collection cursors that
**| they might be stale, without incurring the cost of broadcasting an event
**| notification to such cursors, which would need more data structure support.
**| Presumably a cursor in a particular mdb code suite has much more direct
**| access to a collection seed member slot that this abstract COM interface,
**| so this information is intended more for clients outside mdb that want to
**| make inferences similar to those made by the collection cursors.  The seed
**| value as an integer magnitude is not very important, and callers should not
**| assume meaningful information can be derived from an integer value beyond
**| whether it is equal or different from a previous inspection.  A seed uses
**| integers of many bits in order to make the odds of wrapping and becoming
**| equal to an earlier seed value have probability that is vanishingly small.
**|
**|| port: every collection is associated with a specific database instance.
**|
**|| cursor: a subclass of nsIMdbCursor suitable for this specific collection
**| subclass.  The ability to GetCursor() from the base nsIMdbCollection class
**| is not really as useful as getting a more specifically typed cursor more
**| directly from the base class without any casting involved.  So including
**| this method here is more for conceptual illustration.
**|
**|| oid: every collection has an identity that persists from session to
**| session. Implementations are probably able to distinguish row IDs from
**| table IDs, but we don't specify anything official in this regard.  A
**| collection has the same identity for the lifetime of the collection,
**| unless identity is swapped with another collection by means of a call to
**| BecomeContent(), which is considered a way to swap a new representation
**| for an old well-known object.  (Even so, only content appears to change,
**| while the identity seems to stay the same.)
**|
**|| become: developers can effectively cause two objects to swap identities,
**| in order to effect a complete swap between what persistent content is
**| represented by two oids.  The caller should consider this a content swap,
**| and not identity wap, because identities will seem to stay the same while
**| only content changes.  However, implementations will likely do this
**| internally by swapping identities.  Callers must swap content only
**| between objects of similar type, such as a row with another row, and a
**| table with another table, because implementations need not support
**| cross-object swapping because it might break object name spaces.
**|
**|| dropping: when a caller expects a row or table will no longer be used, the
**| caller can tell the collection to 'drop activity', which means the runtime
**| object can have it's internal representation purged to save memory or any
**| other resource that is being consumed by the collection's representation.
**| This has no effect on the collection's persistent content or semantics,
**| and is only considered a runtime effect.  After a collection drops
**| activity, the object should still be as usable as before (because it has
**| NOT been closed), but further usage can be expensive to re-instate because
**| it might involve reallocating space and/or re-reading disk space.  But
**| since this future usage is not expected, the caller does not expect to
**| pay the extra expense.  An implementation can choose to implement
**| 'dropping activity' in different ways, or even not at all if this
**| operation is not really feasible.  Callers cannot ask objects whether they
**| are 'dropped' or not, so this should be transparent. (Note that
**| implementors might fear callers do not really know whether future
**| usage will occur, and therefore might delay the act of dropping until
**| the near future, until seeing whether the object is used again
**| immediately elsewhere. Such use soon after the drop request might cause
**| the drop to be cancelled.)
|*/
class nsIMdbCollection : public nsIMdbObject { // sequence of objects
public:

// { ===== begin nsIMdbCollection methods =====

  // { ----- begin attribute methods -----
  virtual mdb_err GetSeed(nsIMdbEnv* ev,
    mdb_seed* outSeed) = 0;    // member change count
  virtual mdb_err GetCount(nsIMdbEnv* ev,
    mdb_count* outCount) = 0; // member count

  virtual mdb_err GetPort(nsIMdbEnv* ev,
    nsIMdbPort** acqPort) = 0; // collection container
  // } ----- end attribute methods -----

  // { ----- begin cursor methods -----
  virtual mdb_err GetCursor( // make a cursor starting iter at inMemberPos
    nsIMdbEnv* ev, // context
    mdb_pos inMemberPos, // zero-based ordinal pos of member in collection
    nsIMdbCursor** acqCursor) = 0; // acquire new cursor instance
  // } ----- end cursor methods -----

  // { ----- begin ID methods -----
  virtual mdb_err GetOid(nsIMdbEnv* ev,
    mdbOid* outOid) = 0; // read object identity
  virtual mdb_err BecomeContent(nsIMdbEnv* ev,
    const mdbOid* inOid) = 0; // exchange content
  // } ----- end ID methods -----

  // { ----- begin activity dropping methods -----
  virtual mdb_err DropActivity( // tell collection usage no longer expected
    nsIMdbEnv* ev) = 0;
  // } ----- end activity dropping methods -----

// } ===== end nsIMdbCollection methods =====
};

/*| nsIMdbTable: an ordered collection of rows
**|
**|| row scope: an integer token for an atomized string in this database
**| that names a space for row IDs.  This attribute of a table is intended
**| as guidance metainformation that helps with searching a database for
**| tables that operate on collections of rows of the specific type.  By
**| convention, a table with a specific row scope is expected to focus on
**| containing rows that belong to that scope, however exceptions are easily
**| allowed because all rows in a table are known by both row ID and scope.
**| (A table with zero row scope is never allowed because this would make it
**| ambiguous to use a zero row scope when iterating over tables in a port to
**| indicate that all row scopes should be seen by a cursor.)
**|
**|| table kind: an integer token for an atomized string in this database
**| that names a kind of table as a subset of the associated row scope. This
**| attribute is intended as guidance metainformation to clarify the role of
**| this table with respect to other tables in the same row scope, and this
**| also helps search for such tables in a database.  By convention, a table
**| with a specific table kind has a consistent role for containing rows with
**| respect to other collections of such rows in the same row scope.  Also by
**| convention, at least one table in a row scope has a table kind purporting
**| to contain ALL the rows that belong in that row scope, so that at least
**| one table exists that allows all rows in a scope to be interated over.
**| (A table with zero table kind is never allowed because this would make it
**| ambiguous to use a zero table kind when iterating over tables in a port to
**| indicate that all table kinds in a row scope should be seen by a cursor.)
**|
**|| port: every table is considered part of some port that contains the
**| table, so that closing the containing port will cause the table to be
**| indirectly closed as well.  We make it easy to get the containing port for
**| a table, because the port supports important semantic interfaces that will
**| affect how content in table is presented; the most important port context
**| that affects a table is specified by the set of token to string mappings
**| that affect all tokens used throughout the database, and which drive the
**| meanings of row scope, table kind, cell columns, etc.
**|
**|| cursor: a cursor that iterates over the rows in this table, where rows
**| have zero-based index positions from zero to count-1.  Making a cursor
**| with negative position will next iterate over the first row in the table.
**|
**|| position: given any position from zero to count-1, a table will return
**| the row ID and row scope for the row at that position.  (One can use the
**| GetRowAllCells() method to read that row, or else use a row cursor to both
**| get the row at some position and read its content at the same time.)  The
**| position depends on whether a table is sorted, and upon the actual sort.
**| Note that moving a row's position is only possible in unsorted tables.
**|
**|| row set: every table contains a collection of rows, where a member row is
**| referenced by the table using the row ID and row scope for the row.  No
**| single table owns a given row instance, because rows are effectively ref-
**| counted and destroyed only when the last table removes a reference to that
**| particular row.  (But a row can be emptied of all content no matter how
**| many refs exist, and this might be the next best thing to destruction.)
**| Once a row exists in a least one table (after NewRow() is called), then it
**| can be added to any other table by calling AddRow(), or removed from any
**| table by calling CutRow(), or queried as a member by calling HasRow().  A
**| row can only be added to a table once, and further additions do nothing and
**| complain not at all.  Cutting a row from a table only does something when
**| the row was actually a member, and otherwise does nothing silently.
**|
**|| row ref count: one can query the number of tables (and/or cells)
**| containing a row as a member or a child.
**|
**|| row content: one can access or modify the cell content in a table's row
**| by moving content to or from an instance of nsIMdbRow.  Note that nsIMdbRow
**| never represents the actual row inside a table, and this is the reason
**| why nsIMdbRow instances do not have row IDs or row scopes.  So an instance
**| of nsIMdbRow always and only contains a snapshot of some or all content in
**| past, present, or future persistent row inside a table.  This means that
**| reading and writing rows in tables has strictly copy semantics, and we
**| currently do not plan any exceptions for specific performance reasons.
**|
**|| sorting: note all rows are assumed sorted by row ID as a secondary
**| sort following the primary column sort, when table rows are sorted.
**|
**|| indexes:
|*/
class nsIMdbTable : public nsIMdbCollection { // a collection of rows
public:

// { ===== begin nsIMdbTable methods =====

  // { ----- begin meta attribute methods -----
  virtual mdb_err SetTablePriority(nsIMdbEnv* ev, mdb_priority inPrio) = 0;
  virtual mdb_err GetTablePriority(nsIMdbEnv* ev, mdb_priority* outPrio) = 0;
  
  virtual mdb_err GetTableBeVerbose(nsIMdbEnv* ev, mdb_bool* outBeVerbose) = 0;
  virtual mdb_err SetTableBeVerbose(nsIMdbEnv* ev, mdb_bool inBeVerbose) = 0;
  
  virtual mdb_err GetTableIsUnique(nsIMdbEnv* ev, mdb_bool* outIsUnique) = 0;
  
  virtual mdb_err GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) = 0;
  virtual mdb_err GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) = 0;
  
  virtual mdb_err GetMetaRow(
    nsIMdbEnv* ev, // context
    const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying 
    mdbOid* outOid, // output meta row oid, can be nil to suppress output
    nsIMdbRow** acqRow) = 0; // acquire table's unique singleton meta row
    // The purpose of a meta row is to support the persistent recording of
    // meta info about a table as cells put into the distinguished meta row.
    // Each table has exactly one meta row, which is not considered a member
    // of the collection of rows inside the table.  The only way to tell
    // whether a row is a meta row is by the fact that it is returned by this
    // GetMetaRow() method from some table. Otherwise nothing distinguishes
    // a meta row from any other row.  A meta row can be used anyplace that
    // any other row can be used, and can even be put into other tables (or
    // the same table) as a table member, if this is useful for some reason.
    // The first attempt to access a table's meta row using GetMetaRow() will
    // cause the meta row to be created if it did not already exist.  When the
    // meta row is created, it will have the row oid that was previously
    // requested for this table's meta row; or if no oid was ever explicitly
    // specified for this meta row, then a unique oid will be generated in
    // the row scope named "m" (so obviously MDB clients should not
    // manually allocate any row IDs from that special meta scope namespace).
    // The meta row oid can be specified either when the table is created, or
    // else the first time that GetMetaRow() is called, by passing a non-nil
    // pointer to an oid for parameter inOptionalMetaRowOid.  The meta row's
    // actual oid is returned in outOid (if this is a non-nil pointer), and
    // it will be different from inOptionalMetaRowOid when the meta row was
    // already given a different oid earlier.
  // } ----- end meta attribute methods -----


  // { ----- begin cursor methods -----
  virtual mdb_err GetTableRowCursor( // make a cursor, starting iteration at inRowPos
    nsIMdbEnv* ev, // context
    mdb_pos inRowPos, // zero-based ordinal position of row in table
    nsIMdbTableRowCursor** acqCursor) = 0; // acquire new cursor instance
  // } ----- end row position methods -----

  // { ----- begin row position methods -----
  virtual mdb_err PosToOid( // get row member for a table position
    nsIMdbEnv* ev, // context
    mdb_pos inRowPos, // zero-based ordinal position of row in table
    mdbOid* outOid) = 0; // row oid at the specified position

  virtual mdb_err OidToPos( // test for the table position of a row member
    nsIMdbEnv* ev, // context
    const mdbOid* inOid, // row to find in table
    mdb_pos* outPos) = 0; // zero-based ordinal position of row in table
    
  virtual mdb_err PosToRow( // test for the table position of a row member
    nsIMdbEnv* ev, // context
    mdb_pos inRowPos, // zero-based ordinal position of row in table
    nsIMdbRow** acqRow) = 0; // acquire row at table position inRowPos
    
  virtual mdb_err RowToPos( // test for the table position of a row member
    nsIMdbEnv* ev, // context
    nsIMdbRow* ioRow, // row to find in table
    mdb_pos* outPos) = 0; // zero-based ordinal position of row in table
  // } ----- end row position methods -----

  // { ----- begin oid set methods -----
  virtual mdb_err AddOid( // make sure the row with inOid is a table member 
    nsIMdbEnv* ev, // context
    const mdbOid* inOid) = 0; // row to ensure membership in table

  virtual mdb_err HasOid( // test for the table position of a row member
    nsIMdbEnv* ev, // context
    const mdbOid* inOid, // row to find in table
    mdb_bool* outHasOid) = 0; // whether inOid is a member row

  virtual mdb_err CutOid( // make sure the row with inOid is not a member 
    nsIMdbEnv* ev, // context
    const mdbOid* inOid) = 0; // row to remove from table
  // } ----- end oid set methods -----

  // { ----- begin row set methods -----
  virtual mdb_err NewRow( // create a new row instance in table
    nsIMdbEnv* ev, // context
    mdbOid* ioOid, // please use minus one (unbound) rowId for db-assigned IDs
    nsIMdbRow** acqRow) = 0; // create new row

  virtual mdb_err AddRow( // make sure the row with inOid is a table member 
    nsIMdbEnv* ev, // context
    nsIMdbRow* ioRow) = 0; // row to ensure membership in table

  virtual mdb_err HasRow( // test for the table position of a row member
    nsIMdbEnv* ev, // context
    nsIMdbRow* ioRow, // row to find in table
    mdb_bool* outHasRow) = 0; // whether row is a table member

  virtual mdb_err CutRow( // make sure the row with inOid is not a member 
    nsIMdbEnv* ev, // context
    nsIMdbRow* ioRow) = 0; // row to remove from table

  virtual mdb_err CutAllRows( // remove all rows from the table
    nsIMdbEnv* ev) = 0; // context
  // } ----- end row set methods -----

  // { ----- begin hinting methods -----
  virtual mdb_err SearchColumnsHint( // advise re future expected search cols  
    nsIMdbEnv* ev, // context
    const mdbColumnSet* inColumnSet) = 0; // columns likely to be searched
    
  virtual mdb_err SortColumnsHint( // advise re future expected sort columns  
    nsIMdbEnv* ev, // context
    const mdbColumnSet* inColumnSet) = 0; // columns for likely sort requests
    
  virtual mdb_err StartBatchChangeHint( // advise before many adds and cuts  
    nsIMdbEnv* ev, // context
    const void* inLabel) = 0; // intend unique address to match end call
    // If batch starts nest by virtue of nesting calls in the stack, then
    // the address of a local variable makes a good batch start label that
    // can be used at batch end time, and such addresses remain unique.
    
  virtual mdb_err EndBatchChangeHint( // advise before many adds and cuts  
    nsIMdbEnv* ev, // context
    const void* inLabel) = 0; // label matching start label
    // Suppose a table is maintaining one or many sort orders for a table,
    // so that every row added to the table must be inserted in each sort,
    // and every row cut must be removed from each sort.  If a db client
    // intends to make many such changes before needing any information
    // about the order or positions of rows inside a table, then a client
    // might tell the table to start batch changes in order to disable
    // sorting of rows for the interim.  Presumably a table will then do
    // a full sort of all rows at need when the batch changes end, or when
    // a surprise request occurs for row position during batch changes.
  // } ----- end hinting methods -----

  // { ----- begin searching methods -----
  virtual mdb_err FindRowMatches( // search variable number of sorted cols
    nsIMdbEnv* ev, // context
    const mdbYarn* inPrefix, // content to find as prefix in row's column cell
    nsIMdbTableRowCursor** acqCursor) = 0; // set of matching rows
    
  virtual mdb_err GetSearchColumns( // query columns used by FindRowMatches()
    nsIMdbEnv* ev, // context
    mdb_count* outCount, // context
    mdbColumnSet* outColSet) = 0; // caller supplied space to put columns
    // GetSearchColumns() returns the columns actually searched when the
    // FindRowMatches() method is called.  No more than mColumnSet_Count
    // slots of mColumnSet_Columns will be written, since mColumnSet_Count
    // indicates how many slots are present in the column array.  The
    // actual number of search column used by the table is returned in
    // the outCount parameter; if this number exceeds mColumnSet_Count,
    // then a caller needs a bigger array to read the entire column set.
    // The minimum of mColumnSet_Count and outCount is the number slots
    // in mColumnSet_Columns that were actually written by this method.
    //
    // Callers are expected to change this set of columns by calls to
    // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both.
  // } ----- end searching methods -----

  // { ----- begin sorting methods -----
  // sorting: note all rows are assumed sorted by row ID as a secondary
  // sort following the primary column sort, when table rows are sorted.

  virtual mdb_err
  CanSortColumn( // query which column is currently used for sorting
    nsIMdbEnv* ev, // context
    mdb_column inColumn, // column to query sorting potential
    mdb_bool* outCanSort) = 0; // whether the column can be sorted
    
  virtual mdb_err GetSorting( // view same table in particular sorting
    nsIMdbEnv* ev, // context
    mdb_column inColumn, // requested new column for sorting table
    nsIMdbSorting** acqSorting) = 0; // acquire sorting for column
    
  virtual mdb_err SetSearchSorting( // use this sorting in FindRowMatches()
    nsIMdbEnv* ev, // context
    mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn()
    nsIMdbSorting* ioSorting) = 0; // requested sorting for some column
    // SetSearchSorting() attempts to inform the table that ioSorting
    // should be used during calls to FindRowMatches() for searching
    // the column which is actually sorted by ioSorting.  This method
    // is most useful in conjunction with nsIMdbSorting::SetCompare(),
    // because otherwise a caller would not be able to override the
    // comparison ordering method used during searchs.  Note that some
    // database implementations might be unable to use an arbitrarily
    // specified sort order, either due to schema or runtime interface
    // constraints, in which case ioSorting might not actually be used.
    // Presumably ioSorting is an instance that was returned from some
    // earlier call to nsIMdbTable::GetSorting().  A caller can also
    // use nsIMdbTable::SearchColumnsHint() to specify desired change
    // in which columns are sorted and searched by FindRowMatches().
    //
    // A caller can pass a nil pointer for ioSorting to request that
    // column inColumn no longer be used at all by FindRowMatches().
    // But when ioSorting is non-nil, then inColumn should match the
    // column actually sorted by ioSorting; when these do not agree,
    // implementations are instructed to give precedence to the column
    // specified by ioSorting (so this means callers might just pass
    // zero for inColumn when ioSorting is also provided, since then
    // inColumn is both redundant and ignored).
  // } ----- end sorting methods -----

  // { ----- begin moving methods -----
  // moving a row does nothing unless a table is currently unsorted
  
  virtual mdb_err MoveOid( // change position of row in unsorted table
    nsIMdbEnv* ev, // context
    const mdbOid* inOid,  // row oid to find in table
    mdb_pos inHintFromPos, // suggested hint regarding start position
    mdb_pos inToPos,       // desired new position for row inRowId
    mdb_pos* outActualPos) = 0; // actual new position of row in table

  virtual mdb_err MoveRow( // change position of row in unsorted table
    nsIMdbEnv* ev, // context
    nsIMdbRow* ioRow,  // row oid to find in table
    mdb_pos inHintFromPos, // suggested hint regarding start position
    mdb_pos inToPos,       // desired new position for row inRowId
    mdb_pos* outActualPos) = 0; // actual new position of row in table
  // } ----- end moving methods -----
  
  // { ----- begin index methods -----
  virtual mdb_err AddIndex( // create a sorting index for column if possible
    nsIMdbEnv* ev, // context
    mdb_column inColumn, // the column to sort by index
    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental index building
  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
  // then the index addition will be finished.
  
  virtual mdb_err CutIndex( // stop supporting a specific column index
    nsIMdbEnv* ev, // context
    mdb_column inColumn, // the column with index to be removed
    nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental index destroy
  // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
  // then the index removal will be finished.
  
  virtual mdb_err HasIndex( // query for current presence of a column index
    nsIMdbEnv* ev, // context
    mdb_column inColumn, // the column to investigate
    mdb_bool* outHasIndex) = 0; // whether column has index for this column

  
  virtual mdb_err EnableIndexOnSort( // create an index for col on first sort
    nsIMdbEnv* ev, // context
    mdb_column inColumn) = 0; // the column to index if ever sorted
  
  virtual mdb_err QueryIndexOnSort( // check whether index on sort is enabled
    nsIMdbEnv* ev, // context
    mdb_column inColumn, // the column to investigate
    mdb_bool* outIndexOnSort) = 0; // whether column has index-on-sort enabled
  
  virtual mdb_err DisableIndexOnSort( // prevent future index creation on sort
    nsIMdbEnv* ev, // context
    mdb_column inColumn) = 0; // the column to index if ever sorted
  // } ----- end index methods -----

// } ===== end nsIMdbTable methods =====
};

/*| nsIMdbSorting: a view of a table in some particular sort order.  This
**| row order closely resembles a readonly array of rows with the same row
**| membership as the underlying table, but in a different order than the
**| table's explicit row order.  But the sorting's row membership changes
**| whenever the table's membership changes (without any notification, so
**| keep this in mind when modifying the table).
**|
**|| table: every sorting is associated with a particular table.  You
**| cannot change which table is used by a sorting (just ask some new
**| table for a suitable sorting instance instead).
**|
**|| compare: the ordering method used by a sorting, wrapped up in a
**| abstract plug-in interface.  When this was never installed by an
**| explicit call to SetNewCompare(), a compare object is still returned,
**| and it might match the compare instance returned by the factory method
**| nsIMdbFactory::MakeCompare(), which represents a default sort order
**| (which we fervently hope is consistently ASCII byte ordering).
**|
**|| cursor: in case callers are more comfortable with a cursor style
**| of accessing row members, each sorting will happily return a cursor
**| instance with behavior very similar to a cursor returned from a call
**| to nsIMdbTable::GetTableRowCursor(), but with different row order.
**| A cursor should show exactly the same information as the pos methods.
**|
**|| pos: the PosToOid() and PosToRow() methods are just like the table
**| methods of the same name, except they show rows in the sort order of 
**| the sorting, rather than that of the table.  These methods are like
**| readonly array position accessor's, or like a C++ operator[].
|*/
class nsIMdbSorting : public nsIMdbObject { // sorting of some table
public:
// { ===== begin nsIMdbSorting methods =====

  // { ----- begin attribute methods -----
  // sorting: note all rows are assumed sorted by row ID as a secondary
  // sort following the primary column sort, when table rows are sorted.
  
  virtual mdb_err GetTable(nsIMdbEnv* ev, nsIMdbTable** acqTable) = 0;
  virtual mdb_err GetSortColumn( // query which col is currently sorted
    nsIMdbEnv* ev, // context
    mdb_column* outColumn) = 0; // col the table uses for sorting (or zero)

  virtual mdb_err SetNewCompare(nsIMdbEnv* ev,
    nsIMdbCompare* ioNewCompare) = 0;
    // Setting the sorting's compare object will typically cause the rows
    // to be resorted, presumably in a lazy fashion when the sorting is
    // next required to be in a valid row ordering state, such as when a
    // call to PosToOid() happens.  ioNewCompare can be nil, in which case
    // implementations should revert to the default sort order, which must
    // be equivalent to whatever is used by nsIMdbFactory::MakeCompare().

  virtual mdb_err GetOldCompare(nsIMdbEnv* ev,
    nsIMdbCompare** acqOldCompare) = 0;
    // Get this sorting instance's compare object, which handles the
    // ordering of rows in the sorting, by comparing yarns from the cells
    // in the column being sorted.  Since nsIMdbCompare has no interface
    // to query the state of the compare object, it is not clear what you
    // would do with this object when returned, except maybe compare it
    // as a pointer address to some other instance, to check identities.
  
  // } ----- end attribute methods -----

  // { ----- begin cursor methods -----
  virtual mdb_err GetSortingRowCursor( // make a cursor, starting at inRowPos
    nsIMdbEnv* ev, // context
    mdb_pos inRowPos, // zero-based ordinal position of row in table
    nsIMdbTableRowCursor** acqCursor) = 0; // acquire new cursor instance
    // A cursor interface turning same info as PosToOid() or PosToRow().
  // } ----- end row position methods -----

  // { ----- begin row position methods -----
  virtual mdb_err PosToOid( // get row member for a table position
    nsIMdbEnv* ev, // context
    mdb_pos inRowPos, // zero-based ordinal position of row in table
    mdbOid* outOid) = 0; // row oid at the specified position
    
  virtual mdb_err PosToRow( // test for the table position of a row member
    nsIMdbEnv* ev, // context
    mdb_pos inRowPos, // zero-based ordinal position of row in table
    nsIMdbRow** acqRow) = 0; // acquire row at table position inRowPos
  // } ----- end row position methods -----

// } ===== end nsIMdbSorting methods =====
};

/*| nsIMdbTableRowCursor: cursor class for iterating table rows
**|
**|| table: the cursor is associated with a specific table, which can be
**| set to a different table (which resets the position to -1 so the
**| next row acquired is the first in the table.
**|
**|| NextRowId: the rows in the table can be iterated by identity alone,
**| without actually reading the cells of any row with this method.
**|
**|| NextRowCells: read the next row in the table, but only read cells
**| from the table which are already present in the row (so no new cells
**| are added to the row, even if they are present in the table).  All the
**| cells will have content specified, even it is the empty string.  No
**| columns will be removed, even if missing from the row (because missing
**| and empty are semantically equivalent).
**|
**|| NextRowAllCells: read the next row in the table, and access all the
**| cells for this row in the table, adding any missing columns to the row
**| as needed until all cells are represented.  All the
**| cells will have content specified, even it is the empty string.  No
**| columns will be removed, even if missing from the row (because missing
**| and empty are semantically equivalent).
**|
|*/
class nsIMdbTableRowCursor : public nsIMdbCursor { // table row iterator
public:

// { ===== begin nsIMdbTableRowCursor methods =====

  // { ----- begin attribute methods -----
  // virtual mdb_err SetTable(nsIMdbEnv* ev, nsIMdbTable* ioTable) = 0; // sets pos to -1
  // Method SetTable() cut and made obsolete in keeping with new sorting methods.
  
  virtual mdb_err GetTable(nsIMdbEnv* ev, nsIMdbTable** acqTable) = 0;
  // } ----- end attribute methods -----

  // { ----- begin duplicate row removal methods -----
  virtual mdb_err CanHaveDupRowMembers(nsIMdbEnv* ev, // cursor might hold dups?
    mdb_bool* outCanHaveDups) = 0;
    
  virtual mdb_err MakeUniqueCursor( // clone cursor, removing duplicate rows
    nsIMdbEnv* ev, // context
    nsIMdbTableRowCursor** acqCursor) = 0;    // acquire clone with no dups
    // Note that MakeUniqueCursor() is never necessary for a cursor which was
    // created by table method nsIMdbTable::GetTableRowCursor(), because a table
    // never contains the same row as a member more than once.  However, a cursor
    // created by table method nsIMdbTable::FindRowMatches() might contain the
    // same row more than once, because the same row can generate a hit by more
    // than one column with a matching string prefix.  Note this method can
    // return the very same cursor instance with just an incremented refcount,
    // when the original cursor could not contain any duplicate rows (calling
    // CanHaveDupRowMembers() shows this case on a false return).  Otherwise
    // this method returns a different cursor instance.  Callers should not use
    // this MakeUniqueCursor() method lightly, because it tends to defeat the
    // purpose of lazy programming techniques, since it can force creation of
    // an explicit row collection in a new cursor's representation, in order to
    // inspect the row membership and remove any duplicates; this can have big
    // impact if a collection holds tens of thousands of rows or more, when
    // the original cursor with dups simply referenced rows indirectly by row
    // position ranges, without using an explicit row set representation.
    // Callers are encouraged to use nsIMdbCursor::GetCount() to determine
    // whether the row collection is very large (tens of thousands), and to
    // delay calling MakeUniqueCursor() when possible, until a user interface
    // element actually demands the creation of an explicit set representation.
  // } ----- end duplicate row removal methods -----

  // { ----- begin oid iteration methods -----
  virtual mdb_err NextRowOid( // get row id of next row in the table
    nsIMdbEnv* ev, // context
    mdbOid* outOid, // out row oid
    mdb_pos* outRowPos) = 0;    // zero-based position of the row in table
  // } ----- end oid iteration methods -----

  // { ----- begin row iteration methods -----
  virtual mdb_err NextRow( // get row cells from table for cells already in row
    nsIMdbEnv* ev, // context
    nsIMdbRow** acqRow, // acquire next row in table
    mdb_pos* outRowPos) = 0;    // zero-based position of the row in table
  // } ----- end row iteration methods -----

  // { ----- begin copy iteration methods -----
  // virtual mdb_err NextRowCopy( // put row cells into sink only when already in sink
  //   nsIMdbEnv* ev, // context
  //   nsIMdbRow* ioSinkRow, // sink for row cells read from next row
  //   mdbOid* outOid, // out row oid
  //   mdb_pos* outRowPos) = 0;    // zero-based position of the row in table
  // 
  // virtual mdb_err NextRowCopyAll( // put all row cells into sink, adding to sink
  //   nsIMdbEnv* ev, // context
  //   nsIMdbRow* ioSinkRow, // sink for row cells read from next row
  //   mdbOid* outOid, // out row oid
  //   mdb_pos* outRowPos) = 0;    // zero-based position of the row in table
  // } ----- end copy iteration methods -----

// } ===== end nsIMdbTableRowCursor methods =====
};

/*| nsIMdbRow: a collection of cells
**|
|*/
class nsIMdbRow : public nsIMdbCollection { // cell tuple
public:

// { ===== begin nsIMdbRow methods =====

  // { ----- begin cursor methods -----
  virtual mdb_err GetRowCellCursor( // make a cursor starting iteration at inRowPos
    nsIMdbEnv* ev, // context
    mdb_pos inRowPos, // zero-based ordinal position of row in table
    nsIMdbRowCellCursor** acqCursor) = 0; // acquire new cursor instance
  // } ----- end cursor methods -----

  // { ----- begin column methods -----
  virtual mdb_err AddColumn( // make sure a particular column is inside row
    nsIMdbEnv* ev, // context
    mdb_column inColumn, // column to add
    const mdbYarn* inYarn) = 0; // cell value to install

  virtual mdb_err CutColumn( // make sure a column is absent from the row
    nsIMdbEnv* ev, // context
    mdb_column inColumn) = 0; // column to ensure absent from row

  virtual mdb_err CutAllColumns( // remove all columns from the row
    nsIMdbEnv* ev) = 0; // context
  // } ----- end column methods -----

  // { ----- begin cell methods -----
  virtual mdb_err NewCell( // get cell for specified column, or add new one
    nsIMdbEnv* ev, // context
    mdb_column inColumn, // column to add
    nsIMdbCell** acqCell) = 0; // cell column and value
    
  virtual mdb_err AddCell( // copy a cell from another row to this row
    nsIMdbEnv* ev, // context
    const nsIMdbCell* inCell) = 0; // cell column and value
    
  virtual mdb_err GetCell( // find a cell in this row
    nsIMdbEnv* ev, // context
    mdb_column inColumn, // column to find
    nsIMdbCell** acqCell) = 0; // cell for specified column, or null
    
  virtual mdb_err EmptyAllCells( // make all cells in row empty of content
    nsIMdbEnv* ev) = 0; // context
  // } ----- end cell methods -----

  // { ----- begin row methods -----
  virtual mdb_err AddRow( // add all cells in another row to this one
    nsIMdbEnv* ev, // context
    nsIMdbRow* ioSourceRow) = 0; // row to union with
    
  virtual mdb_err SetRow( // make exact duplicate of another row
    nsIMdbEnv* ev, // context
    nsIMdbRow* ioSourceRow) = 0; // row to duplicate
  // } ----- end row methods -----

  // { ----- begin blob methods -----  
  virtual mdb_err SetCellYarn(nsIMdbEnv* ev, // synonym for AddColumn()
    mdb_column inColumn, // column to write
    const mdbYarn* inYarn) = 0;   // reads from yarn slots
  // make this text object contain content from the yarn's buffer
  
  virtual mdb_err GetCellYarn(nsIMdbEnv* ev, 
    mdb_column inColumn, // column to read 
    mdbYarn* outYarn) = 0;  // writes some yarn slots 
  // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
  
  virtual mdb_err AliasCellYarn(nsIMdbEnv* ev, 
    mdb_column inColumn, // column to alias
    mdbYarn* outYarn) = 0; // writes ALL yarn slots
  
  virtual mdb_err NextCellYarn(nsIMdbEnv* ev, // iterative version of GetCellYarn()
    mdb_column* ioColumn, // next column to read
    mdbYarn* outYarn) = 0;  // writes some yarn slots 
  // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
  //
  // The ioColumn argument is an inout parameter which initially contains the
  // last column accessed and returns the next column corresponding to the
  // content read into the yarn.  Callers should start with a zero column
  // value to say 'no previous column', which causes the first column to be
  // read.  Then the value returned in ioColumn is perfect for the next call
  // to NextCellYarn(), since it will then be the previous column accessed.
  // Callers need only examine the column token returned to see which cell
  // in the row is being read into the yarn.  When no more columns remain,
  // and the iteration has ended, ioColumn will return a zero token again.
  // So iterating over cells starts and ends with a zero column token.

  virtual mdb_err SeekCellYarn( // resembles nsIMdbRowCellCursor::SeekCell()
    nsIMdbEnv* ev, // context
    mdb_pos inPos, // position of cell in row sequence
    mdb_column* outColumn, // column for this particular cell
    mdbYarn* outYarn) = 0; // writes some yarn slots
  // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
  // Callers can pass nil for outYarn to indicate no interest in content, so
  // only the outColumn value is returned.  NOTE to subclasses: you must be
  // able to ignore outYarn when the pointer is nil; please do not crash.

  // } ----- end blob methods -----

// } ===== end nsIMdbRow methods =====
};

/*| nsIMdbRowCellCursor: cursor class for iterating row cells
**|
**|| row: the cursor is associated with a specific row, which can be
**| set to a different row (which resets the position to -1 so the
**| next cell acquired is the first in the row.
**|
**|| NextCell: get the next cell in the row and return its position and
**| a new instance of a nsIMdbCell to represent this next cell.
|*/
class nsIMdbRowCellCursor : public nsIMdbCursor { // cell collection iterator
public:

// { ===== begin nsIMdbRowCellCursor methods =====

  // { ----- begin attribute methods -----
  virtual mdb_err SetRow(nsIMdbEnv* ev, nsIMdbRow* ioRow) = 0; // sets pos to -1
  virtual mdb_err GetRow(nsIMdbEnv* ev, nsIMdbRow** acqRow) = 0;
  // } ----- end attribute methods -----

  // { ----- begin cell creation methods -----
  virtual mdb_err MakeCell( // get cell at current pos in the row
    nsIMdbEnv* ev, // context
    mdb_column* outColumn, // column for this particular cell
    mdb_pos* outPos, // position of cell in row sequence
    nsIMdbCell** acqCell) = 0; // the cell at inPos
  // } ----- end cell creation methods -----

  // { ----- begin cell seeking methods -----
  virtual mdb_err SeekCell( // same as SetRow() followed by MakeCell()
    nsIMdbEnv* ev, // context
    mdb_pos inPos, // position of cell in row sequence
    mdb_column* outColumn, // column for this particular cell
    nsIMdbCell** acqCell) = 0; // the cell at inPos
  // } ----- end cell seeking methods -----

  // { ----- begin cell iteration methods -----
  virtual mdb_err NextCell( // get next cell in the row
    nsIMdbEnv* ev, // context
    nsIMdbCell* ioCell, // changes to the next cell in the iteration
    mdb_column* outColumn, // column for this particular cell
    mdb_pos* outPos) = 0; // position of cell in row sequence
    
  virtual mdb_err PickNextCell( // get next cell in row within filter set
    nsIMdbEnv* ev, // context
    nsIMdbCell* ioCell, // changes to the next cell in the iteration
    const mdbColumnSet* inFilterSet, // col set of actual caller interest
    mdb_column* outColumn, // column for this particular cell
    mdb_pos* outPos) = 0; // position of cell in row sequence

  // Note that inFilterSet should not have too many (many more than 10?)
  // cols, since this might imply a potential excessive consumption of time
  // over many cursor calls when looking for column and filter intersection.
  // } ----- end cell iteration methods -----

// } ===== end nsIMdbRowCellCursor methods =====
};

/*| nsIMdbBlob: a base class for objects composed mainly of byte sequence state.
**| (This provides a base class for nsIMdbCell, so that cells themselves can
**| be used to set state in another cell, without extracting a buffer.)
|*/
class nsIMdbBlob : public nsIMdbObject { // a string with associated charset
public:

// { ===== begin nsIMdbBlob methods =====

  // { ----- begin attribute methods -----
  virtual mdb_err SetBlob(nsIMdbEnv* ev,
    nsIMdbBlob* ioBlob) = 0; // reads inBlob slots
  // when inBlob is in the same suite, this might be fastest cell-to-cell
  
  virtual mdb_err ClearBlob( // make empty (so content has zero length)
    nsIMdbEnv* ev) = 0;
  // clearing a yarn is like SetYarn() with empty yarn instance content
  
  virtual mdb_err GetBlobFill(nsIMdbEnv* ev,
    mdb_fill* outFill) = 0;  // size of blob 
  // Same value that would be put into mYarn_Fill, if one called GetYarn()
  // with a yarn instance that had mYarn_Buf==nil and mYarn_Size==0.
  
  virtual mdb_err SetYarn(nsIMdbEnv* ev, 
    const mdbYarn* inYarn) = 0;   // reads from yarn slots
  // make this text object contain content from the yarn's buffer
  
  virtual mdb_err GetYarn(nsIMdbEnv* ev, 
    mdbYarn* outYarn) = 0;  // writes some yarn slots 
  // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
  
  virtual mdb_err AliasYarn(nsIMdbEnv* ev, 
    mdbYarn* outYarn) = 0; // writes ALL yarn slots
  // AliasYarn() reveals sensitive internal text buffer state to the caller
  // by setting mYarn_Buf to point into the guts of this text implementation.
  //
  // The caller must take great care to avoid writing on this space, and to
  // avoid calling any method that would cause the state of this text object
  // to change (say by directly or indirectly setting the text to hold more
  // content that might grow the size of the buffer and free the old buffer).
  // In particular, callers should scrupulously avoid making calls into the
  // mdb interface to write any content while using the buffer pointer found
  // in the returned yarn instance.  Best safe usage involves copying content
  // into some other kind of external content representation beyond mdb.
  //
  // (The original design of this method a week earlier included the concept
  // of very fast and efficient cooperative locking via a pointer to some lock
  // member slot.  But let's ignore that complexity in the current design.)
  //
  // AliasYarn() is specifically intended as the first step in transferring
  // content from nsIMdbBlob to a nsString representation, without forcing extra
  // allocations and/or memory copies. (A standard nsIMdbBlob_AsString() utility
  // will use AliasYarn() as the first step in setting a nsString instance.)
  //
  // This is an alternative to the GetYarn() method, which has copy semantics
  // only; AliasYarn() relaxes a robust safety principle only for performance
  // reasons, to accomodate the need for callers to transform text content to
  // some other canonical representation that would necessitate an additional
  // copy and transformation when such is incompatible with the mdbYarn format.
  //
  // The implementation of AliasYarn() should have extremely little overhead
  // besides the virtual dispatch to the method implementation, and the code
  // necessary to populate all the mdbYarn member slots with internal buffer
  // address and metainformation that describes the buffer content.  Note that
  // mYarn_Grow must always be set to nil to indicate no resizing is allowed.
  
  // } ----- end attribute methods -----

// } ===== end nsIMdbBlob methods =====
};

/*| nsIMdbCell: the text in a single column of a row.  The base nsIMdbBlob
**| class provides all the interface related to accessing cell text.
**|
**|| column: each cell in a row appears in a specific column, where this
**| column is identified by the an integer mdb_scope value (generated by
**| the StringToScopeToken() method in the containing nsIMdbPort instance).
**| Because a row cannot have more than one cell with the same column,
**| something must give if one calls SetColumn() with an existing column
**| in the same row. When this happens, the other cell is replaced with
**| this cell (and the old cell is closed if it has outstanding refs).
**|
**|| row: every cell instance is a part of some row, and every cell knows
**| which row is the parent row.  (Note this should be represented by a
**| weak backpointer, so that outstanding cell references cannot keep a
**| row open that should be closed. Otherwise we'd have ref graph cycles.)
**|
**|| text: a cell can either be text, or it can have a child row or table,
**| but not both at once.  If text is read from a cell with a child, the text
**| content should be empty (for AliasYarn()) or a description of the type
**| of child (perhaps "mdb:cell:child:row" or "mdb:cell:child:table").
**|
**|| child: a cell might reference another row or a table, rather than text.
**| The interface for putting and getting children rows and tables was first
**| defined in the nsIMdbTable interface, but then this was moved to this cell
**| interface as more natural. 
|*/
class nsIMdbCell : public nsIMdbBlob { // text attribute in row with column scope
public:

// { ===== begin nsIMdbCell methods =====

  // { ----- begin attribute methods -----
  virtual mdb_err SetColumn(nsIMdbEnv* ev, mdb_column inColumn) = 0; 
  virtual mdb_err GetColumn(nsIMdbEnv* ev, mdb_column* outColumn) = 0;
  
  virtual mdb_err GetCellInfo(  // all cell metainfo except actual content
    nsIMdbEnv* ev, 
    mdb_column* outColumn,           // the column in the containing row
    mdb_fill*   outBlobFill,         // the size of text content in bytes
    mdbOid*     outChildOid,         // oid of possible row or table child
    mdb_bool*   outIsRowChild) = 0;  // nonzero if child, and a row child

  // Checking all cell metainfo is a good way to avoid forcing a large cell
  // in to memory when you don't actually want to use the content.
  
  virtual mdb_err GetRow(nsIMdbEnv* ev, // parent row for this cell
    nsIMdbRow** acqRow) = 0;
  virtual mdb_err GetPort(nsIMdbEnv* ev, // port containing cell
    nsIMdbPort** acqPort) = 0;
  // } ----- end attribute methods -----

  // { ----- begin children methods -----
  virtual mdb_err HasAnyChild( // does cell have a child instead of text?
    nsIMdbEnv* ev,
    mdbOid* outOid,  // out id of row or table (or unbound if no child)
    mdb_bool* outIsRow) = 0; // nonzero if child is a row (rather than a table)

  virtual mdb_err GetAnyChild( // access table of specific attribute
    nsIMdbEnv* ev, // context
    nsIMdbRow** acqRow, // child row (or null)
    nsIMdbTable** acqTable) = 0; // child table (or null)


  virtual mdb_err SetChildRow( // access table of specific attribute
    nsIMdbEnv* ev, // context
    nsIMdbRow* ioRow) = 0; // inRow must be bound inside this same db port

  virtual mdb_err GetChildRow( // access row of specific attribute
    nsIMdbEnv* ev, // context
    nsIMdbRow** acqRow) = 0; // acquire child row (or nil if no child)


  virtual mdb_err SetChildTable( // access table of specific attribute
    nsIMdbEnv* ev, // context
    nsIMdbTable* inTable) = 0; // table must be bound inside this same db port

  virtual mdb_err GetChildTable( // access table of specific attribute
    nsIMdbEnv* ev, // context
    nsIMdbTable** acqTable) = 0; // acquire child table (or nil if no child)
  // } ----- end children methods -----

// } ===== end nsIMdbCell methods =====
};

// } %%%%% end C++ abstract class interfaces %%%%%

//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789

#endif /* _MDB_ */

