summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/SConscript1
-rw-r--r--src/mongo/db/catalog/index_catalog.cpp2
-rw-r--r--src/mongo/db/index/2d_access_method.h2
-rw-r--r--src/mongo/db/index/btree_access_method.cpp235
-rw-r--r--src/mongo/db/index/btree_access_method.h2
-rw-r--r--src/mongo/db/index/btree_based_access_method.cpp278
-rw-r--r--src/mongo/db/index/btree_based_access_method.h (renamed from src/mongo/db/index/btree_access_method_internal.h)0
-rw-r--r--src/mongo/db/index/fts_access_method.h2
-rw-r--r--src/mongo/db/index/hash_access_method.h2
-rw-r--r--src/mongo/db/index/haystack_access_method.h2
-rw-r--r--src/mongo/db/index/s2_access_method.h2
11 files changed, 286 insertions, 242 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index 8c49a22b21e..7e2731a591f 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -567,6 +567,7 @@ serverOnlyFiles = [ "db/curop.cpp",
"db/index_legacy.cpp",
"db/index/2d_access_method.cpp",
"db/index/btree_access_method.cpp",
+ "db/index/btree_based_access_method.cpp",
"db/index/btree_based_builder.cpp",
"db/index/btree_index_cursor.cpp",
"db/index/btree_interface.cpp",
diff --git a/src/mongo/db/catalog/index_catalog.cpp b/src/mongo/db/catalog/index_catalog.cpp
index 943cee3cc9b..6e75820d287 100644
--- a/src/mongo/db/catalog/index_catalog.cpp
+++ b/src/mongo/db/catalog/index_catalog.cpp
@@ -41,7 +41,7 @@
#include "mongo/db/index_legacy.h"
#include "mongo/db/index/2d_access_method.h"
#include "mongo/db/index/btree_access_method.h"
-#include "mongo/db/index/btree_access_method_internal.h"
+#include "mongo/db/index/btree_based_access_method.h"
#include "mongo/db/index/fts_access_method.h"
#include "mongo/db/index/hash_access_method.h"
#include "mongo/db/index/haystack_access_method.h"
diff --git a/src/mongo/db/index/2d_access_method.h b/src/mongo/db/index/2d_access_method.h
index 782162da8b0..9b0a5f60f01 100644
--- a/src/mongo/db/index/2d_access_method.h
+++ b/src/mongo/db/index/2d_access_method.h
@@ -30,7 +30,7 @@
#include "mongo/base/status.h"
#include "mongo/db/index/2d_common.h"
-#include "mongo/db/index/btree_access_method_internal.h"
+#include "mongo/db/index/btree_based_access_method.h"
#include "mongo/db/jsobj.h"
namespace mongo {
diff --git a/src/mongo/db/index/btree_access_method.cpp b/src/mongo/db/index/btree_access_method.cpp
index d73ae59e321..24c7ac12810 100644
--- a/src/mongo/db/index/btree_access_method.cpp
+++ b/src/mongo/db/index/btree_access_method.cpp
@@ -40,241 +40,6 @@
namespace mongo {
- BtreeBasedAccessMethod::BtreeBasedAccessMethod(IndexCatalogEntry* btreeState)
- : _btreeState(btreeState), _descriptor(btreeState->descriptor()) {
-
- verify(0 == _descriptor->version() || 1 == _descriptor->version());
- _interface = BtreeInterface::interfaces[_descriptor->version()];
- }
-
- // Find the keys for obj, put them in the tree pointing to loc
- Status BtreeBasedAccessMethod::insert(const BSONObj& obj, const DiskLoc& loc,
- const InsertDeleteOptions& options, int64_t* numInserted) {
-
- *numInserted = 0;
-
- BSONObjSet keys;
- // Delegate to the subclass.
- getKeys(obj, &keys);
-
- Status ret = Status::OK();
-
- for (BSONObjSet::const_iterator i = keys.begin(); i != keys.end(); ++i) {
- try {
- _interface->bt_insert(_btreeState,
- _btreeState->head(),
- loc,
- *i,
- options.dupsAllowed,
- true);
- ++*numInserted;
- } catch (AssertionException& e) {
- if (10287 == e.getCode() && !_btreeState->isReady()) {
- // This is the duplicate key exception. We ignore it for some reason in BG
- // indexing.
- DEV log() << "info: key already in index during bg indexing (ok)\n";
- } else if (!options.dupsAllowed) {
- // Assuming it's a duplicate key exception. Clean up any inserted keys.
- for (BSONObjSet::const_iterator j = keys.begin(); j != i; ++j) {
- removeOneKey(*j, loc);
- }
- *numInserted = 0;
- return Status(ErrorCodes::DuplicateKey, e.what(), e.getCode());
- } else {
- problem() << " caught assertion addKeysToIndex "
- << _descriptor->indexNamespace()
- << obj["_id"] << endl;
- ret = Status(ErrorCodes::InternalError, e.what(), e.getCode());
- }
- }
- }
-
- if (*numInserted > 1) {
- _btreeState->setMultikey();
- }
-
- return ret;
- }
-
- bool BtreeBasedAccessMethod::removeOneKey(const BSONObj& key, const DiskLoc& loc) {
- bool ret = false;
-
- try {
- ret = _interface->unindex(_btreeState,
- _btreeState->head(),
- key,
- loc);
- } catch (AssertionException& e) {
- problem() << "Assertion failure: _unindex failed "
- << _descriptor->indexNamespace() << endl;
- out() << "Assertion failure: _unindex failed: " << e.what() << '\n';
- out() << " obj:" << loc.obj().toString() << '\n';
- out() << " key:" << key.toString() << '\n';
- out() << " dl:" << loc.toString() << endl;
- logContext();
- }
-
- return ret;
- }
-
- // Remove the provided doc from the index.
- Status BtreeBasedAccessMethod::remove(const BSONObj &obj, const DiskLoc& loc,
- const InsertDeleteOptions &options, int64_t* numDeleted) {
-
- BSONObjSet keys;
- getKeys(obj, &keys);
- *numDeleted = 0;
-
- for (BSONObjSet::const_iterator i = keys.begin(); i != keys.end(); ++i) {
- bool thisKeyOK = removeOneKey(*i, loc);
-
- if (thisKeyOK) {
- ++*numDeleted;
- } else if (options.logIfError) {
- log() << "unindex failed (key too big?) " << _descriptor->indexNamespace()
- << " key: " << *i << " " << loc.obj()["_id"] << endl;
- }
- }
-
- return Status::OK();
- }
-
- // Return keys in l that are not in r.
- // Lifted basically verbatim from elsewhere.
- static void setDifference(const BSONObjSet &l, const BSONObjSet &r, vector<BSONObj*> *diff) {
- // l and r must use the same ordering spec.
- verify(l.key_comp().order() == r.key_comp().order());
- BSONObjSet::const_iterator i = l.begin();
- BSONObjSet::const_iterator j = r.begin();
- while ( 1 ) {
- if ( i == l.end() )
- break;
- while ( j != r.end() && j->woCompare( *i ) < 0 )
- j++;
- if ( j == r.end() || i->woCompare(*j) != 0 ) {
- const BSONObj *jo = &*i;
- diff->push_back( (BSONObj *) jo );
- }
- i++;
- }
- }
-
- Status BtreeBasedAccessMethod::touch(const BSONObj& obj) {
- BSONObjSet keys;
- getKeys(obj, &keys);
-
- for (BSONObjSet::const_iterator i = keys.begin(); i != keys.end(); ++i) {
- int unusedPos;
- bool unusedFound;
- DiskLoc unusedDiskLoc;
- _interface->locate(_btreeState,
- _btreeState->head(),
- *i,
- unusedPos,
- unusedFound,
- unusedDiskLoc,
- 1);
- }
-
- return Status::OK();
- }
-
- DiskLoc BtreeBasedAccessMethod::findSingle( const BSONObj& key ) {
- DiskLoc head = _btreeState->head();
- Record* record = _btreeState->recordStore()->recordFor( head );
-
- if ( 0 == _descriptor->version() ) {
- return BtreeBucket<V0>::asVersion( record )->findSingle( _btreeState,
- _btreeState->head(),
- key );
- }
- if ( 1 == _descriptor->version() ) {
- return BtreeBucket<V1>::asVersion( record )->findSingle( _btreeState,
- _btreeState->head(),
- key );
- }
- verify( 0 );
- }
-
-
- Status BtreeBasedAccessMethod::validate(int64_t* numKeys) {
- *numKeys = _interface->fullValidate(_btreeState,
- _btreeState->head(),
- _descriptor->keyPattern());
- return Status::OK();
- }
-
- Status BtreeBasedAccessMethod::validateUpdate(
- const BSONObj &from, const BSONObj &to, const DiskLoc &record,
- const InsertDeleteOptions &options, UpdateTicket* status) {
-
- BtreeBasedPrivateUpdateData *data = new BtreeBasedPrivateUpdateData();
- status->_indexSpecificUpdateData.reset(data);
-
- getKeys(from, &data->oldKeys);
- getKeys(to, &data->newKeys);
- data->loc = record;
- data->dupsAllowed = options.dupsAllowed;
-
- setDifference(data->oldKeys, data->newKeys, &data->removed);
- setDifference(data->newKeys, data->oldKeys, &data->added);
-
- bool checkForDups = !data->added.empty()
- && (KeyPattern::isIdKeyPattern(_descriptor->keyPattern()) || _descriptor->unique())
- && !options.dupsAllowed;
-
- if (checkForDups) {
- for (vector<BSONObj*>::iterator i = data->added.begin(); i != data->added.end(); i++) {
- if (_interface->wouldCreateDup(_btreeState,
- _btreeState->head(),
- **i, record)) {
- status->_isValid = false;
- return Status(ErrorCodes::DuplicateKey,
- _interface->dupKeyError(_btreeState,
- _btreeState->head(),
- **i));
- }
- }
- }
-
- status->_isValid = true;
-
- return Status::OK();
- }
-
- Status BtreeBasedAccessMethod::update(const UpdateTicket& ticket, int64_t* numUpdated) {
- if (!ticket._isValid) {
- return Status(ErrorCodes::InternalError, "Invalid updateticket in update");
- }
-
- BtreeBasedPrivateUpdateData* data =
- static_cast<BtreeBasedPrivateUpdateData*>(ticket._indexSpecificUpdateData.get());
-
- if (data->oldKeys.size() + data->added.size() - data->removed.size() > 1) {
- _btreeState->setMultikey();
- }
-
- for (size_t i = 0; i < data->added.size(); ++i) {
- _interface->bt_insert(_btreeState,
- _btreeState->head(),
- data->loc,
- *data->added[i],
- data->dupsAllowed,
- true);
- }
-
- for (size_t i = 0; i < data->removed.size(); ++i) {
- _interface->unindex(_btreeState,
- _btreeState->head(),
- *data->removed[i],
- data->loc);
- }
-
- *numUpdated = data->added.size();
-
- return Status::OK();
- }
-
// Standard Btree implementation below.
BtreeAccessMethod::BtreeAccessMethod(IndexCatalogEntry* btreeState)
: BtreeBasedAccessMethod(btreeState) {
diff --git a/src/mongo/db/index/btree_access_method.h b/src/mongo/db/index/btree_access_method.h
index d6c80a7f215..0c3914d97b5 100644
--- a/src/mongo/db/index/btree_access_method.h
+++ b/src/mongo/db/index/btree_access_method.h
@@ -34,7 +34,7 @@
#include "mongo/db/structure/btree/btree.h"
#include "mongo/db/index/index_access_method.h"
#include "mongo/db/index/btree_key_generator.h"
-#include "mongo/db/index/btree_access_method_internal.h"
+#include "mongo/db/index/btree_based_access_method.h"
#include "mongo/db/jsobj.h"
namespace mongo {
diff --git a/src/mongo/db/index/btree_based_access_method.cpp b/src/mongo/db/index/btree_based_access_method.cpp
new file mode 100644
index 00000000000..8af96bbadbf
--- /dev/null
+++ b/src/mongo/db/index/btree_based_access_method.cpp
@@ -0,0 +1,278 @@
+/**
+* Copyright (C) 2013 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+* As a special exception, the copyright holders give permission to link the
+* code of portions of this program with the OpenSSL library under certain
+* conditions as described in each individual source file and distribute
+* linked combinations including the program with the OpenSSL library. You
+* must comply with the GNU Affero General Public License in all respects for
+* all of the code used other than as permitted herein. If you modify file(s)
+* with this exception, you may extend this exception to your version of the
+* file(s), but you are not obligated to do so. If you do not wish to do so,
+* delete this exception statement from your version. If you delete this
+* exception statement from all source files in the program, then also delete
+* it in the license file.
+*/
+
+#include "mongo/db/index/btree_access_method.h"
+
+#include <vector>
+
+#include "mongo/base/status.h"
+#include "mongo/db/index/btree_index_cursor.h"
+#include "mongo/db/index/btree_interface.h"
+#include "mongo/db/jsobj.h"
+#include "mongo/db/keypattern.h"
+#include "mongo/db/pdfile.h"
+#include "mongo/db/pdfile_private.h"
+
+namespace mongo {
+
+ BtreeBasedAccessMethod::BtreeBasedAccessMethod(IndexCatalogEntry* btreeState)
+ : _btreeState(btreeState), _descriptor(btreeState->descriptor()) {
+
+ verify(0 == _descriptor->version() || 1 == _descriptor->version());
+ _interface = BtreeInterface::interfaces[_descriptor->version()];
+ }
+
+ // Find the keys for obj, put them in the tree pointing to loc
+ Status BtreeBasedAccessMethod::insert(const BSONObj& obj, const DiskLoc& loc,
+ const InsertDeleteOptions& options, int64_t* numInserted) {
+
+ *numInserted = 0;
+
+ BSONObjSet keys;
+ // Delegate to the subclass.
+ getKeys(obj, &keys);
+
+ Status ret = Status::OK();
+
+ for (BSONObjSet::const_iterator i = keys.begin(); i != keys.end(); ++i) {
+ try {
+ _interface->bt_insert(_btreeState,
+ _btreeState->head(),
+ loc,
+ *i,
+ options.dupsAllowed,
+ true);
+ ++*numInserted;
+ } catch (AssertionException& e) {
+ if (10287 == e.getCode() && !_btreeState->isReady()) {
+ // This is the duplicate key exception. We ignore it for some reason in BG
+ // indexing.
+ DEV log() << "info: key already in index during bg indexing (ok)\n";
+ } else if (!options.dupsAllowed) {
+ // Assuming it's a duplicate key exception. Clean up any inserted keys.
+ for (BSONObjSet::const_iterator j = keys.begin(); j != i; ++j) {
+ removeOneKey(*j, loc);
+ }
+ *numInserted = 0;
+ return Status(ErrorCodes::DuplicateKey, e.what(), e.getCode());
+ } else {
+ problem() << " caught assertion addKeysToIndex "
+ << _descriptor->indexNamespace()
+ << obj["_id"] << endl;
+ ret = Status(ErrorCodes::InternalError, e.what(), e.getCode());
+ }
+ }
+ }
+
+ if (*numInserted > 1) {
+ _btreeState->setMultikey();
+ }
+
+ return ret;
+ }
+
+ bool BtreeBasedAccessMethod::removeOneKey(const BSONObj& key, const DiskLoc& loc) {
+ bool ret = false;
+
+ try {
+ ret = _interface->unindex(_btreeState,
+ _btreeState->head(),
+ key,
+ loc);
+ } catch (AssertionException& e) {
+ problem() << "Assertion failure: _unindex failed "
+ << _descriptor->indexNamespace() << endl;
+ out() << "Assertion failure: _unindex failed: " << e.what() << '\n';
+ out() << " obj:" << loc.obj().toString() << '\n';
+ out() << " key:" << key.toString() << '\n';
+ out() << " dl:" << loc.toString() << endl;
+ logContext();
+ }
+
+ return ret;
+ }
+
+ // Remove the provided doc from the index.
+ Status BtreeBasedAccessMethod::remove(const BSONObj &obj, const DiskLoc& loc,
+ const InsertDeleteOptions &options, int64_t* numDeleted) {
+
+ BSONObjSet keys;
+ getKeys(obj, &keys);
+ *numDeleted = 0;
+
+ for (BSONObjSet::const_iterator i = keys.begin(); i != keys.end(); ++i) {
+ bool thisKeyOK = removeOneKey(*i, loc);
+
+ if (thisKeyOK) {
+ ++*numDeleted;
+ } else if (options.logIfError) {
+ log() << "unindex failed (key too big?) " << _descriptor->indexNamespace()
+ << " key: " << *i << " " << loc.obj()["_id"] << endl;
+ }
+ }
+
+ return Status::OK();
+ }
+
+ // Return keys in l that are not in r.
+ // Lifted basically verbatim from elsewhere.
+ static void setDifference(const BSONObjSet &l, const BSONObjSet &r, vector<BSONObj*> *diff) {
+ // l and r must use the same ordering spec.
+ verify(l.key_comp().order() == r.key_comp().order());
+ BSONObjSet::const_iterator i = l.begin();
+ BSONObjSet::const_iterator j = r.begin();
+ while ( 1 ) {
+ if ( i == l.end() )
+ break;
+ while ( j != r.end() && j->woCompare( *i ) < 0 )
+ j++;
+ if ( j == r.end() || i->woCompare(*j) != 0 ) {
+ const BSONObj *jo = &*i;
+ diff->push_back( (BSONObj *) jo );
+ }
+ i++;
+ }
+ }
+
+ Status BtreeBasedAccessMethod::touch(const BSONObj& obj) {
+ BSONObjSet keys;
+ getKeys(obj, &keys);
+
+ for (BSONObjSet::const_iterator i = keys.begin(); i != keys.end(); ++i) {
+ int unusedPos;
+ bool unusedFound;
+ DiskLoc unusedDiskLoc;
+ _interface->locate(_btreeState,
+ _btreeState->head(),
+ *i,
+ unusedPos,
+ unusedFound,
+ unusedDiskLoc,
+ 1);
+ }
+
+ return Status::OK();
+ }
+
+ DiskLoc BtreeBasedAccessMethod::findSingle( const BSONObj& key ) {
+ DiskLoc head = _btreeState->head();
+ Record* record = _btreeState->recordStore()->recordFor( head );
+
+ if ( 0 == _descriptor->version() ) {
+ return BtreeBucket<V0>::asVersion( record )->findSingle( _btreeState,
+ _btreeState->head(),
+ key );
+ }
+ if ( 1 == _descriptor->version() ) {
+ return BtreeBucket<V1>::asVersion( record )->findSingle( _btreeState,
+ _btreeState->head(),
+ key );
+ }
+ verify( 0 );
+ }
+
+
+ Status BtreeBasedAccessMethod::validate(int64_t* numKeys) {
+ *numKeys = _interface->fullValidate(_btreeState,
+ _btreeState->head(),
+ _descriptor->keyPattern());
+ return Status::OK();
+ }
+
+ Status BtreeBasedAccessMethod::validateUpdate(
+ const BSONObj &from, const BSONObj &to, const DiskLoc &record,
+ const InsertDeleteOptions &options, UpdateTicket* status) {
+
+ BtreeBasedPrivateUpdateData *data = new BtreeBasedPrivateUpdateData();
+ status->_indexSpecificUpdateData.reset(data);
+
+ getKeys(from, &data->oldKeys);
+ getKeys(to, &data->newKeys);
+ data->loc = record;
+ data->dupsAllowed = options.dupsAllowed;
+
+ setDifference(data->oldKeys, data->newKeys, &data->removed);
+ setDifference(data->newKeys, data->oldKeys, &data->added);
+
+ bool checkForDups = !data->added.empty()
+ && (KeyPattern::isIdKeyPattern(_descriptor->keyPattern()) || _descriptor->unique())
+ && !options.dupsAllowed;
+
+ if (checkForDups) {
+ for (vector<BSONObj*>::iterator i = data->added.begin(); i != data->added.end(); i++) {
+ if (_interface->wouldCreateDup(_btreeState,
+ _btreeState->head(),
+ **i, record)) {
+ status->_isValid = false;
+ return Status(ErrorCodes::DuplicateKey,
+ _interface->dupKeyError(_btreeState,
+ _btreeState->head(),
+ **i));
+ }
+ }
+ }
+
+ status->_isValid = true;
+
+ return Status::OK();
+ }
+
+ Status BtreeBasedAccessMethod::update(const UpdateTicket& ticket, int64_t* numUpdated) {
+ if (!ticket._isValid) {
+ return Status(ErrorCodes::InternalError, "Invalid updateticket in update");
+ }
+
+ BtreeBasedPrivateUpdateData* data =
+ static_cast<BtreeBasedPrivateUpdateData*>(ticket._indexSpecificUpdateData.get());
+
+ if (data->oldKeys.size() + data->added.size() - data->removed.size() > 1) {
+ _btreeState->setMultikey();
+ }
+
+ for (size_t i = 0; i < data->added.size(); ++i) {
+ _interface->bt_insert(_btreeState,
+ _btreeState->head(),
+ data->loc,
+ *data->added[i],
+ data->dupsAllowed,
+ true);
+ }
+
+ for (size_t i = 0; i < data->removed.size(); ++i) {
+ _interface->unindex(_btreeState,
+ _btreeState->head(),
+ *data->removed[i],
+ data->loc);
+ }
+
+ *numUpdated = data->added.size();
+
+ return Status::OK();
+ }
+
+} // namespace mongo
diff --git a/src/mongo/db/index/btree_access_method_internal.h b/src/mongo/db/index/btree_based_access_method.h
index 320c1de8944..320c1de8944 100644
--- a/src/mongo/db/index/btree_access_method_internal.h
+++ b/src/mongo/db/index/btree_based_access_method.h
diff --git a/src/mongo/db/index/fts_access_method.h b/src/mongo/db/index/fts_access_method.h
index eceff2ae3b9..47dc8a98405 100644
--- a/src/mongo/db/index/fts_access_method.h
+++ b/src/mongo/db/index/fts_access_method.h
@@ -30,7 +30,7 @@
#include "mongo/base/status.h"
#include "mongo/db/fts/fts_spec.h"
-#include "mongo/db/index/btree_access_method_internal.h"
+#include "mongo/db/index/btree_based_access_method.h"
#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/jsobj.h"
diff --git a/src/mongo/db/index/hash_access_method.h b/src/mongo/db/index/hash_access_method.h
index 3fdff0e2b7d..cb49b9e0b8b 100644
--- a/src/mongo/db/index/hash_access_method.h
+++ b/src/mongo/db/index/hash_access_method.h
@@ -33,7 +33,7 @@
#include "mongo/base/status.h"
#include "mongo/db/hasher.h" // For HashSeed.
#include "mongo/db/index/index_descriptor.h"
-#include "mongo/db/index/btree_access_method_internal.h"
+#include "mongo/db/index/btree_based_access_method.h"
#include "mongo/db/jsobj.h"
namespace mongo {
diff --git a/src/mongo/db/index/haystack_access_method.h b/src/mongo/db/index/haystack_access_method.h
index e24e10f7df9..1b07dce048e 100644
--- a/src/mongo/db/index/haystack_access_method.h
+++ b/src/mongo/db/index/haystack_access_method.h
@@ -29,7 +29,7 @@
#pragma once
#include "mongo/base/status.h"
-#include "mongo/db/index/btree_access_method_internal.h"
+#include "mongo/db/index/btree_based_access_method.h"
#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/jsobj.h"
diff --git a/src/mongo/db/index/s2_access_method.h b/src/mongo/db/index/s2_access_method.h
index 66c091f3e90..33e7cf1647b 100644
--- a/src/mongo/db/index/s2_access_method.h
+++ b/src/mongo/db/index/s2_access_method.h
@@ -30,7 +30,7 @@
#include "mongo/base/status.h"
#include "mongo/db/geo/s2common.h"
-#include "mongo/db/index/btree_access_method_internal.h"
+#include "mongo/db/index/btree_based_access_method.h"
#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/jsobj.h"