Index: tests/apitest.cc
===================================================================
--- tests/apitest.cc	(revision 7293)
+++ tests/apitest.cc	(working copy)
@@ -45,6 +45,13 @@
 BackendManager backendmanager;
 
 Xapian::Database
+get_database()
+{
+    vector<string> dbnames;
+    return backendmanager.get_database(dbnames);
+}
+
+Xapian::Database
 get_database(const string &dbname)
 {
     return backendmanager.get_database(dbname);
@@ -101,6 +108,7 @@
     RUNTESTS("inmemory", anydb);
     RUNTESTS("inmemory", specchar);
     RUNTESTS("inmemory", writabledb);
+    RUNTESTS("inmemory", writablelocaldb);
     RUNTESTS("inmemory", localdb);
     RUNTESTS("inmemory", positionaldb);
     RUNTESTS("inmemory", localpositionaldb);
@@ -114,6 +122,7 @@
     RUNTESTS("flint", anydb);
     RUNTESTS("flint", specchar);
     RUNTESTS("flint", writabledb);
+    RUNTESTS("flint", writablelocaldb);
     RUNTESTS("flint", localdb);
     RUNTESTS("flint", positionaldb);
     RUNTESTS("flint", localpositionaldb);
@@ -129,6 +138,7 @@
     RUNTESTS("quartz", anydb);
     RUNTESTS("quartz", specchar);
     RUNTESTS("quartz", writabledb);
+    RUNTESTS("quartz", writablelocaldb);
     RUNTESTS("quartz", localdb);
     RUNTESTS("quartz", positionaldb);
     RUNTESTS("quartz", localpositionaldb);
Index: tests/apitest.h
===================================================================
--- tests/apitest.h	(revision 7293)
+++ tests/apitest.h	(working copy)
@@ -26,6 +26,7 @@
 
 #include <xapian.h>
 
+Xapian::Database get_database();
 Xapian::Database get_database(const std::string &dbname);
 Xapian::Database get_database(const std::string &dbname,
 			      const std::string &dbname2);
Index: tests/api_db.cc
===================================================================
--- tests/api_db.cc	(revision 7293)
+++ tests/api_db.cc	(working copy)
@@ -1011,6 +1011,48 @@
     return true;
 }
 
+// tests all document postlists
+static bool test_allpostlist1()
+{
+    Xapian::Database db(get_database("apitest_manydocs"));
+    Xapian::PostingIterator i = db.postlist_begin("");
+    unsigned int j = 1;
+    while (i != db.postlist_end("")) {
+	TEST_EQUAL(*i, j);
+	i++;
+	j++;
+    }
+    TEST_EQUAL(j, 513);
+    return true;
+}
+
+static void test_emptyterm1_helper(Xapian::Database & db)
+{
+    // Don't bother with postlist_begin() because allpostlist tests cover that.
+    TEST_EXCEPTION(Xapian::InvalidArgumentError, db.positionlist_begin(1, ""));
+    TEST_EQUAL(db.get_doccount(), db.get_termfreq(""));
+    TEST_EQUAL(db.get_doccount() != 0, db.term_exists(""));
+    TEST_EQUAL(db.get_doccount(), db.get_collection_freq(""));
+}
+
+// tests results of passing an empty term to various methods
+static bool test_emptyterm1()
+{
+    Xapian::Database db(get_database("apitest_manydocs"));
+    TEST_EQUAL(db.get_doccount(), 512);
+    test_emptyterm1_helper(db);
+
+    db = get_database("apitest_onedoc");
+    TEST_EQUAL(db.get_doccount(), 1);
+    test_emptyterm1_helper(db);
+
+    db = get_database();
+    TEST_EQUAL(db.get_doccount(), 0);
+    test_emptyterm1_helper(db);
+
+    return true;
+}
+
 // tests collection frequency
 static bool test_collfreq1()
 {
@@ -1379,6 +1421,8 @@
     {"postlist4",	   test_postlist4},
     {"postlist5",	   test_postlist5},
     {"postlist6",	   test_postlist6},
+    {"allpostlist1",	   test_allpostlist1},
+    {"emptyterm1",         test_emptyterm1},
     {"termstats",	   test_termstats},
     {"sortvalue1",	   test_sortvalue1},
     // consistency1 will run on the remote backend, but it's particularly slow
Index: tests/api_wrdb.cc
===================================================================
--- tests/api_wrdb.cc	(revision 7293)
+++ tests/api_wrdb.cc	(working copy)
@@ -909,6 +909,76 @@
     return true;
 }
 
