/** * Perform a remove operation, which might remove multiple documents. Dispatches to remove code * currently to do most of this. * * Might fault or error, otherwise populates the result. */ static void multiRemove( const BatchItemRef& removeItem, WriteOpResult* result ) { const NamespaceString nss( removeItem.getRequest()->getNS() ); DeleteRequest request( nss ); request.setQuery( removeItem.getDelete()->getQuery() ); request.setMulti( removeItem.getDelete()->getLimit() != 1 ); request.setUpdateOpLog(true); request.setGod( false ); DeleteExecutor executor( &request ); Status status = executor.prepare(); if ( !status.isOK() ) { result->error = toWriteError( status ); return; } // NOTE: Deletes will not fault outside the lock once any data has been written PageFaultRetryableSection pFaultSection; /////////////////////////////////////////// Lock::DBWrite writeLock( nss.ns() ); /////////////////////////////////////////// // Check version once we're locked if ( !checkShardVersion( &shardingState, *removeItem.getRequest(), &result->error ) ) { // Version error return; } // Context once we're locked, to set more details in currentOp() // TODO: better constructor? Client::Context writeContext( nss.ns(), storageGlobalParams.dbpath, false /* don't check version */); try { result->stats.n = executor.execute(); } catch ( const PageFaultException& ex ) { // TODO: An actual data structure that's not an exception for this result->fault = new PageFaultException( ex ); } catch ( const DBException& ex ) { status = ex.toStatus(); if (ErrorCodes::isInterruption(status.code())) { throw; } result->error = toWriteError(status); } }
void WriteBatchExecutor::execUpdate( const BatchItemRef& updateItem, BSONObj* upsertedId, WriteErrorDetail** error ) { // Updates currently do a lot of the lock management internally const BatchedCommandRequest& request = *updateItem.getRequest(); const NamespaceString nss( updateItem.getRequest()->getNS() ); // BEGIN CURRENT OP scoped_ptr<CurOp> currentOp( beginCurrentOp( _client, updateItem ) ); incOpStats( updateItem ); WriteOpResult result; { /////////////////////////////////////////// Lock::DBWrite writeLock( nss.ns() ); /////////////////////////////////////////// // Check version once we're locked if ( checkShardVersion( &shardingState, request, &result.error ) ) { // Context once we're locked, to set more details in currentOp() // TODO: better constructor? Client::Context writeContext( nss.ns(), storageGlobalParams.dbpath, false /* don't check version */); multiUpdate( updateItem, &result ); incWriteStats( updateItem, result.stats, result.error, currentOp.get() ); if ( !result.stats.upsertedID.isEmpty() ) { *upsertedId = result.stats.upsertedID.getOwned(); } } } // END CURRENT OP finishCurrentOp( _client, currentOp.get(), result.error ); if ( result.error ) { result.error->setIndex( updateItem.getItemIndex() ); *error = result.releaseError(); } }
/** * Perform a remove operation, which might remove multiple documents. Dispatches to remove code * currently to do most of this. * * Might fault or error, otherwise populates the result. */ static void multiRemove( OperationContext* txn, const BatchItemRef& removeItem, WriteOpResult* result ) { const NamespaceString nss( removeItem.getRequest()->getNS() ); DeleteRequest request( nss ); request.setQuery( removeItem.getDelete()->getQuery() ); request.setMulti( removeItem.getDelete()->getLimit() != 1 ); request.setUpdateOpLog(true); request.setGod( false ); DeleteExecutor executor( &request ); Status status = executor.prepare(); if ( !status.isOK() ) { result->setError(toWriteError(status)); return; } /////////////////////////////////////////// Lock::DBWrite writeLock(txn->lockState(), nss.ns()); /////////////////////////////////////////// // Check version once we're locked if (!checkShardVersion(txn, &shardingState, *removeItem.getRequest(), result)) { // Version error return; } // Context once we're locked, to set more details in currentOp() // TODO: better constructor? Client::Context writeContext( nss.ns(), storageGlobalParams.dbpath, false /* don't check version */); try { result->getStats().n = executor.execute(txn, writeContext.db()); } catch ( const DBException& ex ) { status = ex.toStatus(); if (ErrorCodes::isInterruption(status.code())) { throw; } result->setError(toWriteError(status)); } }
virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int options, string& errmsg, BSONObjBuilder& result, bool fromRepl = false ) { // --- parse NamespaceString ns( dbname, cmdObj[name].String() ); Status status = userAllowedWriteNS( ns ); if ( !status.isOK() ) return appendCommandStatus( result, status ); if ( cmdObj["indexes"].type() != Array ) { errmsg = "indexes has to be an array"; result.append( "cmdObj", cmdObj ); return false; } std::vector<BSONObj> specs; { BSONObjIterator i( cmdObj["indexes"].Obj() ); while ( i.more() ) { BSONElement e = i.next(); if ( e.type() != Object ) { errmsg = "everything in indexes has to be an Object"; result.append( "cmdObj", cmdObj ); return false; } specs.push_back( e.Obj() ); } } if ( specs.size() == 0 ) { errmsg = "no indexes to add"; return false; } // check specs for ( size_t i = 0; i < specs.size(); i++ ) { BSONObj spec = specs[i]; if ( spec["ns"].eoo() ) { spec = _addNsToSpec( ns, spec ); specs[i] = spec; } if ( spec["ns"].type() != String ) { errmsg = "spec has no ns"; result.append( "spec", spec ); return false; } if ( ns != spec["ns"].String() ) { errmsg = "namespace mismatch"; result.append( "spec", spec ); return false; } } { // We first take a read lock to see if we need to do anything // as many calls are ensureIndex (and hence no-ops), this is good so its a shared // lock for common calls. We only take write lock if needed. // Note: createIndexes command does not currently respect shard versioning. Client::ReadContext readContext( ns, storageGlobalParams.dbpath, false /* doVersion */ ); const Collection* collection = readContext.ctx().db()->getCollection( ns.ns() ); if ( collection ) { for ( size_t i = 0; i < specs.size(); i++ ) { BSONObj spec = specs[i]; StatusWith<BSONObj> statusWithSpec = collection->getIndexCatalog()->prepareSpecForCreate( spec ); status = statusWithSpec.getStatus(); if ( status.code() == ErrorCodes::IndexAlreadyExists ) { specs.erase( specs.begin() + i ); i--; continue; } if ( !status.isOK() ) return appendCommandStatus( result, status ); } if ( specs.size() == 0 ) { result.append( "numIndexesBefore", collection->getIndexCatalog()->numIndexesTotal() ); result.append( "note", "all indexes already exist" ); return true; } // need to create index } } // now we know we have to create index(es) // Note: createIndexes command does not currently respect shard versioning. Client::WriteContext writeContext( ns.ns(), storageGlobalParams.dbpath, false /* doVersion */ ); Database* db = writeContext.ctx().db(); Collection* collection = db->getCollection( txn, ns.ns() ); result.appendBool( "createdCollectionAutomatically", collection == NULL ); if ( !collection ) { collection = db->createCollection( txn, ns.ns() ); invariant( collection ); } result.append( "numIndexesBefore", collection->getIndexCatalog()->numIndexesTotal() ); for ( size_t i = 0; i < specs.size(); i++ ) { BSONObj spec = specs[i]; if ( spec["unique"].trueValue() ) { status = checkUniqueIndexConstraints( ns.ns(), spec["key"].Obj() ); if ( !status.isOK() ) { appendCommandStatus( result, status ); return false; } } status = collection->getIndexCatalog()->createIndex(txn, spec, true); if ( status.code() == ErrorCodes::IndexAlreadyExists ) { if ( !result.hasField( "note" ) ) result.append( "note", "index already exists" ); continue; } if ( !status.isOK() ) { appendCommandStatus( result, status ); return false; } if ( !fromRepl ) { std::string systemIndexes = ns.getSystemIndexesCollection(); repl::logOp(txn, "i", systemIndexes.c_str(), spec); } } result.append( "numIndexesAfter", collection->getIndexCatalog()->numIndexesTotal() ); return true; }
void WriteBatchExecutor::execRemove( const BatchItemRef& removeItem, WriteErrorDetail** error ) { // Removes are similar to updates, but page faults are handled externally const BatchedCommandRequest& request = *removeItem.getRequest(); const NamespaceString nss( removeItem.getRequest()->getNS() ); // BEGIN CURRENT OP scoped_ptr<CurOp> currentOp( beginCurrentOp( _client, removeItem ) ); incOpStats( removeItem ); WriteOpResult result; while ( true ) { { // NOTE: Deletes will not fault outside the lock once any data has been written PageFaultRetryableSection pFaultSection; /////////////////////////////////////////// Lock::DBWrite writeLock( nss.ns() ); /////////////////////////////////////////// // Check version once we're locked if ( !checkShardVersion( &shardingState, request, &result.error ) ) { // Version error break; } // Context once we're locked, to set more details in currentOp() // TODO: better constructor? Client::Context writeContext( nss.ns(), storageGlobalParams.dbpath, false /* don't check version */); multiRemove( removeItem, &result ); if ( !result.fault ) { incWriteStats( removeItem, result.stats, result.error, currentOp.get() ); break; } } // // Check page fault out of lock // dassert( result.fault ); result.fault->touch(); result.reset(); } // END CURRENT OP finishCurrentOp( _client, currentOp.get(), result.error ); if ( result.error ) { result.error->setIndex( removeItem.getItemIndex() ); *error = result.releaseError(); } }
static int addTransaction(AB_BANKING *ab, GWEN_DB_NODE *dbArgs, int argc, char **argv) { GWEN_DB_NODE *db; int rv; AB_JOB_TYPE transferType; const char *ctxFile; const char *bankId; const char *accountId; const char *subAccountId; AB_IMEXPORTER_CONTEXT *ctx=0; AB_ACCOUNT_LIST2 *al; AB_ACCOUNT *a; AB_TRANSACTION *t; const GWEN_ARGS args[]={ { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "ctxFile", /* name */ 0, /* minnum */ 1, /* maxnum */ "c", /* short option */ "ctxfile", /* long option */ "Specify the file to store the context in", /* short description */ "Specify the file to store the context in" /* long description */ }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "bankId", /* name */ 0, /* minnum */ 1, /* maxnum */ "b", /* short option */ "bank", /* long option */ "overwrite the bank code", /* short description */ "overwrite the bank code" /* long description */ }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "accountId", /* name */ 0, /* minnum */ 1, /* maxnum */ "a", /* short option */ "account", /* long option */ "overwrite the account number", /* short description */ "overwrite the account number" /* long description */ }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "subAccountId", /* name */ 0, /* minnum */ 1, /* maxnum */ "aa", /* short option */ "subaccount", /* long option */ "Specify the sub account id (Unterkontomerkmal)", /* short description */ "Specify the sub account id (Unterkontomerkmal)" /* long description */ }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "remoteBankId", /* name */ 1, /* minnum */ 1, /* maxnum */ 0, /* short option */ "rbank", /* long option */ "Specify the remote bank code",/* short description */ "Specify the remote bank code" /* long description */ }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "remoteAccountId", /* name */ 1, /* minnum */ 1, /* maxnum */ 0, /* short option */ "raccount", /* long option */ "Specify the remote account number", /* short description */ "Specify the remote account number" /* long description */ }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "remoteIban", /* name */ 0, /* minnum */ 1, /* maxnum */ 0, /* short option */ "riban", /* long option */ "Specify the remote IBAN", /* short description */ "Specify the remote IBAN" /* long description */ }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "remoteBic", /* name */ 0, /* minnum */ 1, /* maxnum */ 0, /* short option */ "rbic", /* long option */ "Specify the remote BIC", /* short description */ "Specify the remote BIC" /* long description */ }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "value", /* name */ 1, /* minnum */ 1, /* maxnum */ "v", /* short option */ "value", /* long option */ "Specify the transfer amount", /* short description */ "Specify the transfer amount" /* long description */ }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Int, /* type */ "textkey", /* name */ 0, /* minnum */ 1, /* maxnum */ "t", /* short option */ "textkey", /* long option */ "Specify the text key (51 for normal transfer)", /* short description */ "Specify the text key (51 for normal transfer)" /* long description */ }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "remoteName", /* name */ 1, /* minnum */ 2, /* maxnum */ 0, /* short option */ "rname", /* long option */ "Specify the remote name", /* short description */ "Specify the remote name" /* long description */ }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "purpose", /* name */ 1, /* minnum */ 6, /* maxnum */ "p", /* short option */ "purpose", /* long option */ "Specify the purpose", /* short description */ "Specify the purpose" /* long description */ }, { GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ GWEN_ArgsType_Int, /* type */ "help", /* name */ 0, /* minnum */ 0, /* maxnum */ "h", /* short option */ "help", /* long option */ "Show this help screen", /* short description */ "Show this help screen" /* long description */ } }; db=GWEN_DB_GetGroup(dbArgs, GWEN_DB_FLAGS_DEFAULT, "local"); rv=GWEN_Args_Check(argc, argv, 1, 0 /*GWEN_ARGS_MODE_ALLOW_FREEPARAM*/, args, db); if (rv==GWEN_ARGS_RESULT_ERROR) { fprintf(stderr, "ERROR: Could not parse arguments\n"); return 1; } else if (rv==GWEN_ARGS_RESULT_HELP) { GWEN_BUFFER *ubuf; ubuf=GWEN_Buffer_new(0, 1024, 0, 1); if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) { fprintf(stderr, "ERROR: Could not create help string\n"); return 1; } fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf)); GWEN_Buffer_free(ubuf); return 0; } bankId=GWEN_DB_GetCharValue(db, "bankId", 0, 0); accountId=GWEN_DB_GetCharValue(db, "accountId", 0, 0); subAccountId=GWEN_DB_GetCharValue(db, "subAccountId", 0, 0); rv=AB_Banking_Init(ab); if (rv) { DBG_ERROR(0, "Error on init (%d)", rv); return 2; } rv=AB_Banking_OnlineInit(ab); if (rv) { DBG_ERROR(0, "Error on init (%d)", rv); return 2; } /* get account */ al=AB_Banking_FindAccounts(ab, "*", "*", bankId, accountId, subAccountId); if (al==NULL || AB_Account_List2_GetSize(al)==0) { DBG_ERROR(0, "Account not found"); AB_Account_List2_free(al); return 2; } else if (AB_Account_List2_GetSize(al)>1) { DBG_ERROR(0, "Ambiguous account specification"); AB_Account_List2_free(al); return 2; } a=AB_Account_List2_GetFront(al); AB_Account_List2_free(al); /* create transaction from arguments */ t=mkTransfer(a, db, &transferType); if (t==NULL) { DBG_ERROR(0, "Could not create transaction from arguments"); return 2; } ctxFile=GWEN_DB_GetCharValue(db, "ctxfile", 0, 0); rv=readContext(ctxFile, &ctx, 0); if (rv<0) { DBG_ERROR(0, "Error reading context (%d)", rv); AB_Transaction_free(t); return 4; } AB_ImExporterContext_AddTransaction(ctx, t); rv=writeContext(ctxFile, ctx); AB_ImExporterContext_free(ctx); if (rv<0) { DBG_ERROR(0, "Error writing context (%d)", rv); return 4; } /* that's it */ rv=AB_Banking_OnlineFini(ab); if (rv) { fprintf(stderr, "ERROR: Error on deinit (%d)\n", rv); AB_Banking_Fini(ab); return 5; } rv=AB_Banking_Fini(ab); if (rv) { fprintf(stderr, "ERROR: Error on deinit (%d)\n", rv); return 5; } return 0; }