text: 'use strict';
  
  // This mixin contains the shared code for all modify operations (update, delete, relate, unrelate)
  // for ManyToManyRelation operations.
  //
  // The most important thing this mixin does is that it moves the filters from the main query
  // into a subquery and then adds a single where clause that uses the subquery. This is done so
  // that we are able to `innerJoin` the join table to the query. Most SQL engines don't allow
  // joins in updates or deletes. Join table is joined so that queries can reference the join
  // table columns.
  const ManyToManyModifyMixin = Operation => {
    return class extends Operation {
      constructor(...args) {
        super(...args);
        this.modifyFilterSubquery = null;
      }
  
      get modifyMainQuery() {
        return true;
      }
  
      // At this point `builder` should only have the user's own wheres and joins. There can
      // be other operations (like orderBy) too, but those are meaningless with modify operations.
      onBuild(builder) {
        this.modifyFilterSubquery = this.createModifyFilterSubquery(builder);
  
        if (this.modifyMainQuery) {
          // We can now remove the where and join statements from the main query.
          this.removeFiltersFromMainQuery(builder);
  
          // Add a single where clause that uses the created subquery.
          this.applyModifyFilterForRelatedTable(builder);
        }
  
        return super.onBuild(builder);
      }
  
      createModifyFilterSubquery(builder) {
        const relatedModelClass = this.relation.relatedModelClass;
        const builderClass = builder.constructor;
        const ownerProp = this.relation.ownerProp;
        const ownerIds = [ownerProp.getProps(this.owner)];
  
        // Create an empty subquery.
        const modifyFilterSubquery = relatedModelClass.query().childQueryOf(builder);
  
        // Add the necessary joins and wheres so that only rows related to
        // `this.owner` are selected.
        this.relation.findQuery(modifyFilterSubquery, { ownerIds });
  
        // Copy all where and join statements from the main query to the subquery.
        modifyFilterSubquery
          .copyFrom(builder, builderClass.WhereSelector)
          .copyFrom(builder, builderClass.JoinSelector);
  
        return modifyFilterSubquery.clearSelect();
      }
  
      removeFiltersFromMainQuery(builder) {
        const builderClass = builder.constructor;
  
        builder.clear(builderClass.WhereSelector);
        builder.clear(builderClass.JoinSelector);
      }
  
      applyModifyFilterForRelatedTable(builder) {
        const idRefs = this.relation.relatedModelClass.getIdRelationProperty().refs(builder);
        const subquery = this.modifyFilterSubquery.clone().select(idRefs);
  
        return builder.whereInComposite(idRefs, subquery);
      }
  
      applyModifyFilterForJoinTable(builder) {
        const joinTableOwnerRefs = this.relation.joinTableOwnerProp.refs(builder);
        const joinTableRelatedRefs = this.relation.joinTableRelatedProp.refs(builder);
  
        const relatedRefs = this.relation.relatedProp.refs(builder);
        const ownerIds = this.relation.ownerProp.getProps(this.owner);
  
        const subquery = this.modifyFilterSubquery.clone().select(relatedRefs);
  
        return builder
          .whereInComposite(joinTableRelatedRefs, subquery)
          .whereComposite(joinTableOwnerRefs, ownerIds);
      }
  
      clone() {
        const clone = super.clone();
        clone.modifyFilterSubquery = this.modifyFilterSubquery;
        return clone;
      }
    };
  };
  
  module.exports = {
    ManyToManyModifyMixin
  };
  