+// tests all document postlists
+static bool test_allpostlist2()
+{
+    Xapian::WritableDatabase db(get_writable_database("apitest_manydocs"));
+    Xapian::PostingIterator i = db.postlist_begin("");
+    unsigned int j = 1;
+    while (i != db.postlist_end("")) {
+	TEST_EQUAL(*i, j);
+	i++;
+	j++;
+    }
+    TEST_EQUAL(j, 513);
+
+    db.delete_document(1);
+    db.delete_document(50);
+    db.delete_document(512);
+
+    i = db.postlist_begin("");
+    j = 2;
+    while (i != db.postlist_end("")) {
+	TEST_EQUAL(*i, j);
+	i++;
+	j++;
+        if (j == 50) j++;
+    }
+    TEST_EQUAL(j, 512);
+
+    return true;
+}
+
+static void test_emptyterm2_helper(Xapian::WritableDatabase & db)
+{
+    // Don't bother with postlist_begin() because allpostlist tests cover that.
+    TEST_EXCEPTION(Xapian::InvalidArgumentError, db.positionlist_begin(1, ""));
+    TEST_EQUAL(db.get_doccount(), db.get_termfreq(""));
+    TEST_EQUAL(db.get_doccount() != 0, db.term_exists(""));
+    TEST_EQUAL(db.get_doccount(), db.get_collection_freq(""));
+}
+
+// tests results of passing an empty term to various methods
+// equivalent of emptyterm1 for a writable database
+static bool test_emptyterm2()
+{
+    Xapian::WritableDatabase db(get_writable_database("apitest_manydocs"));
+    TEST_EQUAL(db.get_doccount(), 512);
+    test_emptyterm2_helper(db);
+    db.delete_document(1);
+    TEST_EQUAL(db.get_doccount(), 511);
+    test_emptyterm2_helper(db);
+    db.delete_document(50);
+    TEST_EQUAL(db.get_doccount(), 510);
+    test_emptyterm2_helper(db);
+    db.delete_document(512);
+    TEST_EQUAL(db.get_doccount(), 509);
+    test_emptyterm2_helper(db);
+
+    db = get_writable_database("apitest_onedoc");
+    TEST_EQUAL(db.get_doccount(), 1);
+    test_emptyterm2_helper(db);
+    db.delete_document(1);
+    TEST_EQUAL(db.get_doccount(), 0);
+    test_emptyterm2_helper(db);
+
+    db = get_writable_database("");
+    TEST_EQUAL(db.get_doccount(), 0);
+    test_emptyterm2_helper(db);
+
+    return true;
+}
+
 // Check that PHRASE/NEAR becomes AND if there's no positional info in the
 // database.
 static bool test_phraseorneartoand1()
@@ -1035,7 +1105,14 @@
     {"replacedoc3",	   test_replacedoc3},
     {"replacedoc4",	   test_replacedoc4},
     {"uniqueterm1",	   test_uniqueterm1},
+    {"emptyterm2",	   test_emptyterm2},
     {"phraseorneartoand1", test_phraseorneartoand1},
     {"longpositionlist1",  test_longpositionlist1},
     {0, 0}
 };
