diff options
Diffstat (limited to 'src/mongo/db/ops/modifier_interface.h')
-rw-r--r-- | src/mongo/db/ops/modifier_interface.h | 275 |
1 files changed, 141 insertions, 134 deletions
diff --git a/src/mongo/db/ops/modifier_interface.h b/src/mongo/db/ops/modifier_interface.h index 3574615e6f4..eba8b6c324d 100644 --- a/src/mongo/db/ops/modifier_interface.h +++ b/src/mongo/db/ops/modifier_interface.h @@ -36,160 +36,167 @@ namespace mongo { - class LogBuilder; +class LogBuilder; +/** + * Abstract base class for update "modifiers" (a.k.a "$ operators"). To create a new + * operator, implement a new derived class. + * + * A typical call sequence for the class is: + * + * + init() with the mod arguments + * + * + For each document that is being touched on that update, the following methods are + * going to be called once for that document and in the order the calls appear here. + * + * + prepare() to check if mod is viable over the document + * + * + apply(), effectively computing the update + * + * + log() registering the change in the log for replication purposes + * + * Again, a modifier implementation may rely on these last three calls being made and in + * that particular order and therefore can keep and reuse state between these calls, when + * appropriate. + * + * TODO: + * For a reference implementation, see modifier_identity.{h,cpp} used in tests. + */ +class ModifierInterface { +public: + virtual ~ModifierInterface() {} + + struct Options; /** - * Abstract base class for update "modifiers" (a.k.a "$ operators"). To create a new - * operator, implement a new derived class. + * Returns OK and extracts the parameters for this given mod from 'modExpr'. For + * instance, for a $inc, extracts the increment value. The init() method would be + * called only once per operand, that is, if a { $inc: { a: 1, b: 1 } } is issued, + * there would be one instance of the operator working on 'a' and one on 'b'. In each + * case, init() would be called once with the respective bson element. * - * A typical call sequence for the class is: + * If 'modExpr' is invalid, returns an error status with a reason description. * - * + init() with the mod arguments + * The optional bool out parameter 'positional', if provided, will be set to 'true' if + * the mod requires matched field details to be provided when calling 'prepare'. The + * field is optional since this is a hint to the caller about what work is needed to + * correctly invoke 'prepare'. It is always legal to provide any match details + * unconditionally. The value set in 'positional' if any, is only meaningful if 'init' + * returns an OK status. * - * + For each document that is being touched on that update, the following methods are - * going to be called once for that document and in the order the calls appear here. + * Note: * - * + prepare() to check if mod is viable over the document + * + An operator may assume the modExpr passed here will be unchanged throughout all + * the mod object lifetime and also that the modExrp's lifetime exceeds the life + * time of this mod. Therefore, taking references to elements inside modExpr is + * valid. + */ + virtual Status init(const BSONElement& modExpr, + const Options& opts, + bool* positional = NULL) = 0; + + /** + * Returns OK if it would be correct to apply this mod over the document 'root' (e.g, if + * we're $inc-ing a field, is that field numeric in the current doc?). * - * + apply(), effectively computing the update + * If the field this mod is targeted to contains a $-positional parameter, that value + * can be bound with 'matchedField', passed by the caller. * - * + log() registering the change in the log for replication purposes + * In addition, the call also identifies which fields(s) of 'root' the mod is interested + * in changing (note that the modifier may want to add a field that's not present in + * the document). The call also determines whether it could modify the document in + * place and whether it is a no-op for the given document. All this information is in + * the passed 'execInfo', which is filled inside the call. * - * Again, a modifier implementation may rely on these last three calls being made and in - * that particular order and therefore can keep and reuse state between these calls, when - * appropriate. + * If the mod cannot be applied over 'root', returns an error status with a reason + * description. * - * TODO: - * For a reference implementation, see modifier_identity.{h,cpp} used in tests. + * Note that you must provide a meaningful 'matchedField' here, unless 'init' set + * 'positional' to 'false', in which case you may pass an empty StringData object. */ - class ModifierInterface { - public: - virtual ~ModifierInterface() { } - - struct Options; - /** - * Returns OK and extracts the parameters for this given mod from 'modExpr'. For - * instance, for a $inc, extracts the increment value. The init() method would be - * called only once per operand, that is, if a { $inc: { a: 1, b: 1 } } is issued, - * there would be one instance of the operator working on 'a' and one on 'b'. In each - * case, init() would be called once with the respective bson element. - * - * If 'modExpr' is invalid, returns an error status with a reason description. - * - * The optional bool out parameter 'positional', if provided, will be set to 'true' if - * the mod requires matched field details to be provided when calling 'prepare'. The - * field is optional since this is a hint to the caller about what work is needed to - * correctly invoke 'prepare'. It is always legal to provide any match details - * unconditionally. The value set in 'positional' if any, is only meaningful if 'init' - * returns an OK status. - * - * Note: - * - * + An operator may assume the modExpr passed here will be unchanged throughout all - * the mod object lifetime and also that the modExrp's lifetime exceeds the life - * time of this mod. Therefore, taking references to elements inside modExpr is - * valid. - */ - virtual Status init(const BSONElement& modExpr, const Options& opts, - bool* positional = NULL) = 0; - - /** - * Returns OK if it would be correct to apply this mod over the document 'root' (e.g, if - * we're $inc-ing a field, is that field numeric in the current doc?). - * - * If the field this mod is targeted to contains a $-positional parameter, that value - * can be bound with 'matchedField', passed by the caller. - * - * In addition, the call also identifies which fields(s) of 'root' the mod is interested - * in changing (note that the modifier may want to add a field that's not present in - * the document). The call also determines whether it could modify the document in - * place and whether it is a no-op for the given document. All this information is in - * the passed 'execInfo', which is filled inside the call. - * - * If the mod cannot be applied over 'root', returns an error status with a reason - * description. - * - * Note that you must provide a meaningful 'matchedField' here, unless 'init' set - * 'positional' to 'false', in which case you may pass an empty StringData object. - */ - struct ExecInfo; - virtual Status prepare(mutablebson::Element root, - StringData matchedField, - /* IN-OUT */ ExecInfo* execInfo) = 0; - - /** - * Returns OK and modifies (or adds) an element (or elements) from the 'root' passed on - * the prepareMod call. This may act on multiple fields but should only be called once - * per operator. - * - * For this call to be issued, the call to 'prepareElem' must have necessarily turned - * off 'ExecInfo.noOp', ie this mod over this document is not a no-op. - * - * If the mod could not be applied, returns an error status with a reason description. - */ - virtual Status apply() const = 0 ; - - /** - * Returns OK and records the result of this mod in the provided LogBuilder. The mod - * must have kept enough state to be able to produce the log record (see idempotency - * note below). This call may be issued even if apply() was not. - * - * If the mod could not be logged, returns an error status with a reason description. - * - * Idempotency Note: - * - * + The modifier must log a mod that is idempotent, ie, applying it more than once - * to a base collection would produce the same result as applying it only once. For - * example, a $inc can be switched to a $set for the resulting incremented value, - * for logging purposes. An array based operator may check the contents of the - * array before operating on it. - */ - virtual Status log(LogBuilder* logBuilder) const = 0; - }; + struct ExecInfo; + virtual Status prepare(mutablebson::Element root, + StringData matchedField, + /* IN-OUT */ ExecInfo* execInfo) = 0; /** - * Options used to control Modifier behavior + * Returns OK and modifies (or adds) an element (or elements) from the 'root' passed on + * the prepareMod call. This may act on multiple fields but should only be called once + * per operator. + * + * For this call to be issued, the call to 'prepareElem' must have necessarily turned + * off 'ExecInfo.noOp', ie this mod over this document is not a no-op. + * + * If the mod could not be applied, returns an error status with a reason description. */ - struct ModifierInterface::Options { - Options() : fromReplication(false), enforceOkForStorage(true) {} - Options(bool repl, bool ofs) : fromReplication(repl), enforceOkForStorage(ofs) {} + virtual Status apply() const = 0; - static Options normal() { return Options(false, true); } - static Options fromRepl() { return Options(true, false); } - static Options unchecked() { return Options(false, false); } - - bool fromReplication; - bool enforceOkForStorage; - }; + /** + * Returns OK and records the result of this mod in the provided LogBuilder. The mod + * must have kept enough state to be able to produce the log record (see idempotency + * note below). This call may be issued even if apply() was not. + * + * If the mod could not be logged, returns an error status with a reason description. + * + * Idempotency Note: + * + * + The modifier must log a mod that is idempotent, ie, applying it more than once + * to a base collection would produce the same result as applying it only once. For + * example, a $inc can be switched to a $set for the resulting incremented value, + * for logging purposes. An array based operator may check the contents of the + * array before operating on it. + */ + virtual Status log(LogBuilder* logBuilder) const = 0; +}; - struct ModifierInterface::ExecInfo { - static const int MAX_NUM_FIELDS = 2; +/** + * Options used to control Modifier behavior + */ +struct ModifierInterface::Options { + Options() : fromReplication(false), enforceOkForStorage(true) {} + Options(bool repl, bool ofs) : fromReplication(repl), enforceOkForStorage(ofs) {} + + static Options normal() { + return Options(false, true); + } + static Options fromRepl() { + return Options(true, false); + } + static Options unchecked() { + return Options(false, false); + } + + bool fromReplication; + bool enforceOkForStorage; +}; + +struct ModifierInterface::ExecInfo { + static const int MAX_NUM_FIELDS = 2; - /** - * An update mod may specify that it wishes to the applied only if the context - * of the update turns out a certain way. - */ - enum UpdateContext { - // This mod wants to be applied only if the update turns out to be an insert. - INSERT_CONTEXT, + /** + * An update mod may specify that it wishes to the applied only if the context + * of the update turns out a certain way. + */ + enum UpdateContext { + // This mod wants to be applied only if the update turns out to be an insert. + INSERT_CONTEXT, - // This mod wants to be applied only if the update is not an insert. - UPDATE_CONTEXT, + // This mod wants to be applied only if the update is not an insert. + UPDATE_CONTEXT, - // This mod doesn't care if the update will be an update or an upsert. - ANY_CONTEXT - }; + // This mod doesn't care if the update will be an update or an upsert. + ANY_CONTEXT + }; - ExecInfo() : noOp(false), context(ANY_CONTEXT) { - for (int i = 0; i < MAX_NUM_FIELDS; i++) { - fieldRef[i] = NULL; - } + ExecInfo() : noOp(false), context(ANY_CONTEXT) { + for (int i = 0; i < MAX_NUM_FIELDS; i++) { + fieldRef[i] = NULL; } + } - // The fields of concern to the driver: no other op may modify the fields listed here. - FieldRef* fieldRef[MAX_NUM_FIELDS]; // not owned here - bool noOp; - UpdateContext context; - }; + // The fields of concern to the driver: no other op may modify the fields listed here. + FieldRef* fieldRef[MAX_NUM_FIELDS]; // not owned here + bool noOp; + UpdateContext context; +}; -} // namespace mongo +} // namespace mongo |