/**
   * Method that will unload all fields that are not in the FetchPlan. This is typically for use
   * when the instance is being refreshed.
   */
  public void unloadNonFetchPlanFields() {
    int[] fpFieldNumbers = myFP.getMemberNumbers();
    int[] nonfpFieldNumbers = null;
    if (fpFieldNumbers == null || fpFieldNumbers.length == 0) {
      nonfpFieldNumbers = cmd.getAllMemberPositions();
    } else {
      int fieldCount = cmd.getMemberCount();
      if (fieldCount == fpFieldNumbers.length) {
        // No fields that arent in FetchPlan
        return;
      }

      nonfpFieldNumbers = new int[fieldCount - fpFieldNumbers.length];
      int currentFPFieldIndex = 0;
      int j = 0;
      for (int i = 0; i < fieldCount; i++) {
        if (currentFPFieldIndex >= fpFieldNumbers.length) {
          // Past end of FetchPlan fields
          nonfpFieldNumbers[j++] = i;
        } else {
          if (fpFieldNumbers[currentFPFieldIndex] == i) {
            // FetchPlan field so move to next
            currentFPFieldIndex++;
          } else {
            nonfpFieldNumbers[j++] = i;
          }
        }
      }
    }

    // Mark all non-FetchPlan fields as unloaded
    for (int i = 0; i < nonfpFieldNumbers.length; i++) {
      loadedFields[nonfpFieldNumbers[i]] = false;
    }
  }
  /**
   * Method to load all unloaded fields in the FetchPlan. Recurses through the FetchPlan objects and
   * loads fields of sub-objects where needed. Used as a precursor to detaching objects at commit
   * since fields can't be loaded during the postCommit phase when the detach actually happens.
   *
   * @param state The FetchPlan state
   */
  public void loadFieldsInFetchPlan(FetchPlanState state) {
    if ((flags & FLAG_LOADINGFPFIELDS) != 0) {
      // Already in the process of loading fields in this class so skip
      return;
    }

    flags |= FLAG_LOADINGFPFIELDS;
    try {
      // Load unloaded FetchPlan fields of this object
      loadUnloadedFieldsInFetchPlan();

      // Recurse through all fields and do the same
      int[] fieldNumbers =
          ClassUtils.getFlagsSetTo(loadedFields, cmd.getAllMemberPositions(), true);
      if (fieldNumbers != null && fieldNumbers.length > 0) {
        // TODO Fix this to just access the fields of the FieldManager yet this actually does a
        // replaceField
        replaceFields(
            fieldNumbers, new LoadFieldManager(this, cmd.getSCOMutableMemberFlags(), myFP, state));
        updateLevel2CacheForFields(fieldNumbers);
      }
    } finally {
      flags &= ~FLAG_LOADINGFPFIELDS;
    }
  }
 /**
  * This method should be called only if it has been determined that field schema should be created
  * (or deleted). Create the {@link CustomField} for the metadata API to use during deploy
  *
  * @param cmd the class metadata for the entity
  * @param storeManager the store manager
  */
 public void createCustomFields(AbstractClassMetaData cmd, ForceStoreManager storeManager) {
   synchronized (cmd) {
     try {
       if (cmd.isEmbeddedOnly() || PersistenceUtils.isReadOnlySchema(cmd, false)) return;
       ForceManagedConnection mconn = storeManager.createConnection();
       try {
         ForceColumnMetaData cols = new ForceColumnMetaData(cmd, tableImpl, storeManager);
         cols.createFieldSchema(mconn.getNamespace());
         if (customObject == null) {
           createCustomObjectStub();
         }
         cols.addFieldsToObject(customObject);
       } catch (NucleusException ce) {
         throw ce;
       } catch (Exception e) {
         throw new NucleusException(e.getMessage(), e);
       } finally {
         mconn.close();
       }
     } catch (Exception e) {
       throw new NucleusUserException(
           "Exception during initialisation of metadata for " + cmd.getFullClassName(), e);
     }
   }
 }
 /** Convenience method to mark PK fields as loaded (if using app id). */
 protected void markPKFieldsAsLoaded() {
   if (cmd.getIdentityType() == IdentityType.APPLICATION) {
     int[] pkPositions = cmd.getPKMemberPositions();
     for (int i = 0; i < pkPositions.length; i++) {
       loadedFields[pkPositions[i]] = true;
     }
   }
 }
  /**
   * Convenience method to find the element information relating to the element type. Used
   * specifically for the "element-type" of a collection/array to find the elements which have table
   * information.
   *
   * @return Element information relating to the element type
   */
  protected ElementInfo[] getElementInformationForClass() {
    ElementInfo[] info = null;

    DatastoreClass rootTbl;
    String[] clsNames;
    if (clr.classForName(elementType).isInterface()) {
      // Collection<interface>, so find implementations of the interface
      clsNames =
          storeMgr
              .getNucleusContext()
              .getMetaDataManager()
              .getClassesImplementingInterface(elementType, clr);
      rootTbl = null;
    } else {
      clsNames = new String[] {elementType};
      rootTbl = storeMgr.getDatastoreClass(elementType, clr);
    }

    if (rootTbl == null) {
      if (clr.classForName(elementType).isInterface()) {
        info = new ElementInfo[clsNames.length];
        for (int i = 0; i < clsNames.length; i++) {
          AbstractClassMetaData implCmd =
              storeMgr.getMetaDataManager().getMetaDataForClass(clsNames[i], clr);
          DatastoreClass table = storeMgr.getDatastoreClass(clsNames[i], clr);
          info[i] = new ElementInfo(implCmd, table);
        }
      } else {
        AbstractClassMetaData[] subclassCmds = storeMgr.getClassesManagingTableForClass(emd, clr);
        info = new ElementInfo[subclassCmds.length];
        for (int i = 0; i < subclassCmds.length; i++) {
          DatastoreClass table =
              storeMgr.getDatastoreClass(subclassCmds[i].getFullClassName(), clr);
          info[i] = new ElementInfo(subclassCmds[i], table);
        }
      }
    } else {
      info = new ElementInfo[clsNames.length];
      for (int i = 0; i < clsNames.length; i++) {
        AbstractClassMetaData cmd =
            storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForClass(clsNames[i], clr);
        DatastoreClass table = storeMgr.getDatastoreClass(cmd.getFullClassName(), clr);
        info[i] = new ElementInfo(cmd, table);
      }
    }

    return info;
  }
 // TODO Remove this when we support subclasses from a query
 @Override
 public Extent getExtent(ExecutionContext ec, Class c, boolean subclasses) {
   AbstractClassMetaData cmd =
       getMetaDataManager().getMetaDataForClass(c, ec.getClassLoaderResolver());
   validateMetaDataForClass(cmd);
   if (!cmd.isRequiresExtent()) {
     throw new NoExtentException(c.getName());
   }
   if (!getBooleanProperty(GET_EXTENT_CAN_RETURN_SUBCLASSES_PROPERTY, false)) {
     subclasses = false;
   }
   // In order to avoid breaking existing apps I'm hard-coding subclasses to
   // be false.  This breaks spec compliance since the no-arg overload of
   // PersistenceManager.getExtent() is supposed to return subclasses.
   return new DefaultCandidateExtent(ec, c, subclasses, cmd);
 }
  /**
   * Perform appengine-specific validation on the provided meta data. Also generates cached
   * information that is needed by persistence.
   *
   * @param cmd The metadata to validate.
   */
  public void validateMetaDataForClass(AbstractClassMetaData cmd) {
    // Only validate each meta data once
    if (validatedClasses.add(cmd.getFullClassName())) {
      if (getBooleanProperty(VALIDATE_METADATA, true)) {
        // Only do if the persistence property is not set to false
        metadataValidator.validate(cmd);
      }

      AbstractMemberMetaData parentPkMmd =
          MetaDataUtils.getParentPkMemberMetaDataForClass(
              cmd,
              getMetaDataManager(),
              getNucleusContext().getClassLoaderResolver(cmd.getClass().getClassLoader()));
      if (parentPkMmd != null) {
        parentMemberMetaDataByClass.put(cmd.getFullClassName(), parentPkMmd);
      }
    }
  }
 /**
  * Convenience accessor for the names of the fields that are loaded.
  *
  * @return Names of the loaded fields
  */
 public String[] getLoadedFieldNames() {
   int[] loadedFieldNumbers = ClassUtils.getFlagsSetTo(loadedFields, true);
   if (loadedFieldNumbers != null && loadedFieldNumbers.length > 0) {
     String[] loadedFieldNames = new String[loadedFieldNumbers.length];
     for (int i = 0; i < loadedFieldNumbers.length; i++) {
       loadedFieldNames[i] =
           cmd.getMetaDataForManagedMemberAtAbsolutePosition(loadedFieldNumbers[i]).getName();
     }
     return loadedFieldNames;
   }
   return null;
 }
 @Override
 public FieldManager getFieldManagerForResultProcessing(
     ExecutionContext ec,
     Object resultSet,
     StatementClassMapping resultMappings,
     AbstractClassMetaData cmd) {
   Class<?> cls = ec.getClassLoaderResolver().classForName(cmd.getFullClassName());
   Object internalKey = EntityUtils.idToInternalKey(ec, cls, resultSet, true);
   // Need to provide this to the field manager in the form of the pk
   // of the type: Key, Long, encoded String, or unencoded String
   return new KeyOnlyFieldManager(internalKey);
 }
 /**
  * Convenience accessor for the names of the fields that are dirty.
  *
  * @return Names of the dirty fields
  */
 public String[] getDirtyFieldNames() {
   int[] dirtyFieldNumbers = ClassUtils.getFlagsSetTo(dirtyFields, true);
   if (dirtyFieldNumbers != null && dirtyFieldNumbers.length > 0) {
     String[] dirtyFieldNames = new String[dirtyFieldNumbers.length];
     for (int i = 0; i < dirtyFieldNumbers.length; i++) {
       dirtyFieldNames[i] =
           cmd.getMetaDataForManagedMemberAtAbsolutePosition(dirtyFieldNumbers[i]).getName();
     }
     return dirtyFieldNames;
   }
   return null;
 }
  /* (non-Javadoc)
   * @see org.datanucleus.store.fieldmanager.AbstractFieldManager#fetchObjectField(int)
   */
  @Override
  public Object fetchObjectField(int fieldNumber) {
    ClassLoaderResolver clr = ec.getClassLoaderResolver();
    AbstractMemberMetaData mmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
    RelationType relationType = mmd.getRelationType(clr);
    EmbeddedMetaData embmd = mmds.get(0).getEmbeddedMetaData();
    if (mmds.size() == 1
        && embmd != null
        && embmd.getOwnerMember() != null
        && embmd.getOwnerMember().equals(mmd.getName())) {
      // Special case of this being a link back to the owner. TODO Repeat this for nested and their
      // owners
      ObjectProvider[] ownerOps = ec.getOwnersForEmbeddedObjectProvider(op);
      return (ownerOps != null && ownerOps.length > 0 ? ownerOps[0].getObject() : null);
    }

    if (relationType != RelationType.NONE
        && MetaDataUtils.getInstance()
            .isMemberEmbedded(ec.getMetaDataManager(), clr, mmd, relationType, null)) {
      // Embedded field
      if (RelationType.isRelationSingleValued(relationType)) {
        // TODO Cater for null value detection

        List<AbstractMemberMetaData> embMmds = new ArrayList<AbstractMemberMetaData>(mmds);
        embMmds.add(mmd);

        AbstractClassMetaData embCmd =
            ec.getMetaDataManager().getMetaDataForClass(mmd.getType(), clr);
        ObjectProvider embOP =
            ec.getNucleusContext()
                .getObjectProviderFactory()
                .newForEmbedded(ec, embCmd, op, fieldNumber);
        FieldManager ffm = new FetchEmbeddedFieldManager(embOP, result, embMmds, table);
        embOP.replaceFields(embCmd.getAllMemberPositions(), ffm);
        return embOP.getObject();
      }
    }

    return fetchNonEmbeddedObjectField(mmd, relationType, clr);
  }
 private static void addColumnsToScanForEmbeddedMember(
     Scan scan, List<AbstractMemberMetaData> embMmds, Table table, ExecutionContext ec) {
   AbstractMemberMetaData lastMmd = embMmds.get(embMmds.size() - 1);
   ClassLoaderResolver clr = ec.getClassLoaderResolver();
   AbstractClassMetaData embCmd =
       ec.getMetaDataManager().getMetaDataForClass(lastMmd.getTypeName(), clr);
   int[] embMmdPosns = embCmd.getAllMemberPositions();
   for (int i = 0; i < embMmdPosns.length; i++) {
     AbstractMemberMetaData embMmd = embCmd.getMetaDataForManagedMemberAtAbsolutePosition(i);
     List<AbstractMemberMetaData> subEmbMmds = new ArrayList<AbstractMemberMetaData>(embMmds);
     subEmbMmds.add(embMmd);
     RelationType relationType = embMmd.getRelationType(clr);
     MemberColumnMapping mapping = table.getMemberColumnMappingForEmbeddedMember(subEmbMmds);
     if (RelationType.isRelationSingleValued(relationType)) {
       addColumnsToScanForEmbeddedMember(scan, subEmbMmds, table, ec);
     } else {
       String familyName = HBaseUtils.getFamilyNameForColumn(mapping.getColumn(0));
       String qualifName = HBaseUtils.getQualifierNameForColumn(mapping.getColumn(0));
       scan.addColumn(familyName.getBytes(), qualifName.getBytes());
     }
   }
 }
 /** Method to (re)connect the provider to the specified ExecutionContext and object type. */
 public void connect(ExecutionContext ec, AbstractClassMetaData cmd) {
   int fieldCount = cmd.getMemberCount();
   this.cmd = cmd;
   this.dirtyFields = new boolean[fieldCount];
   this.loadedFields = new boolean[fieldCount];
   this.dirty = false;
   this.myEC = ec;
   this.myFP = myEC.getFetchPlan().getFetchPlanForClass(cmd);
   this.lock = new ReentrantLock();
   this.lockMode = LockManager.LOCK_MODE_NONE;
   this.savedFlags = 0;
   this.savedLoadedFields = null;
   this.objectType = 0;
   this.activity = ActivityState.NONE;
   this.myVersion = null;
   this.transactionalVersion = null;
   this.persistenceFlags = 0;
 }
  /**
   * Convenience accessor to return the field numbers for the input loaded and dirty field arrays.
   *
   * @param loadedFields Fields that were detached with the object
   * @param dirtyFields Fields that have been modified while detached
   * @return The field numbers of loaded or dirty fields
   */
  protected int[] getFieldNumbersOfLoadedOrDirtyFields(
      boolean[] loadedFields, boolean[] dirtyFields) {
    // Find the number of fields that are loaded or dirty
    int numFields = 0;
    for (int i = 0; i < loadedFields.length; i++) {
      if (loadedFields[i] || dirtyFields[i]) {
        numFields++;
      }
    }

    int[] fieldNumbers = new int[numFields];
    int n = 0;
    int[] allFieldNumbers = cmd.getAllMemberPositions();
    for (int i = 0; i < loadedFields.length; i++) {
      if (loadedFields[i] || dirtyFields[i]) {
        fieldNumbers[n++] = allFieldNumbers[i];
      }
    }
    return fieldNumbers;
  }
  /**
   * Method to return an expression for Map.containsValue using INNER JOIN to the element. This is
   * only for use when there are no "!containsValue" and no "OR" operations. Creates SQL by adding
   * INNER JOIN to the join table (where it exists), and also to the value table adding an AND
   * condition on the value (with value of the valueExpr). Returns a BooleanExpression "TRUE" (since
   * the INNER JOIN will guarantee if the value is contained of not).
   *
   * @param mapExpr Map expression
   * @param valExpr Expression for the value
   * @return Contains expression
   */
  protected SQLExpression containsAsInnerJoin(MapExpression mapExpr, SQLExpression valExpr) {
    boolean valIsUnbound = (valExpr instanceof UnboundExpression);
    String varName = null;
    String valAlias = null;
    if (valIsUnbound) {
      varName = ((UnboundExpression) valExpr).getVariableName();
      NucleusLogger.QUERY.debug(
          "map.containsValue("
              + valExpr
              + ") binding unbound variable "
              + varName
              + " using INNER JOIN");
      // TODO What if the variable is declared as a subtype, handle this see
      // CollectionContainsMethod
    } else if (!stmt.getQueryGenerator().hasExplicitJoins()) {
      JoinType joinType = stmt.getJoinTypeForTable(valExpr.getSQLTable());
      if (joinType == JoinType.CROSS_JOIN) {
        // Value is currently joined via CROSS JOIN, so remove it (and use INNER JOIN below)
        valAlias = stmt.removeCrossJoin(valExpr.getSQLTable());
        valIsUnbound = true;
        NucleusLogger.QUERY.debug(
            "map.containsValue("
                + valExpr
                + ") was previously bound as CROSS JOIN but changing to INNER JOIN");
      }

      // TODO If owner is joined via CROSS JOIN and value is already present then remove CROSS JOIN
      // and join via INNER JOIN
    }

    RDBMSStoreManager storeMgr = stmt.getRDBMSManager();
    MetaDataManager mmgr = storeMgr.getMetaDataManager();
    AbstractMemberMetaData mmd = mapExpr.getJavaTypeMapping().getMemberMetaData();
    AbstractClassMetaData valCmd = mmd.getMap().getValueClassMetaData(clr, mmgr);
    if (mmd.getMap().getMapType() == MapType.MAP_TYPE_JOIN) {
      // Map formed in join table - add join to join table, then to value table (if present)
      MapTable mapTbl = (MapTable) storeMgr.getTable(mmd);
      SQLTable joinSqlTbl =
          stmt.innerJoin(
              mapExpr.getSQLTable(),
              mapExpr.getSQLTable().getTable().getIdMapping(),
              mapTbl,
              null,
              mapTbl.getOwnerMapping(),
              null,
              null);
      if (valCmd != null) {
        if (valIsUnbound) {
          DatastoreClass valTbl = storeMgr.getDatastoreClass(valCmd.getFullClassName(), clr);
          SQLTable valSqlTbl =
              stmt.innerJoin(
                  joinSqlTbl,
                  mapTbl.getValueMapping(),
                  valTbl,
                  valAlias,
                  valTbl.getIdMapping(),
                  null,
                  null);

          // Bind the variable in the QueryGenerator
          valExpr = exprFactory.newExpression(stmt, valSqlTbl, valSqlTbl.getTable().getIdMapping());
          stmt.getQueryGenerator()
              .bindVariable(varName, valCmd, valExpr.getSQLTable(), valExpr.getJavaTypeMapping());
        } else {
          // Add restrict to value
          SQLExpression valIdExpr =
              exprFactory.newExpression(stmt, joinSqlTbl, mapTbl.getValueMapping());
          stmt.whereAnd(valIdExpr.eq(valExpr), true);
        }
      } else {
        if (valIsUnbound) {
          // Bind the variable in the QueryGenerator
          valExpr = exprFactory.newExpression(stmt, joinSqlTbl, mapTbl.getValueMapping());
          stmt.getQueryGenerator()
              .bindVariable(varName, null, valExpr.getSQLTable(), valExpr.getJavaTypeMapping());
        } else {
          // Add restrict to value
          SQLExpression valIdExpr =
              exprFactory.newExpression(stmt, joinSqlTbl, mapTbl.getValueMapping());
          stmt.whereAnd(valIdExpr.eq(valExpr), true);
        }
      }
    } else if (mmd.getMap().getMapType() == MapType.MAP_TYPE_KEY_IN_VALUE) {
      // Map formed in value table - add join to value table
      DatastoreClass valTbl = storeMgr.getDatastoreClass(valCmd.getFullClassName(), clr);
      JavaTypeMapping ownerMapping = null;
      if (mmd.getMappedBy() != null) {
        ownerMapping = valTbl.getMemberMapping(valCmd.getMetaDataForMember(mmd.getMappedBy()));
      } else {
        ownerMapping = valTbl.getExternalMapping(mmd, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK);
      }
      SQLTable valSqlTbl =
          stmt.innerJoin(
              mapExpr.getSQLTable(),
              mapExpr.getSQLTable().getTable().getIdMapping(),
              valTbl,
              valAlias,
              ownerMapping,
              null,
              null);

      if (valIsUnbound) {
        // Bind the variable in the QueryGenerator
        valExpr = exprFactory.newExpression(stmt, valSqlTbl, valTbl.getIdMapping());
        stmt.getQueryGenerator()
            .bindVariable(varName, valCmd, valExpr.getSQLTable(), valExpr.getJavaTypeMapping());
      } else {
        // Add restrict to value
        SQLExpression valIdExpr = exprFactory.newExpression(stmt, valSqlTbl, valTbl.getIdMapping());
        stmt.whereAnd(valIdExpr.eq(valExpr), true);
      }
    } else if (mmd.getMap().getMapType() == MapType.MAP_TYPE_VALUE_IN_KEY) {
      // Map formed in key table - add join to key table then to value table
      AbstractClassMetaData keyCmd = mmd.getMap().getKeyClassMetaData(clr, mmgr);
      DatastoreClass keyTbl = storeMgr.getDatastoreClass(keyCmd.getFullClassName(), clr);
      AbstractMemberMetaData keyValMmd =
          keyCmd.getMetaDataForMember(mmd.getValueMetaData().getMappedBy());
      JavaTypeMapping ownerMapping = null;
      if (mmd.getMappedBy() != null) {
        ownerMapping = keyTbl.getMemberMapping(keyCmd.getMetaDataForMember(mmd.getMappedBy()));
      } else {
        ownerMapping = keyTbl.getExternalMapping(mmd, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK);
      }
      SQLTable keySqlTbl =
          stmt.innerJoin(
              mapExpr.getSQLTable(),
              mapExpr.getSQLTable().getTable().getIdMapping(),
              keyTbl,
              null,
              ownerMapping,
              null,
              null);

      if (valCmd != null) {
        DatastoreClass valTbl = storeMgr.getDatastoreClass(valCmd.getFullClassName(), clr);
        SQLTable valSqlTbl =
            stmt.innerJoin(
                keySqlTbl,
                keyTbl.getMemberMapping(keyValMmd),
                valTbl,
                valAlias,
                valTbl.getIdMapping(),
                null,
                null);

        if (valIsUnbound) {
          // Bind the variable in the QueryGenerator
          valExpr = exprFactory.newExpression(stmt, valSqlTbl, valTbl.getIdMapping());
          stmt.getQueryGenerator()
              .bindVariable(varName, valCmd, valExpr.getSQLTable(), valExpr.getJavaTypeMapping());
        } else {
          // Add restrict to value
          SQLExpression valIdExpr =
              exprFactory.newExpression(stmt, valSqlTbl, valTbl.getIdMapping());
          stmt.whereAnd(valIdExpr.eq(valExpr), true);
        }
      } else {
        if (valIsUnbound) {
          // Bind the variable in the QueryGenerator
          valExpr = exprFactory.newExpression(stmt, keySqlTbl, keyTbl.getMemberMapping(keyValMmd));
          stmt.getQueryGenerator()
              .bindVariable(varName, null, valExpr.getSQLTable(), valExpr.getJavaTypeMapping());
        } else {
          // Add restrict to value
          SQLExpression valIdExpr =
              exprFactory.newExpression(stmt, keySqlTbl, keyTbl.getMemberMapping(keyValMmd));
          stmt.whereAnd(valIdExpr.eq(valExpr), true);
        }
      }
    }

    JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
    return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, true));
  }
 /**
  * Accessor for the metadata of the member of this class marked as "gae.parent-pk".
  *
  * @param cmd Metadata for the class
  * @return The member marked as "gae.parent-pk".
  */
 public AbstractMemberMetaData getMetaDataForParentPK(AbstractClassMetaData cmd) {
   return parentMemberMetaDataByClass.get(cmd.getFullClassName());
 }
  /** Method called at the end of the class. */
  public void visitEnd() {
    AbstractClassMetaData cmd = enhancer.getClassMetaData();
    if (cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE) {
      // Add any new fields
      List fields = enhancer.getFieldsList();
      Iterator fieldsIter = fields.iterator();
      while (fieldsIter.hasNext()) {
        ClassField field = (ClassField) fieldsIter.next();
        if (field.getName().equals(enhancer.getNamer().getDetachedStateFieldName())
            && hasDetachedState) {
          // No need to add this field since exists
          continue;
        }

        if (DataNucleusEnhancer.LOGGER.isDebugEnabled()) {
          DataNucleusEnhancer.LOGGER.debug(
              Localiser.msg("005021", ((Class) field.getType()).getName() + " " + field.getName()));
        }
        cv.visitField(
            field.getAccess(),
            field.getName(),
            Type.getDescriptor((Class) field.getType()),
            null,
            null);
      }

      if (!hasStaticInitialisation) {
        // Add a static initialisation block for the class since nothing added yet
        InitClass method = InitClass.getInstance(enhancer);
        method.initialise(cv);
        method.execute();
        method.close();
      }

      if (!hasDefaultConstructor
          && enhancer.hasOption(ClassEnhancer.OPTION_GENERATE_DEFAULT_CONSTRUCTOR)) {
        // Add a default constructor
        DefaultConstructor ctr = DefaultConstructor.getInstance(enhancer);
        ctr.initialise(cv);
        ctr.execute();
        ctr.close();
      }

      // Add any new methods
      List methods = enhancer.getMethodsList();
      Iterator<ClassMethod> methodsIter = methods.iterator();
      while (methodsIter.hasNext()) {
        ClassMethod method = methodsIter.next();
        method.initialise(cv);
        method.execute();
        method.close();
      }

      if (Serializable.class.isAssignableFrom(enhancer.getClassBeingEnhanced())) {
        // Class is Serializable
        if (!hasSerialVersionUID) {
          // Needs "serialVersionUID" field
          Long uid = null;
          try {
            uid =
                (Long)
                    AccessController.doPrivileged(
                        new PrivilegedAction() {
                          public Object run() {
                            return Long.valueOf(
                                ObjectStreamClass.lookup(enhancer.getClassBeingEnhanced())
                                    .getSerialVersionUID());
                          }
                        });
          } catch (Throwable e) {
            DataNucleusEnhancer.LOGGER.warn(StringUtils.getStringFromStackTrace(e));
          }
          ClassField cf =
              new ClassField(
                  enhancer,
                  enhancer.getNamer().getSerialVersionUidFieldName(),
                  Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL,
                  long.class,
                  uid);
          if (DataNucleusEnhancer.LOGGER.isDebugEnabled()) {
            DataNucleusEnhancer.LOGGER.debug(
                Localiser.msg("005021", ((Class) cf.getType()).getName() + " " + cf.getName()));
          }
          cv.visitField(
              cf.getAccess(),
              cf.getName(),
              Type.getDescriptor((Class) cf.getType()),
              null,
              cf.getInitialValue());
        }

        // The dnPreSerialize method need be called only once for a persistent instance. The
        // writeObject method in the least-derived
        // pc class that implements Serializable in the inheritance hierarchy needs to be modified
        // or generated to call it.
        if (cmd.getSuperAbstractClassMetaData() == null && !hasWriteObject) {
          // User hasn't provided their own writeObject, so provide the default but with a call to
          // dnPreSerialize first
          ClassMethod method = WriteObject.getInstance(enhancer);
          method.initialise(cv);
          method.execute();
          method.close();
        }
      }

      // Add dnGetXXX, dnSetXXX for each of the (managed) fields/properties
      AbstractMemberMetaData[] fmds = cmd.getManagedMembers();
      for (int i = 0; i < fmds.length; i++) {
        if (fmds[i].getPersistenceModifier() == FieldPersistenceModifier.NONE) {
          // Field/Property is not persistent so ignore
          continue;
        }

        byte persistenceFlags = fmds[i].getPersistenceFlags();
        ClassMethod getMethod = null;
        ClassMethod setMethod = null;
        if (fmds[i] instanceof PropertyMetaData) {
          // dnGetXXX, dnSetXXX for property are generated when processing existing getXXX, setXXX
          // methods
        } else {
          // Generate dnGetXXX, dnSetXXX for field
          if ((persistenceFlags & Persistable.MEDIATE_READ) == Persistable.MEDIATE_READ) {
            getMethod = new GetViaMediate(enhancer, fmds[i]);
          } else if ((persistenceFlags & Persistable.CHECK_READ) == Persistable.CHECK_READ) {
            getMethod = new GetViaCheck(enhancer, fmds[i]);
          } else {
            getMethod = new GetNormal(enhancer, fmds[i]);
          }

          if ((persistenceFlags & Persistable.MEDIATE_WRITE) == Persistable.MEDIATE_WRITE) {
            setMethod = new SetViaMediate(enhancer, fmds[i]);
          } else if ((persistenceFlags & Persistable.CHECK_WRITE) == Persistable.CHECK_WRITE) {
            setMethod = new SetViaCheck(enhancer, fmds[i]);
          } else {
            setMethod = new SetNormal(enhancer, fmds[i]);
          }
        }

        if (getMethod != null) {
          getMethod.initialise(cv);
          getMethod.execute();
          getMethod.close();
        }
        if (setMethod != null) {
          setMethod.initialise(cv);
          setMethod.execute();
          setMethod.close();
        }
      }
    }
    cv.visitEnd();
  }
  /**
   * Method to return a statement selecting the candidate table(s) required to cover all possible
   * types for this candidates inheritance strategy.
   *
   * @param storeMgr RDBMS StoreManager
   * @param parentStmt Parent statement (if there is one)
   * @param cmd Metadata for the class
   * @param clsMapping Mapping for the results of the statement
   * @param ec ExecutionContext
   * @param candidateCls Candidate class
   * @param subclasses Whether to create a statement for subclasses of the candidate too
   * @param result The result clause
   * @param candidateAlias alias for the candidate (if any)
   * @param candidateTableGroupName TableGroup name for the candidate (if any)
   * @return The SQLStatement
   * @throws NucleusException if there are no tables for concrete classes in this query (hence would
   *     return null)
   */
  public static SQLStatement getStatementForCandidates(
      RDBMSStoreManager storeMgr,
      SQLStatement parentStmt,
      AbstractClassMetaData cmd,
      StatementClassMapping clsMapping,
      ExecutionContext ec,
      Class candidateCls,
      boolean subclasses,
      String result,
      String candidateAlias,
      String candidateTableGroupName) {
    SQLStatement stmt = null;

    DatastoreIdentifier candidateAliasId = null;
    if (candidateAlias != null) {
      candidateAliasId = storeMgr.getIdentifierFactory().newTableIdentifier(candidateAlias);
    }

    ClassLoaderResolver clr = ec.getClassLoaderResolver();
    List<DatastoreClass> candidateTables = new ArrayList<DatastoreClass>();
    if (cmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.COMPLETE_TABLE) {
      DatastoreClass candidateTable = storeMgr.getDatastoreClass(cmd.getFullClassName(), clr);
      if (candidateTable != null) {
        candidateTables.add(candidateTable);
      }
      if (subclasses) {
        Collection<String> subclassNames =
            storeMgr.getSubClassesForClass(cmd.getFullClassName(), subclasses, clr);
        if (subclassNames != null) {
          Iterator<String> subclassIter = subclassNames.iterator();
          while (subclassIter.hasNext()) {
            String subclassName = subclassIter.next();
            DatastoreClass tbl = storeMgr.getDatastoreClass(subclassName, clr);
            if (tbl != null) {
              candidateTables.add(tbl);
            }
          }
        }
      }

      Iterator<DatastoreClass> iter = candidateTables.iterator();
      int maxClassNameLength = cmd.getFullClassName().length();
      while (iter.hasNext()) {
        DatastoreClass cls = iter.next();
        String className = cls.getType();
        if (className.length() > maxClassNameLength) {
          maxClassNameLength = className.length();
        }
      }

      iter = candidateTables.iterator();
      while (iter.hasNext()) {
        DatastoreClass cls = iter.next();

        SQLStatement tblStmt =
            new SQLStatement(parentStmt, storeMgr, cls, candidateAliasId, candidateTableGroupName);
        tblStmt.setClassLoaderResolver(clr);
        tblStmt.setCandidateClassName(cls.getType());

        // Add SELECT of dummy column accessible as "NUCLEUS_TYPE" containing the classname
        JavaTypeMapping m = storeMgr.getMappingManager().getMapping(String.class);
        String nuctypeName = cls.getType();
        if (maxClassNameLength > nuctypeName.length()) {
          nuctypeName = StringUtils.leftAlignedPaddedString(nuctypeName, maxClassNameLength);
        }
        StringLiteral lit = new StringLiteral(tblStmt, m, nuctypeName, null);
        tblStmt.select(lit, UnionStatementGenerator.NUC_TYPE_COLUMN);

        if (stmt == null) {
          stmt = tblStmt;
        } else {
          stmt.union(tblStmt);
        }
      }
      if (clsMapping != null) {
        clsMapping.setNucleusTypeColumnName(UnionStatementGenerator.NUC_TYPE_COLUMN);
      }
    } else {
      // "new-table", "superclass-table", "subclass-table"
      List<Class> candidateClasses = new ArrayList<Class>();
      if (ClassUtils.isReferenceType(candidateCls)) {
        // Persistent interface, so find all persistent implementations
        String[] clsNames =
            storeMgr
                .getNucleusContext()
                .getMetaDataManager()
                .getClassesImplementingInterface(candidateCls.getName(), clr);
        for (int i = 0; i < clsNames.length; i++) {
          Class cls = clr.classForName(clsNames[i]);
          DatastoreClass table = storeMgr.getDatastoreClass(clsNames[i], clr);
          candidateClasses.add(cls);
          candidateTables.add(table);
          AbstractClassMetaData implCmd =
              storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForClass(cls, clr);
          if (implCmd.getIdentityType() != cmd.getIdentityType()) {
            throw new NucleusUserException(
                "You are querying an interface ("
                    + cmd.getFullClassName()
                    + ") "
                    + "yet one of its implementations ("
                    + implCmd.getFullClassName()
                    + ") "
                    + " uses a different identity type!");
          } else if (cmd.getIdentityType() == IdentityType.APPLICATION) {
            if (cmd.getPKMemberPositions().length != implCmd.getPKMemberPositions().length) {
              throw new NucleusUserException(
                  "You are querying an interface ("
                      + cmd.getFullClassName()
                      + ") "
                      + "yet one of its implementations ("
                      + implCmd.getFullClassName()
                      + ") "
                      + " has a different number of PK members!");
            }
          }
        }
      } else {
        DatastoreClass candidateTable = storeMgr.getDatastoreClass(cmd.getFullClassName(), clr);
        if (candidateTable != null) {
          // Candidate has own table
          candidateClasses.add(candidateCls);
          candidateTables.add(candidateTable);
        } else {
          // Candidate stored in subclass tables
          AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(cmd, clr);
          if (cmds != null && cmds.length > 0) {
            for (int i = 0; i < cmds.length; i++) {
              DatastoreClass table = storeMgr.getDatastoreClass(cmds[i].getFullClassName(), clr);
              Class cls = clr.classForName(cmds[i].getFullClassName());
              candidateClasses.add(cls);
              candidateTables.add(table);
            }
          } else {
            throw new UnsupportedOperationException(
                "No tables for query of " + cmd.getFullClassName());
          }
        }
      }

      for (int i = 0; i < candidateTables.size(); i++) {
        DatastoreClass tbl = candidateTables.get(i);
        Class cls = candidateClasses.get(i);
        StatementGenerator stmtGen = null;
        if (tbl.getDiscriminatorMapping(true) != null
            || QueryUtils.resultHasOnlyAggregates(result)) {
          // Either has a discriminator, or only selecting aggregates so need single select
          stmtGen =
              new DiscriminatorStatementGenerator(
                  storeMgr, clr, cls, subclasses, candidateAliasId, candidateTableGroupName);
          stmtGen.setOption(StatementGenerator.OPTION_RESTRICT_DISCRIM);
        } else {
          stmtGen =
              new UnionStatementGenerator(
                  storeMgr, clr, cls, subclasses, candidateAliasId, candidateTableGroupName);
          if (result == null) {
            // Returning one row per candidate so include distinguisher column
            stmtGen.setOption(StatementGenerator.OPTION_SELECT_NUCLEUS_TYPE);
            clsMapping.setNucleusTypeColumnName(UnionStatementGenerator.NUC_TYPE_COLUMN);
          }
        }
        stmtGen.setParentStatement(parentStmt);
        SQLStatement tblStmt = stmtGen.getStatement();

        if (stmt == null) {
          stmt = tblStmt;
        } else {
          stmt.union(tblStmt);
        }
      }
    }

    return stmt;
  }
 public DiscriminatorStrategy getDiscriminatorStrategy() {
   return cmd.getDiscriminatorStrategyForTable();
 }
  /**
   * Utility that does a discriminator candidate query for the specified candidate and subclasses
   * and returns the class name of the instance that has the specified identity (if any).
   *
   * @param storeMgr RDBMS StoreManager
   * @param ec execution context
   * @param id The id
   * @param cmd Metadata for the root candidate class
   * @return Name of the class with this identity (or null if none found)
   */
  public static String getClassNameForIdUsingDiscriminator(
      RDBMSStoreManager storeMgr, ExecutionContext ec, Object id, AbstractClassMetaData cmd) {
    // Check for input error
    if (cmd == null || id == null) {
      return null;
    }

    SQLExpressionFactory exprFactory = storeMgr.getSQLExpressionFactory();
    ClassLoaderResolver clr = ec.getClassLoaderResolver();
    DatastoreClass primaryTable = storeMgr.getDatastoreClass(cmd.getFullClassName(), clr);

    // Form the query to find which one of these classes has the instance with this id
    DiscriminatorStatementGenerator stmtGen =
        new DiscriminatorStatementGenerator(
            storeMgr, clr, clr.classForName(cmd.getFullClassName()), true, null, null);
    stmtGen.setOption(SelectStatementGenerator.OPTION_RESTRICT_DISCRIM);
    SelectStatement sqlStmt = stmtGen.getStatement();

    // Select the discriminator
    JavaTypeMapping discrimMapping = primaryTable.getDiscriminatorMapping(true);
    SQLTable discrimSqlTbl =
        SQLStatementHelper.getSQLTableForMappingOfTable(
            sqlStmt, sqlStmt.getPrimaryTable(), discrimMapping);
    sqlStmt.select(discrimSqlTbl, discrimMapping, null);

    // Restrict to this id
    JavaTypeMapping idMapping = primaryTable.getIdMapping();
    JavaTypeMapping idParamMapping = new PersistableIdMapping((PersistableMapping) idMapping);
    SQLExpression sqlFldExpr =
        exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), idMapping);
    SQLExpression sqlFldVal = exprFactory.newLiteralParameter(sqlStmt, idParamMapping, id, "ID");
    sqlStmt.whereAnd(sqlFldExpr.eq(sqlFldVal), true);

    // Perform the query
    try {
      ManagedConnection mconn = storeMgr.getConnection(ec);
      SQLController sqlControl = storeMgr.getSQLController();
      if (ec.getSerializeReadForClass(cmd.getFullClassName())) {
        sqlStmt.addExtension(SQLStatement.EXTENSION_LOCK_FOR_UPDATE, true);
      }

      try {
        PreparedStatement ps =
            SQLStatementHelper.getPreparedStatementForSQLStatement(sqlStmt, ec, mconn, null, null);
        String statement = sqlStmt.getSQLText().toSQL();
        try {
          ResultSet rs = sqlControl.executeStatementQuery(ec, mconn, statement, ps);
          try {
            if (rs != null) {
              while (rs.next()) {
                DiscriminatorMetaData dismd = discrimMapping.getTable().getDiscriminatorMetaData();
                return RDBMSQueryUtils.getClassNameFromDiscriminatorResultSetRow(
                    discrimMapping, dismd, rs, ec);
              }
            }
          } finally {
            rs.close();
          }
        } finally {
          sqlControl.closeStatement(mconn, ps);
        }
      } finally {
        mconn.release();
      }
    } catch (SQLException sqe) {
      NucleusLogger.DATASTORE.error("Exception thrown on querying of discriminator for id", sqe);
      throw new NucleusDataStoreException(sqe.toString(), sqe);
    }

    return null;
  }
  /**
   * Utility that does a union candidate query for the specified candidate(s) and subclasses and
   * returns the class name of the instance that has the specified identity (if any).
   *
   * @param storeMgr RDBMS StoreManager
   * @param ec execution context
   * @param id The id
   * @param rootCmds Metadata for the classes at the root
   * @return Name of the class with this identity (or null if none found)
   */
  public static String getClassNameForIdUsingUnion(
      RDBMSStoreManager storeMgr,
      ExecutionContext ec,
      Object id,
      List<AbstractClassMetaData> rootCmds) {
    // Check for input error
    if (rootCmds == null || rootCmds.isEmpty() || id == null) {
      return null;
    }

    SQLExpressionFactory exprFactory = storeMgr.getSQLExpressionFactory();
    ClassLoaderResolver clr = ec.getClassLoaderResolver();

    // Form a query UNIONing all possible root candidates (and their subclasses)
    Iterator<AbstractClassMetaData> rootCmdIter = rootCmds.iterator();

    AbstractClassMetaData sampleCmd =
        null; // Metadata for sample class in the tree so we can check if needs locking
    SelectStatement sqlStmtMain = null;
    while (rootCmdIter.hasNext()) {
      AbstractClassMetaData rootCmd = rootCmdIter.next();
      DatastoreClass rootTbl = storeMgr.getDatastoreClass(rootCmd.getFullClassName(), clr);
      if (rootTbl == null) {
        // Class must be using "subclass-table" (no table of its own) so find where it is
        AbstractClassMetaData[] subcmds = storeMgr.getClassesManagingTableForClass(rootCmd, clr);
        if (subcmds == null || subcmds.length == 0) {
          // No table for this class so ignore
        } else {
          for (int i = 0; i < subcmds.length; i++) {
            UnionStatementGenerator stmtGen =
                new UnionStatementGenerator(
                    storeMgr,
                    clr,
                    clr.classForName(subcmds[i].getFullClassName()),
                    true,
                    null,
                    null);
            stmtGen.setOption(SelectStatementGenerator.OPTION_SELECT_NUCLEUS_TYPE);
            if (sqlStmtMain == null) {
              sampleCmd = subcmds[i];
              sqlStmtMain = stmtGen.getStatement();

              // WHERE (object id) = ?
              JavaTypeMapping idMapping = sqlStmtMain.getPrimaryTable().getTable().getIdMapping();
              JavaTypeMapping idParamMapping =
                  new PersistableIdMapping((PersistableMapping) idMapping);
              SQLExpression fieldExpr =
                  exprFactory.newExpression(sqlStmtMain, sqlStmtMain.getPrimaryTable(), idMapping);
              SQLExpression fieldVal =
                  exprFactory.newLiteralParameter(sqlStmtMain, idParamMapping, id, "ID");
              sqlStmtMain.whereAnd(fieldExpr.eq(fieldVal), true);
            } else {
              SelectStatement sqlStmt = stmtGen.getStatement();

              // WHERE (object id) = ?
              JavaTypeMapping idMapping = sqlStmt.getPrimaryTable().getTable().getIdMapping();
              JavaTypeMapping idParamMapping =
                  new PersistableIdMapping((PersistableMapping) idMapping);
              SQLExpression fieldExpr =
                  exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), idMapping);
              SQLExpression fieldVal =
                  exprFactory.newLiteralParameter(sqlStmt, idParamMapping, id, "ID");
              sqlStmt.whereAnd(fieldExpr.eq(fieldVal), true);

              sqlStmtMain.union(sqlStmt);
            }
          }
        }
      } else {
        UnionStatementGenerator stmtGen =
            new UnionStatementGenerator(
                storeMgr, clr, clr.classForName(rootCmd.getFullClassName()), true, null, null);
        stmtGen.setOption(SelectStatementGenerator.OPTION_SELECT_NUCLEUS_TYPE);
        if (sqlStmtMain == null) {
          sampleCmd = rootCmd;
          sqlStmtMain = stmtGen.getStatement();

          // WHERE (object id) = ?
          JavaTypeMapping idMapping = sqlStmtMain.getPrimaryTable().getTable().getIdMapping();
          JavaTypeMapping idParamMapping = new PersistableIdMapping((PersistableMapping) idMapping);
          SQLExpression fieldExpr =
              exprFactory.newExpression(sqlStmtMain, sqlStmtMain.getPrimaryTable(), idMapping);
          SQLExpression fieldVal =
              exprFactory.newLiteralParameter(sqlStmtMain, idParamMapping, id, "ID");
          sqlStmtMain.whereAnd(fieldExpr.eq(fieldVal), true);
        } else {
          SelectStatement sqlStmt = stmtGen.getStatement();

          // WHERE (object id) = ?
          JavaTypeMapping idMapping = sqlStmt.getPrimaryTable().getTable().getIdMapping();
          JavaTypeMapping idParamMapping = new PersistableIdMapping((PersistableMapping) idMapping);
          SQLExpression fieldExpr =
              exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), idMapping);
          SQLExpression fieldVal =
              exprFactory.newLiteralParameter(sqlStmt, idParamMapping, id, "ID");
          sqlStmt.whereAnd(fieldExpr.eq(fieldVal), true);

          sqlStmtMain.union(sqlStmt);
        }
      }
    }

    // Perform the query
    try {
      ManagedConnection mconn = storeMgr.getConnection(ec);
      SQLController sqlControl = storeMgr.getSQLController();
      if (ec.getSerializeReadForClass(sampleCmd.getFullClassName())) {
        sqlStmtMain.addExtension(SQLStatement.EXTENSION_LOCK_FOR_UPDATE, true);
      }

      try {
        PreparedStatement ps =
            SQLStatementHelper.getPreparedStatementForSQLStatement(
                sqlStmtMain, ec, mconn, null, null);
        String statement = sqlStmtMain.getSQLText().toSQL();
        try {
          ResultSet rs = sqlControl.executeStatementQuery(ec, mconn, statement, ps);
          try {
            if (rs != null) {
              while (rs.next()) {
                try {
                  return rs.getString(UnionStatementGenerator.NUC_TYPE_COLUMN).trim();
                } catch (SQLException sqle) {
                }
              }
            }
          } finally {
            rs.close();
          }
        } finally {
          sqlControl.closeStatement(mconn, ps);
        }
      } finally {
        mconn.release();
      }
    } catch (SQLException sqe) {
      NucleusLogger.DATASTORE.error("Exception with UNION statement", sqe);
      throw new NucleusDataStoreException(sqe.toString());
    }

    return null;
  }
 public String toString() {
   return "ElementInfo : [class=" + cmd.getFullClassName() + " table=" + table + "]";
 }
  protected static Object getObjectUsingNondurableIdForResult(
      final Result result,
      final AbstractClassMetaData cmd,
      final ExecutionContext ec,
      boolean ignoreCache,
      final int[] fpMembers,
      String tableName,
      StoreManager storeMgr,
      Table table) {
    if (cmd.hasDiscriminatorStrategy()) {
      // Check the class for this discriminator value
      String familyName = HBaseUtils.getFamilyNameForColumn(table.getDiscriminatorColumn());
      String columnName = HBaseUtils.getQualifierNameForColumn(table.getDiscriminatorColumn());
      Object discValue = new String(result.getValue(familyName.getBytes(), columnName.getBytes()));
      if (cmd.getDiscriminatorStrategy() == DiscriminatorStrategy.CLASS_NAME
          && !cmd.getFullClassName().equals(discValue)) {
        return null;
      } else if (cmd.getDiscriminatorStrategy() == DiscriminatorStrategy.VALUE_MAP
          && !cmd.getDiscriminatorValue().equals(discValue)) {
        return null;
      }
    }

    final FieldManager fm = new FetchFieldManager(ec, cmd, result, table);
    SCOID id = new SCOID(cmd.getFullClassName());
    Object pc =
        ec.findObject(
            id,
            new FieldValues() {
              // ObjectProvider calls the fetchFields method
              public void fetchFields(ObjectProvider op) {
                op.replaceFields(fpMembers, fm);
              }

              public void fetchNonLoadedFields(ObjectProvider op) {
                op.replaceNonLoadedFields(fpMembers, fm);
              }

              public FetchPlan getFetchPlanForLoading() {
                return null;
              }
            },
            null,
            ignoreCache,
            false);

    if (cmd.isVersioned()) {
      // Set the version on the object
      ObjectProvider op = ec.findObjectProvider(pc);
      Object version = null;
      VersionMetaData vermd = cmd.getVersionMetaDataForClass();
      if (vermd.getFieldName() != null) {
        // Set the version from the field value
        AbstractMemberMetaData verMmd = cmd.getMetaDataForMember(vermd.getFieldName());
        version = op.provideField(verMmd.getAbsoluteFieldNumber());
      } else {
        // Get the surrogate version from the datastore
        version = HBaseUtils.getSurrogateVersionForObject(cmd, result, tableName, storeMgr);
      }
      op.setVersion(version);
    }

    if (result.getRow() != null) {
      ObjectProvider sm = ec.findObjectProvider(pc);
      sm.setAssociatedValue("HBASE_ROW_KEY", result.getRow());
    }

    return pc;
  }
  protected static Object getObjectUsingDatastoreIdForResult(
      final Result result,
      final AbstractClassMetaData cmd,
      final ExecutionContext ec,
      boolean ignoreCache,
      final int[] fpMembers,
      String tableName,
      StoreManager storeMgr,
      Table table) {
    if (cmd.hasDiscriminatorStrategy()) {
      // Check the class for this discriminator value
      String familyName = HBaseUtils.getFamilyNameForColumn(table.getDiscriminatorColumn());
      String columnName = HBaseUtils.getQualifierNameForColumn(table.getDiscriminatorColumn());
      Object discValue = new String(result.getValue(familyName.getBytes(), columnName.getBytes()));
      if (cmd.getDiscriminatorStrategy() == DiscriminatorStrategy.CLASS_NAME
          && !cmd.getFullClassName().equals(discValue)) {
        return null;
      } else if (cmd.getDiscriminatorStrategy() == DiscriminatorStrategy.VALUE_MAP
          && !cmd.getDiscriminatorValue().equals(discValue)) {
        return null;
      }
    }

    String dsidFamilyName = HBaseUtils.getFamilyNameForColumn(table.getDatastoreIdColumn());
    String dsidColumnName = HBaseUtils.getQualifierNameForColumn(table.getDatastoreIdColumn());
    Object id = null;
    try {
      byte[] bytes = result.getValue(dsidFamilyName.getBytes(), dsidColumnName.getBytes());
      if (bytes != null) {
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bis);
        Object key = ois.readObject();
        id =
            ec.getNucleusContext().getIdentityManager().getDatastoreId(cmd.getFullClassName(), key);
        ois.close();
        bis.close();
      } else {
        throw new NucleusException(
            "Retrieved identity for family="
                + dsidFamilyName
                + " column="
                + dsidColumnName
                + " IS NULL");
      }
    } catch (Exception e) {
      throw new NucleusException(e.getMessage(), e);
    }

    final FieldManager fm = new FetchFieldManager(ec, cmd, result, table);
    Object pc =
        ec.findObject(
            id,
            new FieldValues() {
              // ObjectProvider calls the fetchFields method
              public void fetchFields(ObjectProvider op) {
                op.replaceFields(fpMembers, fm);
              }

              public void fetchNonLoadedFields(ObjectProvider op) {
                op.replaceNonLoadedFields(fpMembers, fm);
              }

              public FetchPlan getFetchPlanForLoading() {
                return null;
              }
            },
            null,
            ignoreCache,
            false);

    if (cmd.isVersioned()) {
      // Set the version on the object
      ObjectProvider op = ec.findObjectProvider(pc);
      Object version = null;
      VersionMetaData vermd = cmd.getVersionMetaDataForClass();
      if (vermd.getFieldName() != null) {
        // Set the version from the field value
        AbstractMemberMetaData verMmd = cmd.getMetaDataForMember(vermd.getFieldName());
        version = op.provideField(verMmd.getAbsoluteFieldNumber());
      } else {
        // Get the surrogate version from the datastore
        version = HBaseUtils.getSurrogateVersionForObject(cmd, result, tableName, storeMgr);
      }
      op.setVersion(version);
    }

    if (result.getRow() != null) {
      ObjectProvider sm = ec.findObjectProvider(pc);
      sm.setAssociatedValue("HBASE_ROW_KEY", result.getRow());
    }

    return pc;
  }
  /**
   * Convenience method to get all objects of the specified type.
   *
   * @param ec Execution Context
   * @param mconn Managed Connection
   * @param cmd Metadata for the type to return
   * @param ignoreCache Whether to ignore the cache
   * @param fp Fetch Plan
   * @param filter Optional filter for the candidates
   * @param storeMgr StoreManager in use
   * @return List of objects of the candidate type
   */
  private static List getObjectsOfType(
      final ExecutionContext ec,
      final HBaseManagedConnection mconn,
      final AbstractClassMetaData cmd,
      boolean ignoreCache,
      FetchPlan fp,
      final Filter filter,
      final StoreManager storeMgr) {
    List results = new ArrayList();

    if (!storeMgr.managesClass(cmd.getFullClassName())) {
      storeMgr.manageClasses(ec.getClassLoaderResolver(), cmd.getFullClassName());
    }
    final Table table = storeMgr.getStoreDataForClass(cmd.getFullClassName()).getTable();
    final String tableName = table.getName();
    final int[] fpMembers = fp.getFetchPlanForClass(cmd).getMemberNumbers();
    try {
      final ClassLoaderResolver clr = ec.getClassLoaderResolver();

      Iterator<Result> it =
          (Iterator<Result>)
              AccessController.doPrivileged(
                  new PrivilegedExceptionAction() {
                    public Object run() throws Exception {
                      Scan scan = new Scan();
                      if (filter != null) {
                        scan.setFilter(filter);
                      }

                      // Retrieve all fetch-plan fields
                      for (int i = 0; i < fpMembers.length; i++) {
                        AbstractMemberMetaData mmd =
                            cmd.getMetaDataForManagedMemberAtAbsolutePosition(fpMembers[i]);
                        RelationType relationType = mmd.getRelationType(clr);
                        if (relationType != RelationType.NONE
                            && MetaDataUtils.getInstance()
                                .isMemberEmbedded(
                                    ec.getMetaDataManager(), clr, mmd, relationType, null)) {
                          if (RelationType.isRelationSingleValued(relationType)) {
                            // 1-1 embedded
                            List<AbstractMemberMetaData> embMmds =
                                new ArrayList<AbstractMemberMetaData>();
                            embMmds.add(mmd);
                            addColumnsToScanForEmbeddedMember(scan, embMmds, table, ec);
                          }
                        } else {
                          Column col =
                              table
                                  .getMemberColumnMappingForMember(mmd)
                                  .getColumn(0); // TODO Support multicol mapping
                          byte[] familyName = HBaseUtils.getFamilyNameForColumn(col).getBytes();
                          byte[] qualifName = HBaseUtils.getQualifierNameForColumn(col).getBytes();
                          scan.addColumn(familyName, qualifName);
                        }
                      }

                      VersionMetaData vermd = cmd.getVersionMetaDataForClass();
                      if (cmd.isVersioned() && vermd.getFieldName() == null) {
                        // Add version column
                        byte[] familyName =
                            HBaseUtils.getFamilyNameForColumn(table.getVersionColumn()).getBytes();
                        byte[] qualifName =
                            HBaseUtils.getQualifierNameForColumn(table.getVersionColumn())
                                .getBytes();
                        scan.addColumn(familyName, qualifName);
                      }
                      if (cmd.hasDiscriminatorStrategy()) {
                        // Add discriminator column
                        byte[] familyName =
                            HBaseUtils.getFamilyNameForColumn(table.getDiscriminatorColumn())
                                .getBytes();
                        byte[] qualifName =
                            HBaseUtils.getQualifierNameForColumn(table.getDiscriminatorColumn())
                                .getBytes();
                        scan.addColumn(familyName, qualifName);
                      }
                      if (cmd.getIdentityType() == IdentityType.DATASTORE) {
                        // Add datastore identity column
                        byte[] familyName =
                            HBaseUtils.getFamilyNameForColumn(table.getDatastoreIdColumn())
                                .getBytes();
                        byte[] qualifName =
                            HBaseUtils.getQualifierNameForColumn(table.getDatastoreIdColumn())
                                .getBytes();
                        scan.addColumn(familyName, qualifName);
                      }

                      HTableInterface htable = mconn.getHTable(tableName);
                      ResultScanner scanner = htable.getScanner(scan);
                      if (ec.getStatistics() != null) {
                        // Add to statistics
                        ec.getStatistics().incrementNumReads();
                      }
                      Iterator<Result> it = scanner.iterator();
                      return it;
                    }
                  });

      // Instantiate the objects
      if (cmd.getIdentityType() == IdentityType.APPLICATION) {
        while (it.hasNext()) {
          final Result result = it.next();
          Object obj =
              getObjectUsingApplicationIdForResult(
                  result, cmd, ec, ignoreCache, fpMembers, tableName, storeMgr, table);
          if (obj != null) {
            results.add(obj);
          }
        }
      } else if (cmd.getIdentityType() == IdentityType.DATASTORE) {
        while (it.hasNext()) {
          final Result result = it.next();
          Object obj =
              getObjectUsingDatastoreIdForResult(
                  result, cmd, ec, ignoreCache, fpMembers, tableName, storeMgr, table);
          if (obj != null) {
            results.add(obj);
          }
        }
      } else {
        while (it.hasNext()) {
          final Result result = it.next();
          Object obj =
              getObjectUsingNondurableIdForResult(
                  result, cmd, ec, ignoreCache, fpMembers, tableName, storeMgr, table);
          if (obj != null) {
            results.add(obj);
          }
        }
      }
    } catch (PrivilegedActionException e) {
      throw new NucleusDataStoreException(e.getMessage(), e.getCause());
    }
    return results;
  }
 public String getClassName() {
   return cmd.getFullClassName();
 }
  /**
   * Utility to create the application identity columns and mapping. Uses the id mapping of the
   * specified class table and copies the mappings and columns, whilst retaining the passed
   * preferences for column namings. This is used to copy the PK mappings of a superclass table so
   * we have the same PK.
   *
   * @param columnContainer The container of column MetaData with any namings
   * @param refTable The table that we use as reference
   * @param clr The ClassLoaderResolver
   * @param cmd The ClassMetaData
   */
  final void addApplicationIdUsingClassTableId(
      ColumnMetaDataContainer columnContainer,
      DatastoreClass refTable,
      ClassLoaderResolver clr,
      AbstractClassMetaData cmd) {
    ColumnMetaData[] userdefinedCols = null;
    int nextUserdefinedCol = 0;
    if (columnContainer != null) {
      userdefinedCols = columnContainer.getColumnMetaData();
    }

    pkMappings = new JavaTypeMapping[cmd.getPKMemberPositions().length];
    for (int i = 0; i < cmd.getPKMemberPositions().length; i++) {
      AbstractMemberMetaData mmd =
          cmd.getMetaDataForManagedMemberAtAbsolutePosition(cmd.getPKMemberPositions()[i]);
      JavaTypeMapping mapping = refTable.getMemberMapping(mmd);
      if (mapping == null) {
        // probably due to invalid metadata defined by the user
        throw new NucleusUserException(
            "Cannot find mapping for field "
                + mmd.getFullFieldName()
                + " in table "
                + refTable.toString()
                + " "
                + StringUtils.collectionToString(refTable.getColumns()));
      }

      JavaTypeMapping masterMapping =
          storeMgr.getMappingManager().getMapping(clr.classForName(mapping.getType()));
      masterMapping.setMemberMetaData(mmd); // Update field info in mapping
      masterMapping.setTable(this);
      pkMappings[i] = masterMapping;

      // Loop through each id column in the reference table and add the same here
      // applying the required names from the columnContainer
      for (int j = 0; j < mapping.getNumberOfDatastoreMappings(); j++) {
        JavaTypeMapping m = masterMapping;
        Column refColumn = mapping.getDatastoreMapping(j).getColumn();
        if (mapping instanceof PersistableMapping) {
          m =
              storeMgr
                  .getMappingManager()
                  .getMapping(clr.classForName(refColumn.getJavaTypeMapping().getType()));
          ((PersistableMapping) masterMapping).addJavaTypeMapping(m);
        }

        ColumnMetaData userdefinedColumn = null;
        if (userdefinedCols != null) {
          for (int k = 0; k < userdefinedCols.length; k++) {
            if (refColumn.getIdentifier().toString().equals(userdefinedCols[k].getTarget())) {
              userdefinedColumn = userdefinedCols[k];
              break;
            }
          }
          if (userdefinedColumn == null && nextUserdefinedCol < userdefinedCols.length) {
            userdefinedColumn = userdefinedCols[nextUserdefinedCol++];
          }
        }

        // Add this application identity column
        Column idColumn = null;
        if (userdefinedColumn != null) {
          // User has provided a name for this column
          // Currently we only use the column namings from the users definition but we could easily
          // take more of their details.
          idColumn =
              addColumn(
                  refColumn.getStoredJavaType(),
                  storeMgr
                      .getIdentifierFactory()
                      .newIdentifier(IdentifierType.COLUMN, userdefinedColumn.getName()),
                  m,
                  refColumn.getColumnMetaData());
        } else {
          // No name provided so take same as superclass
          idColumn =
              addColumn(
                  refColumn.getStoredJavaType(),
                  refColumn.getIdentifier(),
                  m,
                  refColumn.getColumnMetaData());
        }
        if (mapping.getDatastoreMapping(j).getColumn().getColumnMetaData() != null) {
          refColumn.copyConfigurationTo(idColumn);
        }
        idColumn.setPrimaryKey();

        // Set the column type based on the field.getType()
        getStoreManager()
            .getMappingManager()
            .createDatastoreMapping(m, idColumn, refColumn.getJavaTypeMapping().getType());
      }

      // Update highest field number if this is higher
      int absoluteFieldNumber = mmd.getAbsoluteFieldNumber();
      if (absoluteFieldNumber > highestMemberNumber) {
        highestMemberNumber = absoluteFieldNumber;
      }
    }
  }
  /**
   * Utility to create the datastore identity column and mapping. This is used in 2 modes. The first
   * is where we have a (primary) class table and we aren't creating the OID mapping as a FK to
   * another class. The second is where we have a (secondary) class table and we are creating the
   * OID mapping as a FK to the primary class. In the second case the refTable will be specified.
   *
   * @param columnMetaData The column MetaData for the datastore id
   * @param refTable Table used as a reference (if any)
   * @param cmd The MetaData for the class
   */
  void addDatastoreId(
      ColumnMetaData columnMetaData, DatastoreClass refTable, AbstractClassMetaData cmd) {
    // Create the mapping, setting its table
    datastoreIDMapping = new DatastoreIdMapping();
    datastoreIDMapping.setTable(this);
    datastoreIDMapping.initialize(storeMgr, cmd.getFullClassName());

    // Create a ColumnMetaData in the container if none is defined
    ColumnMetaData colmd = null;
    if (columnMetaData == null) {
      colmd = new ColumnMetaData();
    } else {
      colmd = columnMetaData;
    }
    if (colmd.getName() == null) {
      // Provide default column naming if none is defined
      if (refTable != null) {
        colmd.setName(
            storeMgr
                .getIdentifierFactory()
                .newColumnIdentifier(
                    refTable.getIdentifier().getName(),
                    this.storeMgr
                        .getNucleusContext()
                        .getTypeManager()
                        .isDefaultEmbeddedType(DatastoreId.class),
                    FieldRole.ROLE_OWNER,
                    false)
                .getName());
      } else {
        colmd.setName(
            storeMgr
                .getIdentifierFactory()
                .newColumnIdentifier(
                    identifier.getName(),
                    this.storeMgr
                        .getNucleusContext()
                        .getTypeManager()
                        .isDefaultEmbeddedType(DatastoreId.class),
                    FieldRole.ROLE_NONE,
                    false)
                .getName());
      }
    }

    // Add the datastore identity column as the PK
    Column idColumn =
        addColumn(
            DatastoreId.class.getName(),
            storeMgr.getIdentifierFactory().newIdentifier(IdentifierType.COLUMN, colmd.getName()),
            datastoreIDMapping,
            colmd);
    idColumn.setPrimaryKey();

    // Set the identity column type based on the IdentityStrategy
    String strategyName = cmd.getIdentityMetaData().getValueStrategy().toString();
    if (cmd.getIdentityMetaData().getValueStrategy().equals(IdentityStrategy.CUSTOM)) {
      strategyName = cmd.getIdentityMetaData().getValueStrategy().getCustomName();
    }
    if (strategyName != null && IdentityStrategy.NATIVE.toString().equals(strategyName)) {
      strategyName = storeMgr.getStrategyForNative(cmd, -1);
    }

    // Check the value generator type being stored
    Class valueGeneratedType = Long.class;
    if (strategyName != null && IdentityStrategy.IDENTITY.toString().equals(strategyName)) {
      valueGeneratedType = dba.getAutoIncrementJavaTypeForType(valueGeneratedType);
      if (valueGeneratedType != Long.class) {
        NucleusLogger.DATASTORE_SCHEMA.debug(
            "Class "
                + cmd.getFullClassName()
                + " uses IDENTITY strategy and rather than using BIGINT "
                + " for the column type, using "
                + valueGeneratedType.getName()
                + " since the datastore requires that");
      }
    }
    try {
      // Create generator so we can find the generated type
      // a). Try as unique generator first
      AbstractGenerator generator =
          (AbstractGenerator)
              storeMgr
                  .getNucleusContext()
                  .getPluginManager()
                  .createExecutableExtension(
                      "org.datanucleus.store_valuegenerator",
                      new String[] {"name", "unique"},
                      new String[] {strategyName, "true"},
                      "class-name",
                      new Class[] {String.class, Properties.class},
                      new Object[] {null, null});
      if (generator == null) {
        // b). Try as datastore-specific generator
        generator =
            (AbstractGenerator)
                storeMgr
                    .getNucleusContext()
                    .getPluginManager()
                    .createExecutableExtension(
                        "org.datanucleus.store_valuegenerator",
                        new String[] {"name", "datastore"},
                        new String[] {strategyName, storeMgr.getStoreManagerKey()},
                        "class-name",
                        new Class[] {String.class, Properties.class},
                        new Object[] {null, null});
      }
      try {
        if (generator != null) {
          ParameterizedType parameterizedType =
              (ParameterizedType) generator.getClass().getGenericSuperclass();
          valueGeneratedType = (Class) parameterizedType.getActualTypeArguments()[0];
          if (valueGeneratedType == null) {
            // Use getStorageClass method if available
            valueGeneratedType =
                (Class) generator.getClass().getMethod("getStorageClass").invoke(null);
          }
        }
      } catch (Exception e) {
      }
    } catch (Exception e) {
      NucleusLogger.VALUEGENERATION.warn(
          "Error obtaining generator for strategy=" + strategyName, e);
    }

    storeMgr
        .getMappingManager()
        .createDatastoreMapping(datastoreIDMapping, idColumn, valueGeneratedType.getName());
    logMapping("DATASTORE_ID", datastoreIDMapping);

    // Handle any auto-increment requirement
    if (isObjectIdDatastoreAttributed()) {
      if (this instanceof DatastoreClass && ((DatastoreClass) this).isBaseDatastoreClass()) {
        // Only the base class can be autoincremented
        idColumn.setIdentity(true);
      }
    }

    // Check if auto-increment and that it is supported by this RDBMS
    if (idColumn.isIdentity() && !dba.supportsOption(DatastoreAdapter.IDENTITY_COLUMNS)) {
      throw new NucleusException(
              Localiser.msg("057020", cmd.getFullClassName(), "datastore-identity"))
          .setFatal();
    }
  }
  /**
   * Method to return an expression for Map.containsValue using a subquery "EXISTS". This is for use
   * when there are "!contains" or "OR" operations in the filter. Creates the following SQL,
   *
   * <ul>
   *   <li><b>Map using join table</b>
   *       <pre>
   * SELECT 1 FROM JOIN_TBL A0_SUB
   * WHERE A0_SUB.JOIN_OWN_ID = A0.ID AND A0_SUB.JOIN_VAL_ID = {valExpr}
   * </pre>
   *   <li><b>Map with key stored in value</b>
   *       <pre>
   * SELECT 1 FROM VAL_TABLE A0_SUB INNER JOIN KEY_TBL B0 ON ...
   * WHERE B0.JOIN_OWN_ID = A0.ID AND A0_SUB.ID = {valExpr}
   * </pre>
   *   <li><b>Map of value stored in key</b>
   *       <pre>
   * SELECT 1 FROM VAL_TABLE A0_SUB
   * WHERE A0_SUB.OWN_ID = A0.ID AND A0_SUB.ID = {valExpr}
   * </pre>
   * </ul>
   *
   * and returns a BooleanSubqueryExpression ("EXISTS (subquery)")
   *
   * @param mapExpr Map expression
   * @param valExpr Expression for the value
   * @return Contains expression
   */
  protected SQLExpression containsAsSubquery(MapExpression mapExpr, SQLExpression valExpr) {
    boolean valIsUnbound = (valExpr instanceof UnboundExpression);
    String varName = null;
    if (valIsUnbound) {
      varName = ((UnboundExpression) valExpr).getVariableName();
      NucleusLogger.QUERY.debug(
          "map.containsValue binding unbound variable " + varName + " using SUBQUERY");
      // TODO What if the variable is declared as a subtype, handle this see
      // CollectionContainsMethod
    }

    RDBMSStoreManager storeMgr = stmt.getRDBMSManager();
    MetaDataManager mmgr = storeMgr.getMetaDataManager();
    AbstractMemberMetaData mmd = mapExpr.getJavaTypeMapping().getMemberMetaData();
    AbstractClassMetaData valCmd = mmd.getMap().getValueClassMetaData(clr, mmgr);
    MapTable joinTbl = (MapTable) storeMgr.getTable(mmd);
    SQLStatement subStmt = null;
    if (mmd.getMap().getMapType() == MapType.MAP_TYPE_JOIN) {
      // JoinTable Map
      if (valCmd == null) {
        // Map<?, Non-PC>
        subStmt = new SQLStatement(stmt, storeMgr, joinTbl, null, null);
        subStmt.setClassLoaderResolver(clr);
        JavaTypeMapping oneMapping = storeMgr.getMappingManager().getMapping(Integer.class);
        subStmt.select(exprFactory.newLiteral(subStmt, oneMapping, 1), null);

        // Restrict to map owner
        JavaTypeMapping ownerMapping = ((JoinTable) joinTbl).getOwnerMapping();
        SQLExpression ownerExpr =
            exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), ownerMapping);
        SQLExpression ownerIdExpr =
            exprFactory.newExpression(
                stmt, mapExpr.getSQLTable(), mapExpr.getSQLTable().getTable().getIdMapping());
        subStmt.whereAnd(ownerExpr.eq(ownerIdExpr), true);

        if (valIsUnbound) {
          // Bind the variable in the QueryGenerator
          valExpr =
              exprFactory.newExpression(
                  subStmt, subStmt.getPrimaryTable(), joinTbl.getValueMapping());
          stmt.getQueryGenerator()
              .bindVariable(varName, null, valExpr.getSQLTable(), valExpr.getJavaTypeMapping());
        } else {
          // Add restrict to value
          SQLExpression valIdExpr =
              exprFactory.newExpression(
                  subStmt, subStmt.getPrimaryTable(), joinTbl.getValueMapping());
          subStmt.whereAnd(valIdExpr.eq(valExpr), true);
        }
      } else {
        // Map<?, PC>
        DatastoreClass valTbl = storeMgr.getDatastoreClass(mmd.getMap().getValueType(), clr);
        subStmt = new SQLStatement(stmt, storeMgr, valTbl, null, null);
        subStmt.setClassLoaderResolver(clr);
        JavaTypeMapping oneMapping = storeMgr.getMappingManager().getMapping(Integer.class);
        subStmt.select(exprFactory.newLiteral(subStmt, oneMapping, 1), null);

        // Join to join table
        SQLTable joinSqlTbl =
            subStmt.innerJoin(
                subStmt.getPrimaryTable(),
                valTbl.getIdMapping(),
                joinTbl,
                null,
                joinTbl.getValueMapping(),
                null,
                null);

        // Restrict to map owner
        JavaTypeMapping ownerMapping = joinTbl.getOwnerMapping();
        SQLExpression ownerExpr = exprFactory.newExpression(subStmt, joinSqlTbl, ownerMapping);
        SQLExpression ownerIdExpr =
            exprFactory.newExpression(
                stmt, mapExpr.getSQLTable(), mapExpr.getSQLTable().getTable().getIdMapping());
        subStmt.whereAnd(ownerExpr.eq(ownerIdExpr), true);

        if (valIsUnbound) {
          // Bind the variable in the QueryGenerator
          valExpr =
              exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), valTbl.getIdMapping());
          stmt.getQueryGenerator()
              .bindVariable(varName, valCmd, valExpr.getSQLTable(), valExpr.getJavaTypeMapping());
        } else {
          // Add restrict to value
          SQLExpression valIdExpr =
              exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), valTbl.getIdMapping());
          subStmt.whereAnd(valIdExpr.eq(valExpr), true);
        }
      }
    } else if (mmd.getMap().getMapType() == MapType.MAP_TYPE_KEY_IN_VALUE) {
      // Key stored in value table
      DatastoreClass valTbl = storeMgr.getDatastoreClass(mmd.getMap().getValueType(), clr);
      JavaTypeMapping ownerMapping = null;
      if (mmd.getMappedBy() != null) {
        ownerMapping = valTbl.getMemberMapping(valCmd.getMetaDataForMember(mmd.getMappedBy()));
      } else {
        ownerMapping = valTbl.getExternalMapping(mmd, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK);
      }

      subStmt = new SQLStatement(stmt, storeMgr, valTbl, null, null);
      subStmt.setClassLoaderResolver(clr);
      JavaTypeMapping oneMapping = storeMgr.getMappingManager().getMapping(Integer.class);
      subStmt.select(exprFactory.newLiteral(subStmt, oneMapping, 1), null);

      // Restrict to map owner (on value table)
      SQLExpression ownerExpr =
          exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), ownerMapping);
      SQLExpression ownerIdExpr =
          exprFactory.newExpression(
              stmt, mapExpr.getSQLTable(), mapExpr.getSQLTable().getTable().getIdMapping());
      subStmt.whereAnd(ownerExpr.eq(ownerIdExpr), true);

      if (valIsUnbound) {
        // Bind the variable in the QueryGenerator
        valExpr =
            exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), valTbl.getIdMapping());
        stmt.getQueryGenerator()
            .bindVariable(varName, valCmd, valExpr.getSQLTable(), valExpr.getJavaTypeMapping());
      } else {
        // Add restrict to value
        JavaTypeMapping valMapping = valTbl.getIdMapping();
        SQLExpression valIdExpr =
            exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), valMapping);
        subStmt.whereAnd(valIdExpr.eq(valExpr), true);
      }
    } else if (mmd.getMap().getMapType() == MapType.MAP_TYPE_VALUE_IN_KEY) {
      AbstractClassMetaData keyCmd = mmd.getMap().getKeyClassMetaData(clr, mmgr);
      DatastoreClass keyTbl = storeMgr.getDatastoreClass(mmd.getMap().getKeyType(), clr);
      JavaTypeMapping ownerMapping = null;
      if (mmd.getMappedBy() != null) {
        ownerMapping = keyTbl.getMemberMapping(keyCmd.getMetaDataForMember(mmd.getMappedBy()));
      } else {
        ownerMapping = keyTbl.getExternalMapping(mmd, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK);
      }

      AbstractMemberMetaData keyValMmd =
          keyCmd.getMetaDataForMember(mmd.getValueMetaData().getMappedBy());
      if (valCmd == null) {
        subStmt = new SQLStatement(stmt, storeMgr, keyTbl, null, null);
        subStmt.setClassLoaderResolver(clr);
        JavaTypeMapping oneMapping = storeMgr.getMappingManager().getMapping(Integer.class);
        subStmt.select(exprFactory.newLiteral(subStmt, oneMapping, 1), null);

        // Restrict to map owner (on key table)
        SQLExpression ownerExpr =
            exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), ownerMapping);
        SQLExpression ownerIdExpr =
            exprFactory.newExpression(
                stmt, mapExpr.getSQLTable(), mapExpr.getSQLTable().getTable().getIdMapping());
        subStmt.whereAnd(ownerExpr.eq(ownerIdExpr), true);

        if (valIsUnbound) {
          // Bind the variable in the QueryGenerator
          valExpr =
              exprFactory.newExpression(
                  subStmt, subStmt.getPrimaryTable(), keyTbl.getMemberMapping(keyValMmd));
          stmt.getQueryGenerator()
              .bindVariable(varName, null, valExpr.getSQLTable(), valExpr.getJavaTypeMapping());
        } else {
          // Add restrict to value
          JavaTypeMapping valMapping = keyTbl.getMemberMapping(keyValMmd);
          SQLExpression valIdExpr =
              exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), valMapping);
          subStmt.whereAnd(valIdExpr.eq(valExpr), true);
        }
      } else {
        DatastoreClass valTbl = storeMgr.getDatastoreClass(mmd.getMap().getValueType(), clr);
        subStmt = new SQLStatement(stmt, storeMgr, valTbl, null, null);
        subStmt.setClassLoaderResolver(clr);
        JavaTypeMapping oneMapping = storeMgr.getMappingManager().getMapping(Integer.class);
        subStmt.select(exprFactory.newLiteral(subStmt, oneMapping, 1), null);

        // Join to key table
        SQLTable keySqlTbl =
            subStmt.innerJoin(
                subStmt.getPrimaryTable(),
                valTbl.getIdMapping(),
                keyTbl,
                null,
                keyTbl.getMemberMapping(keyValMmd),
                null,
                null);

        // Restrict to map owner (on key table)
        SQLExpression ownerExpr = exprFactory.newExpression(subStmt, keySqlTbl, ownerMapping);
        SQLExpression ownerIdExpr =
            exprFactory.newExpression(
                stmt, mapExpr.getSQLTable(), mapExpr.getSQLTable().getTable().getIdMapping());
        subStmt.whereAnd(ownerExpr.eq(ownerIdExpr), true);

        if (valIsUnbound) {
          // Bind the variable in the QueryGenerator
          valExpr =
              exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), valTbl.getIdMapping());
          stmt.getQueryGenerator()
              .bindVariable(varName, valCmd, valExpr.getSQLTable(), valExpr.getJavaTypeMapping());
        } else {
          // Add restrict to value
          SQLExpression valIdExpr =
              exprFactory.newExpression(subStmt, subStmt.getPrimaryTable(), valTbl.getIdMapping());
          subStmt.whereAnd(valIdExpr.eq(valExpr), true);
        }
      }
    }

    return new BooleanSubqueryExpression(stmt, "EXISTS", subStmt);
  }