+
+/// The tests which use a writable, but local, backend
+test_desc writablelocaldb_tests[] = {
+    {"allpostlist2",	   test_allpostlist2},
+    {0, 0}
+};
Index: tests/api_wrdb.h
===================================================================
--- tests/api_wrdb.h	(revision 7293)
+++ tests/api_wrdb.h	(working copy)
@@ -27,5 +27,6 @@
 #include "testsuite.h"
 
 extern test_desc writabledb_tests[];
+extern test_desc writablelocaldb_tests[];
 
 #endif /* XAPIAN_HGUARD_API_WRDB_H */
Index: common/database.h
===================================================================
--- common/database.h	(revision 7293)
+++ common/database.h	(working copy)
@@ -188,7 +188,7 @@
 	 *                use.
 	 */
 	LeafPostList * open_post_list(const string & tname) const {
-	    if (!term_exists(tname)) {
+	    if (!tname.empty() && !term_exists(tname)) {
 		DEBUGLINE(MATCH, tname + " is not in database.");
 		// Term doesn't exist in this database.  However, we create
 		// a (empty) postlist for it to help make distributed searching
Index: api/omdatabase.cc
===================================================================
--- api/omdatabase.cc	(revision 7293)
+++ api/omdatabase.cc	(working copy)
@@ -101,8 +101,6 @@
 Database::postlist_begin(const string &tname) const
 {
     DEBUGAPICALL(PostingIterator, "Database::postlist_begin", tname);
-    if (tname.empty())
-	throw InvalidArgumentError("Zero length terms are invalid");
 
     // Don't bother checking that the term exists first.  If it does, we
     // just end up doing more work, and if it doesn't, we save very little
@@ -248,8 +246,9 @@
 Database::get_termfreq(const string & tname) const
 {
     DEBUGAPICALL(Xapian::doccount, "Database::get_termfreq", tname);
-    if (tname.empty())
-	throw InvalidArgumentError("Zero length terms are invalid");
+    if (tname.empty()) {
+        return get_doccount();
+    }
     Xapian::doccount tf = 0;
     vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
     for (i = internal.begin(); i != internal.end(); i++) {
@@ -262,8 +261,9 @@
 Database::get_collection_freq(const string & tname) const
 {
     DEBUGAPICALL(Xapian::termcount, "Database::get_collection_freq", tname);
-    if (tname.empty())
-	throw InvalidArgumentError("Zero length terms are invalid");
+    if (tname.empty()) {
+        return get_doccount();
+    }
 
     Xapian::termcount cf = 0;
     vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
@@ -303,8 +303,9 @@
 bool
 Database::term_exists(const string & tname) const
 {
-    if (tname.empty())
-	throw InvalidArgumentError("Zero length terms are invalid");
+    if (tname.empty()) {
+        return get_doccount() != 0;
+    }
     vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
     for (i = internal.begin(); i != internal.end(); ++i) {
 	if ((*i)->term_exists(tname)) return true;
Index: backends/inmemory/inmemory_database.cc
===================================================================
--- backends/inmemory/inmemory_database.cc	(revision 7293)
+++ backends/inmemory/inmemory_database.cc	(working copy)
@@ -260,6 +260,89 @@
     return Xapian::PositionIterator(db->open_position_list(did, (*pos).tname));
 }
 
+/////////////////////////////
+// InMemoryAllDocsPostList //
+/////////////////////////////
+
+InMemoryAllDocsPostList::InMemoryAllDocsPostList(Xapian::Internal::RefCntPtr<const InMemoryDatabase> db_)
+        : did(0), db(db_)
+{
+}
+
+Xapian::doccount
+InMemoryAllDocsPostList::get_termfreq() const
+{
+    return db->totdocs;
+}
+    
+Xapian::docid
+InMemoryAllDocsPostList::get_docid() const
+{
+    Assert(did > 0);
+    Assert(did <= db->termlists.size());
+    Assert(db->termlists[did - 1].is_valid);
+    return did;
+}
+
+Xapian::doclength
+InMemoryAllDocsPostList::get_doclength() const
+{
+    return db->get_doclength(did);
+}
+
+Xapian::termcount
+InMemoryAllDocsPostList::get_wdf() const
+{
+    return 1;
+}
+
+PositionList *
+InMemoryAllDocsPostList::read_position_list()
+{
+    throw Xapian::UnimplementedError("Can't open position list for all docs iterator"); 
+}
+
+PositionList *
+InMemoryAllDocsPostList::open_position_list() const
+{
+    throw Xapian::UnimplementedError("Can't open position list for all docs iterator"); 
+}
+
+PostList *
+InMemoryAllDocsPostList::next(Xapian::weight /*w_min*/)
+{
+    Assert(!at_end());
+    do {
+       ++did;
+    } while (did <= db->termlists.size() && !db->termlists[did - 1].is_valid);
+    return NULL;
+}
+
+PostList *
+InMemoryAllDocsPostList::skip_to(Xapian::docid did_, Xapian::weight /*w_min*/)
+{
+    Assert(!at_end());
+    if (did <= did_) {
+        did = did_;
+        while (did <= db->termlists.size() && !db->termlists[did - 1].is_valid) {
+            ++did;
+        }
+    }
+    return NULL;
+}
+
+bool
+InMemoryAllDocsPostList::at_end() const
+{
+    return (did > db->termlists.size());
+}
+
+string
+InMemoryAllDocsPostList::get_description() const
+{
+    return "InMemoryAllDocsPostList" + om_tostring(did);
+}
+
 ///////////////////////////
 // Actual database class //
 ///////////////////////////
@@ -279,7 +362,11 @@
 LeafPostList *
 InMemoryDatabase::do_open_post_list(const string & tname) const
 {
-    Assert(tname.size() != 0);
+    if (tname.empty()) {
+        if (termlists.empty())
+            return new EmptyPostList();
+        return new InMemoryAllDocsPostList(Xapian::Internal::RefCntPtr<const InMemoryDatabase>(this));
+    }
     map<string, InMemoryTerm>::const_iterator i = postlists.find(tname);
     if (i == postlists.end() || i->second.term_freq == 0)
 	return new EmptyPostList();
Index: backends/inmemory/inmemory_database.h
===================================================================
--- backends/inmemory/inmemory_database.h	(revision 7293)
+++ backends/inmemory/inmemory_database.h	(working copy)
@@ -159,6 +159,35 @@
 	string get_description() const;
 };
 
+/** A PostList over all docs in an inmemory database.
+ */
+class InMemoryAllDocsPostList : public LeafPostList {
+    friend class InMemoryDatabase;
+    private:
+        Xapian::docid did;
+
+        Xapian::Internal::RefCntPtr<const InMemoryDatabase> db;
+
+        InMemoryAllDocsPostList(Xapian::Internal::RefCntPtr<const InMemoryDatabase> db);
+    public:
+        Xapian::doccount get_termfreq() const;
+
+        Xapian::docid       get_docid() const;     // Gets current docid
+        Xapian::doclength   get_doclength() const; // Length of current document
+        Xapian::termcount   get_wdf() const;              // Within Document Frequency
+        PositionList * read_position_list();
+        PositionList * open_position_list() const;
+
+        PostList *next(Xapian::weight w_min); // Moves to next docid
+
+        PostList *skip_to(Xapian::docid did, Xapian::weight w_min); // Moves to next docid >= specified docid
+
+        // True if we're off the end of the list
+        bool at_end() const;
+
+        string get_description() const;
+};
+
 // Term List
 class InMemoryTermList : public LeafTermList {
     friend class InMemoryDatabase;
@@ -193,6 +222,7 @@
  *  This is a prototype database, mainly used for debugging and testing.
  */
 class InMemoryDatabase : public Xapian::Database::Internal {
+    friend class InMemoryAllDocsPostList;
     private:
 	map<string, InMemoryTerm> postlists;
 	vector<InMemoryDoc> termlists;
